diff --git a/.gitattributes b/.gitattributes index 9e7206629..76f18e81b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ *.nc filter=lfs diff=lfs merge=lfs -text *.nc4 filter=lfs diff=lfs merge=lfs -text +*.dat filter=lfs diff=lfs merge=lfs -text diff --git a/.github/ISSUE_TEMPLATE/basic-issue.md b/.github/ISSUE_TEMPLATE/basic-issue.md new file mode 100644 index 000000000..44b692f55 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/basic-issue.md @@ -0,0 +1,10 @@ +--- +name: Regular issue +about: This is a blank issue. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/missing-docs.md b/.github/ISSUE_TEMPLATE/missing-docs.md new file mode 100644 index 000000000..28e051d75 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/missing-docs.md @@ -0,0 +1,30 @@ +--- +name: 'Missing Documentation' +about: 'Use this issue to note missing documentation on ReadTheDocs and/or Doxygen' +title: "[Documentation]: Give a descriptive title here" +labels: documentation +assignees: huishao-r, rhoneyager, mmiesch + +--- + +## Description + +> Edit the text of this pull request to describe what is missing. You should include the file name or the class name. + + + +## ZenHub organization + +Please make sure that you have properly set: +- Labels: documentation +- Milestone: OBS - current month +- Estimate: usually 0.5 - 1 +- Epics: Select a relevant epic. Here are a few hints. + - JEDI1.10: For **generic** UFO components and bias correction. Generic means a core UFO component, such as a base class + or a component of JEDI that more than one agency will use. + - OBS3.1.2: For **specific** ObsOperators, Filters, and ObsFunctions. +- Assignees: Generally, the person who created the code is responsible for writing the documentation. + The "git blame" feature may be quite helpful in determining this. @huishao-r, @mmiesch, and @rhoneyager are + automatically added to help track these issues. + + diff --git a/CI/CMakeLists.txt b/CI/CMakeLists.txt index 1f74d2aca..52ad6a4fd 100644 --- a/CI/CMakeLists.txt +++ b/CI/CMakeLists.txt @@ -10,7 +10,7 @@ cmake_minimum_required( VERSION 3.12 FATAL_ERROR ) -project( ufo-bundle LANGUAGES C CXX Fortran ) +project( ufo-bundle VERSION 1.0.0 LANGUAGES C CXX Fortran ) find_package(ecbuild) set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_CURRENT_SOURCE_DIR}/ecbuild/cmake;${CMAKE_MODULE_PATH}") @@ -19,20 +19,45 @@ include( ecbuild_bundle ) set( ENABLE_MPI ON CACHE BOOL "Compile with MPI" ) ecbuild_bundle_initialize() -ecbuild_bundle( PROJECT jedicmake GIT "https://github.com/JCSDA/jedi-cmake.git" ) +ecbuild_bundle( PROJECT jedicmake GIT "https://github.com/JCSDA-internal/jedi-cmake.git" ) +include( jedicmake/cmake/Functions/git_functions.cmake ) option("BUNDLE_SKIP_ECKIT" "Don't build eckit" "ON") #Skip eckit build unless user passes -DBUNDLE_SKIP_ECKIT=OFF -ecbuild_bundle( PROJECT eckit GIT "https://github.com/JCSDA/eckit.git" ) -ecbuild_bundle( PROJECT fckit GIT "https://github.com/JCSDA/fckit.git" ) -ecbuild_bundle( PROJECT atlas GIT "https://github.com/JCSDA/atlas.git" ) +option("BUNDLE_SKIP_FCKIT" "Don't build fckit" "ON") #Skip fckit build unless user passes -DBUNDLE_SKIP_FCKIT=OFF +option("BUNDLE_SKIP_ATLAS" "Don't build atlas" "ON") #Skip atlas build unless user passes -DBUNDLE_SKIP_ATLAS=OFF -ecbuild_bundle( PROJECT oops GIT "https://github.com/JCSDA/oops.git" ) -ecbuild_bundle( PROJECT saber GIT "https://github.com/JCSDA/saber.git" ) -#ecbuild_bundle( PROJECT gsw GIT "https://github.com/JCSDA/GSW-Fortran.git" BRANCH develop UPDATE ) -ecbuild_bundle( PROJECT crtm GIT "https://github.com/JCSDA/crtm.git" ) -ecbuild_bundle( PROJECT ioda GIT "https://github.com/JCSDA/ioda.git" ) -ecbuild_bundle( PROJECT ufo GIT "https://github.com/JCSDA/ufo.git" ) +ecbuild_bundle( PROJECT eckit GIT "https://github.com/ecmwf/eckit.git" TAG 1.16.0 ) +ecbuild_bundle( PROJECT fckit GIT "https://github.com/ecmwf/fckit.git" TAG 0.9.2 ) +ecbuild_bundle( PROJECT atlas GIT "https://github.com/ecmwf/atlas.git" TAG 0.24.1 ) +ecbuild_bundle( PROJECT oops GIT "https://github.com/JCSDA-internal/oops.git" ) +ecbuild_bundle( PROJECT saber GIT "https://github.com/JCSDA-internal/saber.git" ) +#ecbuild_bundle( PROJECT gsw GIT "https://github.com/JCSDA-internal/GSW-Fortran.git" ) +ecbuild_bundle( PROJECT crtm GIT "https://github.com/JCSDA-internal/crtm.git" ) +ecbuild_bundle( PROJECT ioda GIT "https://github.com/JCSDA-internal/ioda.git" ) +ecbuild_bundle( PROJECT rttov GIT "https://github.com/JCSDA-internal/rttov.git" ) +ecbuild_bundle( PROJECT ropp-ufo GIT "https://github.com/JCSDA-internal/ropp-test.git" ) +ecbuild_bundle( PROJECT geos-aero GIT "https://github.com/JCSDA-internal/geos-aero.git" ) +ecbuild_bundle( PROJECT ufo GIT "https://github.com/JCSDA-internal/ufo.git" ) + +# Don't need ioda-data in CI because we are only testing ufo + +# If IODA branch is being built set GIT_BRANCH_FUNC to IODA's current branch. +# If a tagged version of IODA is being built set GIT_TAG_FUNC to ioda's current tag. In this case, +# IODA test files will be download from UCAR DASH and ioda-data repo will not be cloned. +#find_branch_name(REPO_DIR_NAME ioda) +# When LOCAL_PATH_JEDI_TESTFILES is set to the directory of IODA test files stored +# in a local directory, ioda-data repo will not be cloned + +#if( NOT DEFINED ENV{LOCAL_PATH_JEDI_TESTFILES} AND NOT DEFINED ${GIT_TAG_FUNC} ) +# ecbuild_bundle( PROJECT ioda-data GIT "https://github.com/JCSDA-internal/ioda-data.git" ) +#endif() + +# same procedure for ufo-data +find_branch_name(REPO_DIR_NAME ufo) +if( NOT DEFINED ENV{LOCAL_PATH_JEDI_TESTFILES} AND NOT DEFINED ${GIT_TAG_FUNC} ) + ecbuild_bundle( PROJECT ufo-data GIT "https://github.com/JCSDA-internal/ufo-data.git" ) +endif() # Build Doxygen documentation option(BUILD_UFO_BUNDLE_DOC "Build documentation" OFF) @@ -44,4 +69,3 @@ ecbuild_bundle_finalize() include(cmake/cdash-integration.cmake) include(CTest) - diff --git a/CI/buildspec_clang.yml b/CI/buildspec_clang.yml index d3d37a8c1..1475d03d1 100644 --- a/CI/buildspec_clang.yml +++ b/CI/buildspec_clang.yml @@ -1,6 +1,7 @@ version: 0.2 env: + shell: bash parameter-store: GIT_USER: "/CodeBuild/Git_USER" GIT_PASS: "/CodeBuild/Git_PASS" @@ -45,7 +46,7 @@ phases: - git lfs install # creates .gitconfig # Set crtm branch name - - export CRTM_JEDI_BRANCH="v2.3-jedi" + - export CRTM_JEDI_BRANCH="release/crtm_jedi" - if [ "$CODEBUILD_GIT_BRANCH" = "develop" ]; then export CODEBUILD_GIT_BRANCH_FORK="release-stable"; @@ -55,29 +56,26 @@ phases: echo "CODEBUILD_GIT_BRANCH_FORK=${CODEBUILD_GIT_BRANCH_FORK}"; fi + # Upload branch name and commit sha as CodeBuild artifact to S3 + - mkdir -p /jcsda/artifacts + - echo ${CODEBUILD_GIT_BRANCH} > /jcsda/artifacts/branch_name.txt + - echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} > /jcsda/artifacts/commit_sha.txt + ## public repos # ufo - - ./clone.sh $GIT_USER $GIT_PASS jcsda/ufo $CODEBUILD_GIT_BRANCH ufo /jcsda/ufo-bundle develop + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/ufo $CODEBUILD_GIT_BRANCH ufo /jcsda/ufo-bundle develop # crtm - - ./clone.sh $GIT_USER $GIT_PASS jcsda/crtm $CODEBUILD_GIT_BRANCH_CRTM crtm /jcsda/ufo-bundle $CRTM_JEDI_BRANCH + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/crtm $CODEBUILD_GIT_BRANCH_CRTM crtm /jcsda/ufo-bundle $CRTM_JEDI_BRANCH # ioda - - ./clone.sh $GIT_USER $GIT_PASS jcsda/ioda $CODEBUILD_GIT_BRANCH ioda /jcsda/ufo-bundle develop + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/ioda $CODEBUILD_GIT_BRANCH ioda /jcsda/ufo-bundle develop # saber - - ./clone.sh $GIT_USER $GIT_PASS jcsda/saber $CODEBUILD_GIT_BRANCH saber /jcsda/ufo-bundle develop + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/saber $CODEBUILD_GIT_BRANCH saber /jcsda/ufo-bundle develop # oops - - ./clone.sh $GIT_USER $GIT_PASS jcsda/oops $CODEBUILD_GIT_BRANCH oops /jcsda/ufo-bundle develop - - - ## ecmwf repos - # atlas - - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/atlas $CODEBUILD_GIT_BRANCH_FORK atlas /jcsda/ufo-bundle release-stable - - #fckit - - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/fckit $CODEBUILD_GIT_BRANCH_FORK fckit /jcsda/ufo-bundle release-stable + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/oops $CODEBUILD_GIT_BRANCH oops /jcsda/ufo-bundle develop ## internal repos @@ -99,33 +97,33 @@ phases: #gsw - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/GSW-Fortran $CODEBUILD_GIT_BRANCH gsw /jcsda/ufo-bundle develop + # ufo-test + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/ufo-data $CODEBUILD_GIT_BRANCH ufo-data /jcsda/ufo-bundle develop + # move CMakeLists.txt - cp CMakeLists.txt /jcsda/ufo-bundle - cp -r cmake /jcsda/ufo-bundle/ - - cd /jcsda/ufo-bundle + - cp /jcsda/ufo-bundle/cmake/CTestConfig.cmake /jcsda/ufo-bundle - - ls - cd /jcsda/ufo-bundle - ls - - sed -i 's/BRANCH develop UPDATE//' CMakeLists.txt - - sed -i 's/BRANCH develop //' CMakeLists.txt - - sed -i 's/BRANCH release-stable UPDATE//' CMakeLists.txt - - sed -i -e '/ecbuild_bundle( PROJECT ufo/i\ - ecbuild_bundle( PROJECT ropp-ufo GIT "https://github.com/JCSDA/ropp-test.git" )' CMakeLists.txt - - sed -i -e '/ecbuild_bundle( PROJECT ufo/i\ - ecbuild_bundle( PROJECT geos-aero GIT "https://github.com/JCSDA/geos-aero.git" )' CMakeLists.txt - build: + on-failure: CONTINUE commands: - echo Executing build phase - echo $CODEBUILD_BUILD_SUCCEEDING - export BUILD_STATUS="0" - echo $BUILD_STATUS + # configure and build - cd /build_container - - ecbuild -DBUILD_RTTOV=1 /jcsda/ufo-bundle/ + - ecbuild -Wno-dev -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCDASH_OVERRIDE_GIT_BRANCH=$CODEBUILD_GIT_BRANCH -DCTEST_UPDATE_VERSION_ONLY=FALSE /jcsda/ufo-bundle - cd ufo + - cp ../DartConfiguration.tcl . + - sed -i 's/ufo-bundle/ufo-bundle\/ufo/' DartConfiguration.tcl + - sed -i 's/build_container/build_container\/ufo/' DartConfiguration.tcl + - cat DartConfiguration.tcl - make -j4 - if [ "$CODEBUILD_BUILD_SUCCEEDING" = "1" ]; @@ -134,16 +132,32 @@ phases: fi - echo $BUILD_STATUS + # run ctest + - cd /build_container/ufo + - ctest -C RelWithDebInfo -D ExperimentalTest + + finally: - cd /build_container/ufo - - ctest + - ctest -C RelWithDebInfo -D ExperimentalSubmit -M Continuous -- --track Continuous --group Continuous post_build: commands: - echo Executing post_build phase - echo $CODEBUILD_BUILD_SUCCEEDING - - if [ "$BUILD_STATUS" = "1" ] && [ "$CODEBUILD_BUILD_SUCCEEDING" = "0" ]; - then echo "Build passed, rerunning failed tests"; - cd /build_container/ufo; - ctest -VV --rerun-failed; + - echo $BUILD_STATUS + + # upload find cdash url and upload it as CodeBuild artifact to S3 + - if [ "$BUILD_STATUS" = "1" ]; + then echo "Build & tests passed, find cdash url"; + bash /jcsda/ufo-bundle/ufo/CI/cdash-url.sh /build_container/ufo/Testing; + url=$(bash /jcsda/ufo-bundle/ufo/CI/cdash-url.sh /build_container/ufo/Testing); + echo $url; + echo ${url} > /jcsda/artifacts/cdash-url.txt; + cat /jcsda/artifacts/cdash-url.txt; else echo "Build failed"; fi + +artifacts: + files: + - '/jcsda/artifacts/*' + name: ufo-clang-url diff --git a/CI/buildspec_gnu.yml b/CI/buildspec_gnu.yml index a55a6fa37..52ef4cb17 100644 --- a/CI/buildspec_gnu.yml +++ b/CI/buildspec_gnu.yml @@ -11,6 +11,8 @@ phases: - echo Executing install phase - echo $CODEBUILD_RESOLVED_SOURCE_VERSION - echo $CODEBUILD_SOURCE_REPO_URL + - org_name=$(echo $CODEBUILD_SOURCE_REPO_URL | awk '{split($0,org,"/"); print org[4]}') + - echo $org_name - echo $CODEBUILD_SOURCE_VERSION - echo $CODEBUILD_WEBHOOK_MERGE_COMMIT @@ -33,47 +35,14 @@ phases: - echo "CODEBUILD_GIT_BRANCH=${CODEBUILD_GIT_BRANCH}" - echo "CODEBUILD_SOURCE_VERSION=${CODEBUILD_SOURCE_VERSION}" - - echo MPI setup for Docker - - mkdir -p /var/run/sshd - - ssh-keygen -A - - sed -i 's/#PermitRootLogin yes/PermitRootLogin yes/g' /etc/ssh/sshd_config - - sed -i 's/#RSAAuthentication yes/RSAAuthentication yes/g' /etc/ssh/sshd_config - - sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/g' /etc/ssh/sshd_config - - - groupadd jcsda -g 9999 - - useradd jcsdauser - - mkdir -p /jcsda /build_container - - chown -R jcsdauser:jcsda /build_container /usr/local - - chmod 6755 /jcsda /build_container /usr/local - - - mkdir /jcsda/.ssh ; echo "StrictHostKeyChecking no" > /jcsda/.ssh/config - - mkdir -p /jcsda/.openmpi - - mkdir -p /home/jcsdauser/.openmpi - - - cp CI/default-mca-params.conf /home/jcsdauser/.openmpi/mca-params.conf - - cat /home/jcsdauser/.openmpi/mca-params.conf - - chown -R jcsdauser:jcsda /jcsda/ - - - su - jcsdauser -c "ssh-keygen -f /jcsda/.ssh/id_rsa -t rsa -N '' - && chmod 600 /jcsda/.ssh/config - && chmod 700 /jcsda/.ssh - && cp /jcsda/.ssh/id_rsa.pub /jcsda/.ssh/authorized_keys - && echo MPI setup for Docker done" - - su - jcsdauser -c "echo $CC - && echo $CXX - && echo $FC - && whereis mpicc" - pre_build: commands: - echo Executing pre_build phase - git lfs install # creates .gitconfig - - cp ~/.gitconfig /home/jcsdauser/ + - mkdir -p /jcsda/ufo-bundle - - cd CI - # Set crtm branch name - - export CRTM_JEDI_BRANCH="v2.3-jedi" + - export CRTM_JEDI_BRANCH="release/crtm_jedi" - if [ "$CODEBUILD_GIT_BRANCH" = "develop" ]; then export CODEBUILD_GIT_BRANCH_FORK="release-stable"; @@ -83,30 +52,27 @@ phases: echo "CODEBUILD_GIT_BRANCH_FORK=${CODEBUILD_GIT_BRANCH_FORK}"; fi + # Upload branch name and commit sha as CodeBuild artifact to S3 + - mkdir -p /jcsda/artifacts + - echo ${CODEBUILD_GIT_BRANCH} > /jcsda/artifacts/branch_name.txt + - echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} > /jcsda/artifacts/commit_sha.txt + + - cd CI ## public repos # ufo - - ./clone.sh $GIT_USER $GIT_PASS jcsda/ufo $CODEBUILD_GIT_BRANCH ufo /jcsda/ufo-bundle develop + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/ufo $CODEBUILD_GIT_BRANCH ufo /jcsda/ufo-bundle develop # crtm - - ./clone.sh $GIT_USER $GIT_PASS jcsda/crtm $CODEBUILD_GIT_BRANCH_CRTM crtm /jcsda/ufo-bundle $CRTM_JEDI_BRANCH + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/crtm $CODEBUILD_GIT_BRANCH_CRTM crtm /jcsda/ufo-bundle $CRTM_JEDI_BRANCH # ioda - - ./clone.sh $GIT_USER $GIT_PASS jcsda/ioda $CODEBUILD_GIT_BRANCH ioda /jcsda/ufo-bundle develop + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/ioda $CODEBUILD_GIT_BRANCH ioda /jcsda/ufo-bundle develop # saber - - ./clone.sh $GIT_USER $GIT_PASS jcsda/saber $CODEBUILD_GIT_BRANCH saber /jcsda/ufo-bundle develop + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/saber $CODEBUILD_GIT_BRANCH saber /jcsda/ufo-bundle develop # oops - - ./clone.sh $GIT_USER $GIT_PASS jcsda/oops $CODEBUILD_GIT_BRANCH oops /jcsda/ufo-bundle develop - - - ## ecmwf repos - # atlas - - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/atlas $CODEBUILD_GIT_BRANCH_FORK atlas /jcsda/ufo-bundle release-stable - - #fckit - - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/fckit $CODEBUILD_GIT_BRANCH_FORK fckit /jcsda/ufo-bundle release-stable - + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/oops $CODEBUILD_GIT_BRANCH oops /jcsda/ufo-bundle develop ## internal repos # jedi-cmake @@ -127,70 +93,88 @@ phases: #gsw - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/GSW-Fortran $CODEBUILD_GIT_BRANCH gsw /jcsda/ufo-bundle develop + # ufo-test + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/ufo-data $CODEBUILD_GIT_BRANCH ufo-data /jcsda/ufo-bundle develop + # move CMakeLists.txt - cp CMakeLists.txt /jcsda/ufo-bundle - cp -r cmake /jcsda/ufo-bundle/ - - cd /jcsda/ufo-bundle - - - ls -lt /jcsda/ufo-bundle/ufo - - sed -i 's/BRANCH develop UPDATE//' CMakeLists.txt - - sed -i 's/BRANCH develop //' CMakeLists.txt - - sed -i 's/BRANCH release-stable UPDATE//' CMakeLists.txt - - sed -i -e '/ecbuild_bundle( PROJECT ufo/i\ - ecbuild_bundle( PROJECT ropp-ufo GIT "https://github.com/JCSDA/ropp-test.git" )' CMakeLists.txt - - sed -i -e '/ecbuild_bundle( PROJECT ufo/i\ - ecbuild_bundle( PROJECT geos-aero GIT "https://github.com/JCSDA/geos-aero.git" )' CMakeLists.txt - - cat CMakeLists.txt + # cdash upload setup + - cp /jcsda/ufo-bundle/cmake/CTestConfig.cmake /jcsda/ufo-bundle/ - chmod -R 777 /jcsda/ufo-bundle - - ls -lt /jcsda/ufo-bundle + build: + on-failure: CONTINUE commands: - echo Executing build phase - echo $CODEBUILD_BUILD_SUCCEEDING - export BUILD_STATUS="0" - echo $BUILD_STATUS - - gcc --version - - g++ --version - - gfortran --version - - which gfortran - - which gcc - - which g++ - - ls /usr/local/bin - - echo $PATH - - - su - jcsdauser -c "cd /build_container + - su - jedi -c "cd /home/jedi && echo $CC && echo $CXX && echo $FC - && CC=mpicc CXX=mpicxx FC=mpifort - && cmake -DCMAKE_MODULE_PATH=/usr/local/share/ecbuild/cmake/ -DBUILD_RTTOV=1 -DCMAKE_BUILD_TYPE=Debug -DENABLE_GPROF=ON -DCMAKE_CXX_COMPILER=$(which g++) -DCMAKE_C_COMPILER=$(which gcc) -DCMAKE_Fortran_COMPILER=$(which gfortran) /jcsda/ufo-bundle/ - && cd ufo + && CC=mpicc CXX=mpicxx FC=mpifort + && ecbuild -Wno-dev -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCDASH_OVERRIDE_GIT_BRANCH=$CODEBUILD_GIT_BRANCH -DCTEST_UPDATE_VERSION_ONLY=FALSE -DENABLE_GPROF=ON /jcsda/ufo-bundle/" + + + # configure and build + - su - jedi -c "cd /home/jedi/ufo + && export FC=mpifort + && export CC=mpicc + && export CXX=mpicxx + && cp ../DartConfiguration.tcl . + && sed -i 's/ufo-bundle/ufo-bundle\/ufo/' DartConfiguration.tcl + && sed -i 's/jedi/jedi\/ufo/' DartConfiguration.tcl + && cat DartConfiguration.tcl && make -j4" + - echo $CODEBUILD_BUILD_SUCCEEDING - if [ "$CODEBUILD_BUILD_SUCCEEDING" = "1" ]; then export BUILD_STATUS="1"; echo "Build passed"; fi - echo $BUILD_STATUS - - su - jcsdauser -c "CC=mpicc CXX=mpicxx FC=mpifort - && cd /build_container/ufo - && ctest" + # run ctest + - su - jedi -c "CC=mpicc CXX=mpicxx FC=mpifort + && cd /home/jedi/ufo + && ctest -C RelWithDebInfo -D ExperimentalTest" + + finally: + # upload ctest report to CDASH + - su - jedi -c "cd /home/jedi/ufo + && export FC=mpifort + && export CC=mpicc + && export CXX=mpicxx + && ctest -C RelWithDebInfo -D ExperimentalSubmit -M Continuous -- --track Continuous --group Continuous" post_build: commands: - echo Executing post_build phase - echo $CODEBUILD_BUILD_SUCCEEDING - - if [ "$BUILD_STATUS" = "1" ] && [ "$CODEBUILD_BUILD_SUCCEEDING" = "0" ]; - then echo "Build passed, rerunning failed tests"; - su - jcsdauser -c "cd /build_container/ufo - && ctest -VV --rerun-failed"; + - echo $BUILD_STATUS + + # upload find cdash url and upload it as CodeBuild artifact to S3 + - if [ "$BUILD_STATUS" = "1" ]; + then echo "Build & tests passed, find cdash url"; + bash /jcsda/ufo-bundle/ufo/CI/cdash-url.sh /home/jedi/ufo/Testing; + url=$(bash /jcsda/ufo-bundle/ufo/CI/cdash-url.sh /home/jedi/ufo/Testing); + echo $url; + echo ${url} > /jcsda/artifacts/cdash-url.txt; + cat /jcsda/artifacts/cdash-url.txt; else echo "Build failed"; fi + - echo 'Connect to CodeCov' - - cd /build_container/ufo + - cd /home/jedi/ufo - pwd - ls - - bash /jcsda/ufo-bundle/ufo/CI/codecov_script.sh + - bash /jcsda/ufo-bundle/ufo/CI/codecov_script_$org_name.sh + +artifacts: + files: + - '/jcsda/artifacts/*' + name: ufo-gnu-url diff --git a/CI/buildspec_intel.yml b/CI/buildspec_intel.yml index 0c2a134f3..7e0f67e5e 100644 --- a/CI/buildspec_intel.yml +++ b/CI/buildspec_intel.yml @@ -1,9 +1,10 @@ version: 0.2 env: - parameter-store: - GIT_USER: "/CodeBuild/Git_USER" - GIT_PASS: "/CodeBuild/Git_PASS" + shell: bash + parameter-store: + GIT_USER: "/CodeBuild/Git_USER" + GIT_PASS: "/CodeBuild/Git_PASS" phases: install: @@ -21,56 +22,6 @@ phases: - echo $CODEBUILD_WEBHOOK_EVENT - echo $CODEBUILD_WEBHOOK_TRIGGER - echo $CODEBUILD_WEBHOOK_BASE_REF - - - echo MPI setup for Docker - - mkdir -p /var/run/sshd - - ssh-keygen -A - - sed -i 's/#PermitRootLogin yes/PermitRootLogin yes/g' /etc/ssh/sshd_config - - sed -i 's/#RSAAuthentication yes/RSAAuthentication yes/g' /etc/ssh/sshd_config - - sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/g' /etc/ssh/sshd_config - - - groupadd jcsda -g 9999 - - useradd jcsdauser - - mkdir -p /jcsda /build_container - - chown -R jcsdauser:jcsda /build_container /usr/local - - chmod 6755 /jcsda /build_container /usr/local - - - mkdir /jcsda/.ssh ; echo "StrictHostKeyChecking no" > /jcsda/.ssh/config - - mkdir -p /jcsda/.openmpi - - mkdir -p /home/jcsdauser/.openmpi - - - cp CI/default-mca-params.conf /home/jcsdauser/.openmpi/mca-params.conf - - cat /home/jcsdauser/.openmpi/mca-params.conf - - chown -R jcsdauser:jcsda /jcsda/ - - - su - jcsdauser -c "ssh-keygen -f /jcsda/.ssh/id_rsa -t rsa -N '' - && chmod 600 /jcsda/.ssh/config - && chmod 700 /jcsda/.ssh - && cp /jcsda/.ssh/id_rsa.pub /jcsda/.ssh/authorized_keys - && echo MPI setup for Docker done" - - su - jcsdauser -c "echo $CC - && echo $CXX - && echo $FC - && whereis mpicc" - - ## cannot source /etc/bash.bashrc so copy what's there for root - - sed '12s/INTEL_TARGET_ARCH=/INTEL_TARGET_ARCH=intel64/' /opt/intel/compilers_and_libraries/linux/bin/compilervars.sh - - export COMPILERVARS_ARCHITECTURE=intel64 - - export COMPILERVARS_PLATFORM=linux - - . /opt/intel/compilers_and_libraries/linux/bin/compilervars.sh - - export FC=mpiifort - - export CC=mpiicc - - export CXX=mpiicpc - - export PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - export LD_LIBRARY_PATH=/usr/local/lib - - export LIBRARY_PATH=/usr/local/lib - - . /opt/intel/compilers_and_libraries/linux/bin/compilervars.sh - - pre_build: - commands: - - echo Executing pre_build phase - - git lfs install # creates .gitconfig - - cp ~/.gitconfig /home/jcsdauser/ # Codebuild only runs on PUSH events if HEAD_REF # is refs/heads/develop (merge to develop). In this @@ -85,10 +36,14 @@ phases: - echo "CODEBUILD_GIT_BRANCH=${CODEBUILD_GIT_BRANCH}" - echo "CODEBUILD_SOURCE_VERSION=${CODEBUILD_SOURCE_VERSION}" - - cd CI - + pre_build: + commands: + - echo Executing pre_build phase + + - . /etc/profile.d/intel.sh + # Set crtm branch name - - export CRTM_JEDI_BRANCH="v2.3-jedi" + - export CRTM_JEDI_BRANCH="release/crtm_jedi" - if [ "$CODEBUILD_GIT_BRANCH" = "develop" ]; then export CODEBUILD_GIT_BRANCH_FORK="release-stable"; @@ -98,30 +53,27 @@ phases: echo "CODEBUILD_GIT_BRANCH_FORK=${CODEBUILD_GIT_BRANCH_FORK}"; fi + # Upload branch name and commit sha as CodeBuild artifact to S3 + - mkdir -p /jcsda/artifacts + - echo ${CODEBUILD_GIT_BRANCH} > /jcsda/artifacts/branch_name.txt + - echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} > /jcsda/artifacts/commit_sha.txt + + - cd CI ## public repos # ufo - - ./clone.sh $GIT_USER $GIT_PASS jcsda/ufo $CODEBUILD_GIT_BRANCH ufo /jcsda/ufo-bundle develop + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/ufo $CODEBUILD_GIT_BRANCH ufo /jcsda/ufo-bundle develop # crtm - - ./clone.sh $GIT_USER $GIT_PASS jcsda/crtm $CODEBUILD_GIT_BRANCH_CRTM crtm /jcsda/ufo-bundle $CRTM_JEDI_BRANCH + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/crtm $CODEBUILD_GIT_BRANCH_CRTM crtm /jcsda/ufo-bundle $CRTM_JEDI_BRANCH # ioda - - ./clone.sh $GIT_USER $GIT_PASS jcsda/ioda $CODEBUILD_GIT_BRANCH ioda /jcsda/ufo-bundle develop + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/ioda $CODEBUILD_GIT_BRANCH ioda /jcsda/ufo-bundle develop # saber - - ./clone.sh $GIT_USER $GIT_PASS jcsda/saber $CODEBUILD_GIT_BRANCH saber /jcsda/ufo-bundle develop + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/saber $CODEBUILD_GIT_BRANCH saber /jcsda/ufo-bundle develop # oops - - ./clone.sh $GIT_USER $GIT_PASS jcsda/oops $CODEBUILD_GIT_BRANCH oops /jcsda/ufo-bundle develop - - - ## ecmwf repos - # atlas - - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/atlas $CODEBUILD_GIT_BRANCH_FORK atlas /jcsda/ufo-bundle release-stable - - #fckit - - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/fckit $CODEBUILD_GIT_BRANCH_FORK fckit /jcsda/ufo-bundle release-stable - + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/oops $CODEBUILD_GIT_BRANCH oops /jcsda/ufo-bundle develop ## internal repos # jedi-cmake @@ -142,44 +94,41 @@ phases: #gsw - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/GSW-Fortran $CODEBUILD_GIT_BRANCH gsw /jcsda/ufo-bundle develop + # ufo-test + - ./clone.sh $GIT_USER $GIT_PASS jcsda-internal/ufo-data $CODEBUILD_GIT_BRANCH ufo-data /jcsda/ufo-bundle develop + # move CMakeLists.txt - cp CMakeLists.txt /jcsda/ufo-bundle - cp -r cmake /jcsda/ufo-bundle/ - - cd /jcsda/ufo-bundle - - ls - - - sed -i 's/BRANCH develop UPDATE//' CMakeLists.txt - - sed -i 's/BRANCH develop //' CMakeLists.txt - - sed -i 's/BRANCH release-stable UPDATE//' CMakeLists.txt - - sed -i -e '/ecbuild_bundle( PROJECT ufo/i\ - ecbuild_bundle( PROJECT ropp-ufo GIT "https://github.com/JCSDA/ropp-test.git" )' CMakeLists.txt - - sed -i -e '/ecbuild_bundle( PROJECT ufo/i\ - ecbuild_bundle( PROJECT geos-aero GIT "https://github.com/JCSDA/geos-aero.git" )' CMakeLists.txt - - cat CMakeLists.txt + # cdash upload setup + - cp /jcsda/ufo-bundle/cmake/CTestConfig.cmake /jcsda/ufo-bundle/ - chmod -R 777 /jcsda/ufo-bundle - - ls -lt /jcsda/ufo-bundle + build: commands: - echo Executing build phase - ## cannot source /etc/bash.bashrc so copy what's there for jscdauser - export BUILD_STATUS="0" - echo $BUILD_STATUS - - su - jcsdauser -c "export COMPILERVARS_ARCHITECTURE=intel64 - && export COMPILERVARS_PLATFORM=linux - && . /opt/intel/compilers_and_libraries/linux/bin/compilervars.sh + - su - jedi -c "export CC=mpiicc && export FC=mpiifort - && export CC=mpiicc && export CXX=mpiicpc - && export PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - && export LD_LIBRARY_PATH=/usr/local/lib - && export LIBRARY_PATH=/usr/local/lib - && . /opt/intel/compilers_and_libraries/linux/bin/compilervars.sh - && cd /build_container - && ls - && ecbuild -DBUILD_RTTOV=1 /jcsda/ufo-bundle/ - && cd ufo + && export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH + && export PATH=/usr/local/lib:$PATH + && cd /home/jedi + && ecbuild -Wno-dev -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCDASH_OVERRIDE_GIT_BRANCH=$CODEBUILD_GIT_BRANCH -DCTEST_UPDATE_VERSION_ONLY=FALSE /jcsda/ufo-bundle/" + + - su - jedi -c "export CC=mpiicc + && export FC=mpiifort + && export CXX=mpiicpc + && export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH + && export PATH=/usr/local/lib:$PATH + && cd /home/jedi/ufo + && cp ../DartConfiguration.tcl . + && sed -i 's/ufo-bundle/ufo-bundle\/ufo/' DartConfiguration.tcl + && sed -i 's/jedi/jedi\/ufo/' DartConfiguration.tcl + && cat DartConfiguration.tcl && make -j4" - echo $CODEBUILD_BUILD_SUCCEEDING @@ -189,39 +138,43 @@ phases: fi - echo $BUILD_STATUS - - su - jcsdauser -c "export COMPILERVARS_ARCHITECTURE=intel64 - && export COMPILERVARS_PLATFORM=linux - && . /opt/intel/compilers_and_libraries/linux/bin/compilervars.sh + # run ctest + - su - jedi -c "export CC=mpiicc + && export FC=mpiifort + && export CXX=mpiicpc + && export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH + && export PATH=/usr/local/lib:$PATH + && cd /home/jedi/ufo + && ctest -C RelWithDebInfo -D ExperimentalTest" + + finally: + # upload ctest report to CDASH + - su - jedi -c "export CC=mpiicc && export FC=mpiifort - && export CC=mpiicc && export CXX=mpiicpc - && export PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - && export LD_LIBRARY_PATH=/usr/local/lib - && export LIBRARY_PATH=/usr/local/lib - && . /opt/intel/compilers_and_libraries/linux/bin/compilervars.sh - && cd /build_container/ufo - && ctest" + && export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH + && export PATH=/usr/local/lib:$PATH + && cd /home/jedi/ufo + && ctest -C RelWithDebInfo -D ExperimentalSubmit -M Continuous -- --track Continuous --group Continuous" post_build: commands: - echo Executing post_build phase - echo $CODEBUILD_BUILD_SUCCEEDING + - echo $BUILD_STATUS - - if [ "$BUILD_STATUS" = "1" ] && [ "$CODEBUILD_BUILD_SUCCEEDING" = "0" ]; - then echo "Build passed, rerun failed tests"; - su - jcsdauser -c "export COMPILERVARS_ARCHITECTURE=intel64 - && export COMPILERVARS_PLATFORM=linux - && . /opt/intel/compilers_and_libraries/linux/bin/compilervars.sh - && export FC=mpiifort - && export CC=mpiicc - && export CXX=mpiicpc - && export PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - && export LD_LIBRARY_PATH=/usr/local/lib - && export LIBRARY_PATH=/usr/local/lib - && . /opt/intel/compilers_and_libraries/linux/bin/compilervars.sh - && cd /build_container/ufo - && echo $LD_LIBRARY_PATH - && ctest -VV --rerun-failed"; + # upload find cdash url and upload it as CodeBuild artifact to S3 + - if [ "$BUILD_STATUS" = "1" ]; + then echo "Build & tests passed, find cdash url"; + bash /jcsda/ufo-bundle/ufo/CI/cdash-url.sh /home/jedi/ufo/Testing; + url=$(bash /jcsda/ufo-bundle/ufo/CI/cdash-url.sh /home/jedi/ufo/Testing); + echo $url; + echo ${url} > /jcsda/artifacts/cdash-url.txt; + cat /jcsda/artifacts/cdash-url.txt; else echo "Build failed"; fi +artifacts: + files: + - '/jcsda/artifacts/*' + name: ufo-intel-url diff --git a/CI/cdash-url.sh b/CI/cdash-url.sh new file mode 100755 index 000000000..b80297fef --- /dev/null +++ b/CI/cdash-url.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +dir=$1 +tag=$(head -1 $dir/TAG) +Done=$(cat $dir/$tag/Done.xml) +buildID=$(echo $Done | grep -o -P '(?<=buildId>).*(?=2, there are some 4 and 6 +# action: +# name: reject +#- filter: BlackList +# filter variables: +# - name: integrated_layer_ozone_in_air +# where: +# - variable: +# name: profile_ozone_error_flag@MetaData +# minvalue: 1.1 +# action: +# name: reject +- filter: BlackList + filter variables: + - name: integrated_layer_ozone_in_air + where: + - variable: + name: air_pressure@MetaData + maxvalue: 64.00000 + action: + name: reject +- filter: Domain Check + filter variables: + - name: integrated_layer_ozone_in_air + where: + - variable: + name: latitude@MetaData + minvalue: -90. + maxvalue: 90. + - variable: + name: longitude@MetaData + minvalue: -180. + maxvalue: 360. +- filter: Bounds Check + filter variables: + - name: integrated_layer_ozone_in_air + minvalue: 0.000001 + maxvalue: 1000.0 +- filter: Background Check + filter variables: + - name: integrated_layer_ozone_in_air + absolute threshold: 10.0 diff --git a/ewok/jedi-gdas/ompstc8_npp.yaml b/ewok/jedi-gdas/ompstc8_npp.yaml new file mode 100644 index 000000000..8b4784ee4 --- /dev/null +++ b/ewok/jedi-gdas/ompstc8_npp.yaml @@ -0,0 +1,12 @@ +obs space: + name: ompstc8_npp + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/ompstc8_npp.{{window_begin}}.nc4 + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).ompstc8_npp.{{window_begin}}.nc4 + simulated variables: [integrated_layer_ozone_in_air] +obs operator: + name: AtmVertInterpLay + geovals: [mole_fraction_of_ozone_in_air] + coefficients: [0.007886131] # convert from ppmv to DU + nlevels: [1] diff --git a/ewok/jedi-gdas/rass_tv.yaml b/ewok/jedi-gdas/rass_tv.yaml new file mode 100644 index 000000000..d316542fb --- /dev/null +++ b/ewok/jedi-gdas/rass_tv.yaml @@ -0,0 +1,9 @@ +obs operator: + name: VertInterp +obs space: + name: rass_tv + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/rass_tv.{{window_begin}}.nc4 + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).rass_tv.{{window_begin}}.nc4 + simulated variables: [virtual_temperature] diff --git a/ewok/jedi-gdas/satwind.yaml b/ewok/jedi-gdas/satwind.yaml new file mode 100644 index 000000000..d5969395c --- /dev/null +++ b/ewok/jedi-gdas/satwind.yaml @@ -0,0 +1,315 @@ +obs operator: + name: VertInterp +obs space: + name: satwind + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/satwind.{{window_begin}}.nc4 + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).satwind.{{window_begin}}.nc4 + simulated variables: [eastward_wind, northward_wind] +obs filters: +# +# Reject all obs with PreQC mark already set above 3 +- filter: PreQC + maxvalue: 3 + action: + name: reject +# +# Assign the initial observation error, based on height/pressure +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [100000, 95000, 80000, 65000, 60000, 55000, 50000, 45000, 40000, 35000, 30000, 25000, 20000, 15000, 10000] #Pressure (Pa) + errors: [1.4, 1.5, 1.6, 1.8, 1.9, 2.0, 2.1, 2.3, 2.6, 2.8, 3.0, 3.2, 2.7, 2.4, 2.1] +# +# Observation Range Sanity Check: either wind component or velocity exceeds 135 m/s +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: reject +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: Velocity@ObsFunction + maxvalue: 135.0 + action: + name: reject +# +# All satellite platforms, reject when pressure greater than 950 mb. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + maxvalue: 95000 + action: + name: reject +# +# Difference check surface_pressure and air_pressure@ObsValue, if less than 100 hPa, reject. +# Starting with 730029 values, 338418 missing (half?), 50883 rejected by difference check, leaving 340728 +- filter: Difference Check + filter variables: + - name: eastward_wind + - name: northward_wind + reference: surface_pressure@GeoVaLs + value: air_pressure@MetaData + maxvalue: -10000 +# +# Multiple satellite platforms, reject when pressure is more than 50 mb above tropopause. +- filter: Difference Check + filter variables: + - name: eastward_wind + - name: northward_wind + reference: TropopauseEstimate@ObsFunction + value: air_pressure@MetaData + minvalue: -5000 # 50 hPa above tropopause level, negative p-diff + action: + name: reject +# +# GOES WV (non-cloudy; itype=247) reject when difference of wind direction is more than 50 degrees. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: WindDirAngleDiff@ObsFunction + maxvalue: 50.0 + action: + name: reject +# +# GOES IR (245), EUMET IR (253), JMA IR (252) reject when pressure between 400 and 800 mb. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + minvalue: 40000 + maxvalue: 80000 + where: + - variable: + name: eastward_wind@ObsType + is_in: 245, 252, 253 + action: + name: reject +# +# GOES WV (246, 250, 254), reject when pressure greater than 400 mb. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + maxvalue: 40000 + where: + - variable: + name: eastward_wind@ObsType + is_in: 246, 250, 254 + action: + name: reject +# +# EUMET (242) and JMA (243) vis, reject when pressure less than 700 mb. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + minvalue: 70000 + where: + - variable: + name: eastward_wind@ObsType + is_in: 242, 243 + action: + name: reject +# +# MODIS-Aqua/Terra (257) and (259), reject when pressure less than 250 mb. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + minvalue: 25000 + where: + - variable: + name: eastward_wind@ObsType + is_in: 257-259 + action: + name: reject +# +# MODIS-Aqua/Terra (258) and (259), reject when pressure greater than 600 mb. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + maxvalue: 60000 + where: + - variable: + name: eastward_wind@ObsType + is_in: 258, 259 + action: + name: reject +# +# AVHRR (244), MODIS (257,258,259), VIIRS (260), GOES (247) use a LNVD check. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: SatWindsLNVDCheck@ObsFunction + maxvalue: 3 + where: + - variable: + name: eastward_wind@ObsType + is_in: 244, 247, 257-260 + action: + name: reject +# +# AVHRR and MODIS (ObsType=244,257,258,259) use a SPDB check. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: SatWindsSPDBCheck@ObsFunction + options: + error_min: 1.4 + error_max: 20.0 + maxvalue: 1.75 + where: + - variable: + name: eastward_wind@ObsType + is_in: 244, 257, 258, 259 + action: + name: reject +# +# GOES (ObsType=245,246,253,254) use a SPDB check only between 300-400 mb. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: SatWindsSPDBCheck@ObsFunction + options: + error_min: 1.4 + error_max: 20.0 + maxvalue: 1.75 + where: + - variable: + name: eastward_wind@ObsType + is_in: 244, 257, 258, 259 + - variable: + name: air_pressure@MetaData + minvalue: 30000 + maxvalue: 40000 + action: + name: reject +# +- filter: Background Check + filter variables: + - name: eastward_wind + - name: northward_wind + absolute threshold: 7.5 + action: + name: inflate error + inflation factor: 3.0 + defer to post: true +# +# If the total inflation factor is too big, reject. +- filter: Bounds Check + filter variables: + - name: eastward_wind + action: + name: reject + maxvalue: 2.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: eastward_wind@ObsErrorData # After inflation step + denominator: + name: eastward_wind@ObsError + where: + - variable: + name: eastward_wind@ObsType + is_in: 240, 241, 242, 244, 247, 248, 249, 250, 252, 255-260 + defer to post: true +# +- filter: Bounds Check + filter variables: + - name: northward_wind + action: + name: reject + maxvalue: 2.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: northward_wind@ObsErrorData # After inflation step + denominator: + name: northward_wind@ObsError + where: + - variable: + name: northward_wind@ObsType + is_in: 240, 241, 242, 244, 247, 248, 249, 250, 252, 255-260 + defer to post: true +# +# Some satellite platforms have a lower threshold of inflation factor of 1.5 +- filter: Bounds Check + filter variables: + - name: eastward_wind + action: + name: reject + maxvalue: 1.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: eastward_wind@ObsErrorData # After inflation step + denominator: + name: eastward_wind@ObsError + where: + - variable: + name: eastward_wind@ObsType + is_in: 243, 245, 246, 251, 253, 254 + defer to post: true +# +- filter: Bounds Check + filter variables: + - name: northward_wind + action: + name: reject + maxvalue: 1.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: northward_wind@ObsErrorData # After inflation step + denominator: + name: northward_wind@ObsError + where: + - variable: + name: eastward_wind@ObsType + is_in: 243, 245, 246, 251, 253, 254 + defer to post: true + diff --git a/ewok/jedi-gdas/scatwind.yaml b/ewok/jedi-gdas/scatwind.yaml new file mode 100644 index 000000000..7df4b345a --- /dev/null +++ b/ewok/jedi-gdas/scatwind.yaml @@ -0,0 +1,113 @@ +obs operator: + name: GSISfcModel +obs space: + name: scatwind + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/scatwind.{{window_begin}}.nc4 + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).scatwind.{{window_begin}}.nc4 + simulated variables: [eastward_wind, northward_wind] +obs filters: +# Reject all obs with PreQC mark already set above 3 +- filter: PreQC + maxvalue: 3 + action: + name: reject +# +# WindSat (289), ASCAT (290), or OSCAT (291) either wind component or velocity greater than 20 m/s, then reject. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -20 + maxvalue: 20 +# +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: Velocity@ObsFunction + maxvalue: 20.0 + where: + - variable: + name: eastward_wind@ObsType + is_in: 289, 290, 291 + action: + name: reject +# +# Similar to satellite winds AMV, reject when obs wind direction is more than 50 degrees different than model. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: WindDirAngleDiff@ObsFunction + maxvalue: 50.0 + action: + name: reject +# +# ASCAT (290), RAPIDSCAT (296) use a LNVD check. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: SatWindsLNVDCheck@ObsFunction + maxvalue: 3 + where: + - variable: + name: eastward_wind@ObsType + is_in: 290, 296 + action: + name: reject +# +# Assign the initial observation error (constant value, 3.5 m/s right now). +- filter: BlackList + filter variables: + - name: eastward_wind + - name: northward_wind + action: + name: assign error + error parameter: 3.5 +# +# For OSCAT, ASCAT, RAPIDSCAT, or WindSat, reject when either wind component differs by at least 6 m/s from model. +- filter: Background Check + filter variables: + - name: eastward_wind + - name: northward_wind + absolute threshold: 6.0 + where: + - variable: + name: eastward_wind@ObsType + is_in: 289, 290, 291, 296 + action: + name: reject +# +- filter: Bounds Check + filter variables: + - name: eastward_wind + action: + name: reject + maxvalue: 5.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: eastward_wind@ObsErrorData # After inflation step + denominator: + name: eastward_wind@ObsError +# +- filter: Bounds Check + filter variables: + - name: northward_wind + action: + name: reject + maxvalue: 5.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: northward_wind@ObsErrorData # After inflation step + denominator: + name: northward_wind@ObsError diff --git a/ewok/jedi-gdas/seviri_m11.yaml b/ewok/jedi-gdas/seviri_m11.yaml new file mode 100644 index 000000000..89b27ea56 --- /dev/null +++ b/ewok/jedi-gdas/seviri_m11.yaml @@ -0,0 +1,144 @@ +obs space: + name: seviri_m11 + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/seviri_m11.{{window_begin}}.nc4 + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).seviri_m11.{{window_begin}}.nc4 + simulated variables: [brightness_temperature] + channels: &seviri_m11_channels 4-11 +obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: seviri_m11 + EndianType: little_endian + CoefficientPath: $(jedi_build)/ufo/test/Data/ +obs bias: + input file: $(experiment_dir)/{{current_cycle}}/seviri_m11.{{background_time}}.satbias.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &seviri_m11_tlapse $(experiment_dir)/{{current_cycle}}/seviri_m11.{{background_time}}.tlapse.txt + - name: lapse_rate + options: + tlapse: *seviri_m11_tlapse + - name: emissivity + - name: scan_angle + options: + var_name: scan_position + order: 4 + - name: scan_angle + options: + var_name: scan_position + order: 3 + - name: scan_angle + options: + var_name: scan_position + order: 2 + - name: scan_angle + options: + var_name: scan_position +obs filters: +# Observation Range Sanity Check +- filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *seviri_m11_channels + minvalue: 50.00001 + maxvalue: 449.99999 + action: + name: reject +# Surface Check:use chn 2 and 3 over both sea and land while other IR chns only over sea +# ch2 and ch3 in GSI should be the original seviri ch5 and ch6 +- filter: BlackList + filter variables: + - name: brightness_temperature + channels: 4,7-11 + where: + - variable: + name: water_area_fraction@GeoVaLs + maxvalue: 0.99 +# Do not use ch5,6 over snow +- filter: BlackList + filter variables: + - name: brightness_temperature + channels: *seviri_m11_channels + where: + - variable: + name: surface_snow_area_fraction@GeoVaLs + minvalue: 0.01 +# Do not use ch5,6 over ice +- filter: BlackList + filter variables: + - name: brightness_temperature + channels: *seviri_m11_channels + where: + - variable: + name: ice_area_fraction@GeoVaLs + minvalue: 0.01 +# Do not use over mixed surface +- filter: BlackList + filter variables: + - name: brightness_temperature + channels: *seviri_m11_channels + where: + - variable: + name: land_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: water_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: ice_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: surface_snow_area_fraction@GeoVaLs + maxvalue: 0.99 +# QC_terrain: If seviri and terrain height > 1km. do not use +- filter: Domain Check + filter variables: + - name: brightness_temperature + channels: *seviri_m11_channels + where: + - variable: + name: surface_geopotential_height@GeoVaLs + maxvalue: 1000.0 +# Gross check +- filter: Background Check + filter variables: + - name: brightness_temperature + channels: *seviri_m11_channels + absolute threshold: 2.0 + action: + name: reject +# Surface Jacobians Check +- filter: BlackList + filter variables: + - name: brightness_temperature + channels: *seviri_m11_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *seviri_m11_channels + options: + channels: *seviri_m11_channels + obserr_demisf: [0.01, 0.02, 0.02, 0.02, 0.02] + obserr_dtempf: [0.50, 2.00, 3.00, 3.00, 5.00] +# Useflag Check +- filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *seviri_m11_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *seviri_m11_channels + options: + channels: *seviri_m11_channels + use_flag: [ -1, 1, 1, -1, -1, -1, -1, -1 ] + minvalue: 1.0e-12 + action: + name: reject diff --git a/ewok/jedi-gdas/sfc.yaml b/ewok/jedi-gdas/sfc.yaml new file mode 100644 index 000000000..25eafa60f --- /dev/null +++ b/ewok/jedi-gdas/sfc.yaml @@ -0,0 +1,120 @@ +obs operator: + name: SfcPCorrected + da_psfc_scheme: UKMO +# geovar_geomz: geopotential_height +# geovar_sfc_geomz: surface_geopotential_height +obs space: + name: sfc + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/sfc.{{window_begin}}.nc4 + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).sfc.{{window_begin}}.nc4 + simulated variables: [surface_pressure] #, air_temperature] +obs filters: +# Observation Range Sanity Check +- filter: Bounds Check + filter variables: + - name: surface_pressure + minvalue: 37499 + maxvalue: 106999 + action: + name: reject +# Reject all obs with PreQC mark already set above 3 +- filter: PreQC + maxvalue: 3 + action: + name: reject +# Assign obsError. +- filter: BlackList + filter variables: + - name: surface_pressure + action: + name: assign error + error parameter: 120 # 120 Pa (1.2mb) +# +- filter: BlackList + filter variables: + - name: surface_pressure + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: surface_pressure@ObsValue + xvals: [80000, 75000] + errors: [110, 120] + where: + - variable: + name: surface_pressure@ObsType + is_in: 181 # Type is SYNOP +# +- filter: BlackList + filter variables: + - name: surface_pressure + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: surface_pressure@ObsValue + xvals: [85000, 80000] + errors: [120, 140] + where: + - variable: + name: surface_pressure@ObsType + is_in: 187 # Type is METAR +# +# Inflate ObsError as it is done with GSI +- filter: BlackList + filter variables: + - name: surface_pressure + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSfcPressure@ObsFunction + options: +# original_obserr: ObsError + error_min: 100 # 1 mb + error_max: 300 # 3 mb + geovar_sfc_geomz: surface_geopotential_height +# +# If the ObsError is inflated too much, reject the obs +- filter: Bounds Check + filter variables: + - name: surface_pressure + action: + name: reject + maxvalue: 3.6 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: surface_pressure@ObsErrorData # After inflation step + denominator: + name: surface_pressure@ObsError + defer to post: true + where: + - variable: + name: surface_pressure@ObsType + is_in: 181 # Type is SYNOP +# +- filter: Bounds Check + filter variables: + - name: surface_pressure + action: + name: reject + maxvalue: 4.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: surface_pressure@ObsErrorData # After inflation step + denominator: + name: surface_pressure@ObsError + defer to post: true + where: + - variable: + name: surface_pressure@ObsType + is_in: 187 # Type is METAR diff --git a/ewok/jedi-gdas/sfcship.yaml b/ewok/jedi-gdas/sfcship.yaml new file mode 100644 index 000000000..4a222f204 --- /dev/null +++ b/ewok/jedi-gdas/sfcship.yaml @@ -0,0 +1,129 @@ +obs operator: + name: SfcPCorrected + da_psfc_scheme: UKMO +obs space: + name: sfcship + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/sfcship.{{window_begin}}.nc4 + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).sfcship.{{window_begin}}.nc4 + simulated variables: [surface_pressure] #, air_temperature, specific_humidity] +obs filters: +# Observation Range Sanity Check +#- filter: Bounds Check +# filter variables: +# - name: air_temperature +# minvalue: 195 +# maxvalue: 327 +# action: +# name: reject +# +- filter: Bounds Check + filter variables: + - name: surface_pressure + minvalue: 37499 + maxvalue: 106999 + action: + name: reject +# +#- filter: Bounds Check +# filter variables: +# - name: specific_humidity +# minvalue: 1.0E-7 +# maxvalue: 0.034999999 +# action: +# name: reject +# Reject all obs with PreQC mark already set above 3 +- filter: PreQC + maxvalue: 3 + action: + name: reject +# Assign obsError, first temperature +#- filter: BlackList +# filter variables: +# - name: air_temperature +# action: +# name: assign error +# error parameter: 2.5 +# Assign obsError, next moisture +#- filter: BlackList +# filter variables: +# - name: specific_humidity +# action: +# name: assign error +# error function: +# name: ObsErrorModelStepwiseLinear@ObsFunction +# options: +# xvar: +# name: surface_pressure@ObsValue +# xvals: [110000, 10] +# errors: [.2, .2] +# scale_factor_var: specific_humidity@ObsValue +# Assign obsError, last surface pressure +- filter: BlackList + filter variables: + - name: surface_pressure + action: + name: assign error + error parameter: 130 # 130 Pa (1.3mb) +# +# Inflate ObsError as it is done with GSI +- filter: BlackList + filter variables: + - name: surface_pressure + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSfcPressure@ObsFunction + options: + error_min: 100 # 1 mb + error_max: 300 # 3 mb + geovar_sfc_geomz: surface_geopotential_height +# +- filter: Background Check + filter variables: + - name: surface_pressure + absolute threshold: 500.0 # 5 mb + action: + name: inflate error + inflation factor: 3.0 + defer to post: true +# +#- filter: Background Check +# filter variables: +# - name: air_temperature +# absolute threshold: 4.0 +# action: +# name: inflate error +# inflation factor: 3.0 +# defer to post: true +# +#- filter: Bounds Check +# filter variables: +# - name: air_temperature +# action: +# name: reject +# maxvalue: 4.5 +# test variables: +# - name: ObsErrorFactorQuotient@ObsFunction +# options: +# numerator: +# name: air_temperature@ObsErrorData # After inflation step +# denominator: +# name: air_temperature@ObsError +# defer to post: true +# +- filter: Bounds Check + filter variables: + - name: surface_pressure + action: + name: reject + maxvalue: 4.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: surface_pressure@ObsErrorData # After inflation step + denominator: + name: surface_pressure@ObsError + defer to post: true diff --git a/ewok/jedi-gdas/sondes.yaml b/ewok/jedi-gdas/sondes.yaml new file mode 100644 index 000000000..b79b7ad48 --- /dev/null +++ b/ewok/jedi-gdas/sondes.yaml @@ -0,0 +1,282 @@ +obs space: + name: sondes + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/sondes.{{window_begin}}.nc4 + obsgrouping: + group variables: ["station_id", "LaunchTime"] + sort variable: "air_pressure" + sort order: "descending" + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).sondes.{{window_begin}}.nc4 + simulated variables: [air_temperature, specific_humidity, eastward_wind, northward_wind] # surface_pressure +obs operator: + name: VertInterp +obs filters: +# Reject all obs with PreQC mark already set above 3 +- filter: PreQC + maxvalue: 3 + action: + name: reject +# +# Observation Range Sanity Check: temperature, surface_pressure, moisture, winds +- filter: Bounds Check + filter variables: + - name: air_temperature + minvalue: 185 + maxvalue: 327 + action: + name: reject +# +#- filter: Bounds Check +# filter variables: +# - name: surface_pressure +# minvalue: 37499 +# maxvalue: 106999 +# action: +# name: reject +# +- filter: Bounds Check + filter variables: + - name: specific_humidity + minvalue: 1.0E-8 + maxvalue: 0.034999999 + action: + name: reject +# +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: reject +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: Velocity@ObsFunction + maxvalue: 135.0 + action: + name: reject +# +# Reject when difference of wind direction is more than 50 degrees. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: WindDirAngleDiff@ObsFunction + maxvalue: 50.0 + action: + name: reject +# +# Assign the initial observation error, based on height/pressure +- filter: BlackList + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [100000, 95000, 90000, 85000, 35000, 30000, 25000, 20000, 15000, 10000, 7500, 5000, 4000, 3000, 2000, 1000] + errors: [1.2, 1.1, 0.9, 0.8, 0.8, 0.9, 1.2, 1.2, 1.0, 0.8, 0.8, 0.9, 0.95, 1.0, 1.25, 1.5] +# +#- filter: BlackList +# filter variables: +# - name: surface_pressure +# action: +# name: assign error +# error function: +# name: ObsErrorModelStepwiseLinear@ObsFunction +# options: +# xvar: +# name: surface_pressure@ObsValue +# xvals: [80000, 75000] +# errors: [110, 120] # 1.1 mb below 800 mb and 1.2 mb agove 750 mb +# +#- filter: BlackList +# filter variables: +# - name: surface_pressure +# action: +# name: inflate error +# inflation variable: +# name: ObsErrorFactorSfcPressure@ObsFunction +# options: +# error_min: 100 # 1 mb +# error_max: 300 # 3 mb +# geovar_sfc_geomz: surface_geopotential_height +# +- filter: BlackList + filter variables: + - name: specific_humidity + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [25000, 20000, 10] + errors: [0.2, 0.4, 0.8] # 20% RH up to 250 mb, then increased rapidly above + scale_factor_var: specific_humidity@ObsValue +# +- filter: BlackList + filter variables: + - name: eastward_wind + - name: northward_wind + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [100000, 95000, 80000, 65000, 60000, 55000, 50000, 45000, 40000, 35000, 30000, 25000, 20000, 15000, 10000] #Pressure (Pa) + errors: [1.4, 1.5, 1.6, 1.8, 1.9, 2.0, 2.1, 2.3, 2.6, 2.8, 3.0, 3.2, 2.7, 2.4, 2.1] +# +# Inflate obserror when multiple obs exist inside vertical model layers. +- filter: BlackList + filter variables: + - name: specific_humidity + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [specific_humidity] + defer to post: true +- filter: BlackList + filter variables: + - name: air_temperature + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [air_temperature] + defer to post: true +- filter: BlackList + filter variables: + - name: eastward_wind + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [eastward_wind] + defer to post: true +# +- filter: BlackList + filter variables: + - name: northward_wind + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [northward_wind] + defer to post: true +# +- filter: Background Check + filter variables: + - name: air_temperature + absolute threshold: 4.0 + action: + name: inflate error + inflation factor: 3.0 + defer to post: true +# +- filter: Background Check + filter variables: + - name: eastward_wind + - name: northward_wind + absolute threshold: 7.5 + action: + name: inflate error + inflation factor: 3.0 + defer to post: true +# +# If the total inflation factor is too big, reject. +- filter: Bounds Check + filter variables: + - name: air_temperature + action: + name: reject + maxvalue: 8.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: air_temperature@ObsErrorData # After inflation step + denominator: + name: air_temperature@ObsError + defer to post: true +# +- filter: Bounds Check + filter variables: + - name: specific_humidity + action: + name: reject + maxvalue: 8.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: specific_humidity@ObsErrorData # After inflation step + denominator: + name: specific_humidity@ObsError + defer to post: true +# +- filter: Bounds Check + filter variables: + - name: eastward_wind + action: + name: reject + maxvalue: 8.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: eastward_wind@ObsErrorData # After inflation step + denominator: + name: eastward_wind@ObsError + defer to post: true +# +- filter: Bounds Check + filter variables: + - name: northward_wind + action: + name: reject + maxvalue: 8.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: northward_wind@ObsErrorData # After inflation step + denominator: + name: northward_wind@ObsError + defer to post: true +# +#- filter: Bounds Check +# filter variables: +# - name: surface_pressure +# action: +# name: reject +# maxvalue: 4.0 +# test variables: +# - name: ObsErrorFactorQuotient@ObsFunction +# options: +# numerator: +# name: surface_pressure@ObsErrorData # After inflation step +# denominator: +# name: surface_pressure@ObsError +# defer to post: true diff --git a/ewok/jedi-gdas/ssmis_f17.yaml b/ewok/jedi-gdas/ssmis_f17.yaml new file mode 100644 index 000000000..71dd9e0a9 --- /dev/null +++ b/ewok/jedi-gdas/ssmis_f17.yaml @@ -0,0 +1,171 @@ +obs space: + name: ssmis_f17 + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/ssmis_f17.{{window_begin}}.nc4 + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).ssmis_f17.{{window_begin}}.nc4 + simulated variables: [brightness_temperature] + channels: &ssmis_f17_channels 1-24 +obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: ssmis_f17 + EndianType: little_endian + CoefficientPath: $(jedi_build)/ufo/test/Data/ +obs bias: + input file: $(experiment_dir)/{{current_cycle}}/ssmis_f17.{{background_time}}.satbias.nc4 + variational bc: + predictors: + - name: constant + - name: cloud_liquid_water + options: + satellite: SSMIS + ch19h: 12 + ch19v: 13 + ch22v: 14 + ch37h: 15 + ch37v: 16 + ch91v: 17 + ch91h: 18 + - name: cosine_of_latitude_times_orbit_node + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &ssmis_f17_tlapse $(experiment_dir)/{{current_cycle}}/ssmis_f17.{{background_time}}.tlapse.txt + - name: lapse_rate + options: + tlapse: *ssmis_f17_tlapse + - name: emissivity + - name: scan_angle + options: + var_name: scan_position + order: 4 + - name: scan_angle + options: + var_name: scan_position + order: 3 + - name: scan_angle + options: + var_name: scan_position + order: 2 + - name: scan_angle + options: + var_name: scan_position +obs filters: +#step1: Gross check (setuprad) +- filter: Background Check + filter variables: + - name: brightness_temperature + channels: *ssmis_f17_channels + threshold: 1.5 + action: + name: reject +#step1: Gross check(qcmod) +- filter: Background Check + filter variables: + - name: brightness_temperature + channels: *ssmis_f17_channels + absolute threshold: 3.5 + bias correction parameter: 1.0 + action: + name: reject +# #step2: clw check +# Keep the CLW check in yaml for further improvement. +# The test case using 2020110112 global SSMIS data shows that CLW check is not activated in GSI. +#- filter: Bounds Check +# filter variables: +# - name: brightness_temperature +# channels: 1 +# test variables: +# - name: CLWRetMW_SSMIS@ObsFunction +# options: +# satellite: SSMIS +# ch19h: 12 +# ch19v: 13 +# ch22v: 14 +# ch37h: 15 +# ch37v: 16 +# ch91v: 17 +# ch91h: 18 +# varGroup: ObsValue +# minvalue: 0.0 +# maxvalue: 0.1 +# where: +# - variable: +# name: water_area_fraction@GeoVaLs +# minvalue: 0.99 +# action: +# name: reject +#step3: +- filter: Difference Check + filter variables: + - name: brightness_temperature + channels: 1-2,12-16 + reference: brightness_temperature_2@ObsValue + value: brightness_temperature_2@HofX + minvalue: -1.5 + maxvalue: 1.5 + where: + - variable: + name: water_area_fraction@GeoVaLs + maxvalue: 0.99 +#QC_terrain: If ssmis and terrain height > 2km. do not use +- filter: Domain Check + filter variables: + - name: brightness_temperature + channels: *ssmis_f17_channels + where: + - variable: + name: height_above_mean_sea_level@MetaData + maxvalue: 2000.0 +#Do not use over mixed surface +- filter: BlackList + filter variables: + - name: brightness_temperature + channels: 1-3,8-18 + where: + - variable: + name: land_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: water_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: ice_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: surface_snow_area_fraction@GeoVaLs + maxvalue: 0.99 +#step4: Generate q.c. bounds and modified variances +- filter: BlackList + filter variables: + - name: brightness_temperature + channels: *ssmis_f17_channels + action: + name: inflate error + inflation variable: +#Surface Jacobian check + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *ssmis_f17_channels + options: + channels: *ssmis_f17_channels + obserr_demisf: [0.010, 0.010, 0.010, 0.010, 0.010] + obserr_dtempf: [0.500, 0.500, 0.500, 0.500, 0.500] +#Useflag Check +- filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *ssmis_f17_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *ssmis_f17_channels + options: + channels: *ssmis_f17_channels + use_flag: [ 1, -1, -1, -1, 1 , 1, 1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1] + minvalue: 1.0e-12 + action: + name: reject diff --git a/ewok/jedi-gdas/vadwind.yaml b/ewok/jedi-gdas/vadwind.yaml new file mode 100644 index 000000000..f4bf74fa4 --- /dev/null +++ b/ewok/jedi-gdas/vadwind.yaml @@ -0,0 +1,154 @@ +obs operator: + name: VertInterp +obs space: + name: vadwind + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/vadwind.{{window_begin}}.nc4 + obsgrouping: + group variables: ["station_id", "datetime"] + sort variable: "air_pressure" + sort order: "descending" + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).vadwind.{{window_begin}}.nc4 + simulated variables: [eastward_wind, northward_wind] +#-------------------------------------------------------------------------------------------------------------------- +obs filters: +# Begin by assigning all ObsError to a constant value. These might get overwritten later. +- filter: BlackList + filter variables: + - name: eastward_wind + - name: northward_wind + action: + name: assign error + error parameter: 2.0 # 2.0 m/s +# +# Assign the initial ObsError, based on height/pressure +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [100000, 95000, 85000, 80000, 70000, 65000, 60000, 55000, 50000, 45000, 40000, 35000, 30000, 25000, 20000, 15000, 10000] + errors: [1.4, 1.5, 1.5, 1.6, 1.6, 1.8, 1.9, 2.0, 2.1, 2.3, 2.6, 2.8, 3.0, 3.2, 2.7, 2.4, 2.1] +# +# Reject all obs with PreQC mark already set above 3 +- filter: PreQC + maxvalue: 3 + action: + name: reject +# +# Reject when pressure is less than 226 mb. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + minvalue: 22600 + action: + name: reject +# +# Observation Range Sanity Check: either wind component or velocity exceeds 135 m/s +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: reject +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: Velocity@ObsFunction + maxvalue: 135.0 + action: + name: reject +# +# Reject when difference of wind direction is more than 50 degrees. +- filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: WindDirAngleDiff@ObsFunction + maxvalue: 50.0 + action: + name: reject + defer to post: true +# +# Inflate obserror when multiple obs exist inside vertical model layers. +- filter: BlackList + filter variables: + - name: eastward_wind + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [eastward_wind] + defer to post: true +# +- filter: BlackList + filter variables: + - name: northward_wind + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [northward_wind] + defer to post: true +# +- filter: Background Check + filter variables: + - name: eastward_wind + - name: northward_wind + absolute threshold: 7.5 + action: + name: inflate error + inflation factor: 2.5 + defer to post: true +# +# If the total inflation factor is too big, reject. +- filter: Bounds Check + filter variables: + - name: eastward_wind + action: + name: reject + maxvalue: 6.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: eastward_wind@ObsErrorData # After inflation step + denominator: + name: eastward_wind@ObsError + defer to post: true +# +- filter: Bounds Check + filter variables: + - name: northward_wind + action: + name: reject + maxvalue: 6.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: northward_wind@ObsErrorData # After inflation step + denominator: + name: northward_wind@ObsError + defer to post: true diff --git a/ewok/radiosonde.yaml b/ewok/radiosonde.yaml new file mode 100644 index 000000000..98c937f80 --- /dev/null +++ b/ewok/radiosonde.yaml @@ -0,0 +1,12 @@ +obs space: + name: Radiosonde + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/radiosonde.{{window_begin}}.nc4 + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).radiosonde.{{window_begin}}.nc4 + simulated variables: + - eastward_wind + - northward_wind + - air_temperature +obs operator: + name: VertInterp diff --git a/ewok/ship.yaml b/ewok/ship.yaml new file mode 100644 index 000000000..501eb32bf --- /dev/null +++ b/ewok/ship.yaml @@ -0,0 +1,11 @@ +obs space: + name: Ship + obsdatain: + obsfile: $(experiment_dir)/{{current_cycle}}/ship.{{window_begin}}.nc4 + obsdataout: + obsfile: $(experiment_dir)/{{current_cycle}}/$(experiment).ship.{{window_begin}}.nc4 + simulated variables: + - surface_pressure +obs operator: + name: SfcPCorrected + da_psfc_scheme: UKMO diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt new file mode 100644 index 000000000..85c742dce --- /dev/null +++ b/resources/CMakeLists.txt @@ -0,0 +1,24 @@ +# (C) Copyright 2017-2020 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Include macros for creating links and symlinks +include( ufo_functions ) + +# Add list of resource files +list( APPEND ufo_resources + bmatrix/rttov/atms_bmatrix_70_test.dat + bmatrix/rttov/amsr_bmatrix_70_test.dat + rmatrix/rttov/atms_noaa_20_rmatrix_test.nc4 + rmatrix/rttov/amsr_gcomw1_rmatrix_test.nc4 + rmatrix/gnssro/gnssro_ba_rmatrix_latitude.nl + rmatrix/gnssro/gnssro_ba_rmatrix_avtemp.nl + bmatrix/gnssro/gnssro_bmatrix.txt +) + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bmatrix/rttov) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/rmatrix/rttov) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bmatrix/gnssro) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/rmatrix/gnssro) +CREATE_SYMLINK( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${ufo_resources} ) diff --git a/resources/bmatrix/gnssro/gnssro_bmatrix.txt b/resources/bmatrix/gnssro/gnssro_bmatrix.txt new file mode 100644 index 000000000..acb458bcb --- /dev/null +++ b/resources/bmatrix/gnssro/gnssro_bmatrix.txt @@ -0,0 +1,6818 @@ + 71 70 141 3 1 +-20.0 20.0 90.0 + 1 1 + 6.464102E-01 6.428906E-01 6.376235E-01 6.307880E-01 6.225962E-01 6.135557E-01 6.039683E-01 5.939720E-01 5.839072E-01 5.736691E-01 + 5.631042E-01 5.518949E-01 5.403202E-01 5.275873E-01 5.150452E-01 5.029524E-01 4.901520E-01 4.770325E-01 4.635755E-01 4.491402E-01 + 4.344629E-01 4.196383E-01 4.044198E-01 3.899038E-01 3.763206E-01 3.634270E-01 3.519810E-01 3.422927E-01 3.338342E-01 3.261566E-01 + 3.186010E-01 3.106300E-01 3.023294E-01 2.935765E-01 2.838432E-01 2.730076E-01 2.607712E-01 2.465192E-01 2.327108E-01 2.205314E-01 + 2.080751E-01 1.971684E-01 1.853464E-01 1.730678E-01 1.608268E-01 1.458917E-01 1.316280E-01 1.140093E-01 9.991724E-02 8.505491E-02 + 7.567483E-02 6.563071E-02 5.841410E-02 5.059823E-02 4.408697E-02 3.694556E-02 3.104836E-02 2.529713E-02 2.040706E-02 1.552108E-02 + 1.203875E-02 9.090391E-03 6.894019E-03 5.379176E-03 4.372177E-03 3.394597E-03 2.603753E-03 2.039942E-03 1.272953E-03 5.722496E-04 + 3.138978E-04 3.426577E-01 3.490910E-01 3.532802E-01 3.535181E-01 3.524210E-01 3.548411E-01 3.633812E-01 3.731994E-01 3.889926E-01 + 4.055438E-01 4.163987E-01 4.395017E-01 4.598849E-01 4.656510E-01 4.558396E-01 4.469573E-01 4.304626E-01 3.985081E-01 3.719308E-01 + 3.572311E-01 3.366956E-01 3.123109E-01 2.868760E-01 2.585225E-01 2.277748E-01 1.999714E-01 1.710162E-01 1.463556E-01 1.209050E-01 + 1.057361E-01 8.865328E-02 7.149710E-02 5.437393E-02 4.144325E-02 3.243334E-02 2.356141E-02 1.659335E-02 1.244441E-02 9.088066E-03 + 6.398395E-03 4.406605E-03 2.646548E-03 1.933614E-03 1.219504E-03 6.352891E-04 3.814667E-04 2.389608E-04 1.677097E-04 1.750621E-04 + 1.218809E-04 1.455114E-04 1.010456E-04 7.931125E-05 5.723567E-05 3.793379E-05 4.079336E-05 5.570335E-05 6.681128E-05 3.619433E-05 + 6.096450E-06 3.281440E-05 5.319200E-05 7.488318E-05 1.270932E-05 1.147749E-05 3.224692E-05 4.984548E-05 1.248977E-04 1.895262E-04 + 6.356454E-11 + 1 + 1.087227E+05 -1.089876E+05 -3.802088E+04 1.814448E+04 2.573815E+04 1.019541E+03 -8.904638E+03 7.318004E+02 3.243844E+03 -2.255796E+03 + -6.190664E+02 2.471122E+03 -1.562774E+03 -4.276610E+02 1.786100E+03 -1.700444E+03 2.416036E+02 1.221892E+03 -1.212115E+03 -5.533356E+01 + 9.214406E+02 -5.879604E+02 -3.332942E+02 7.182964E+02 -8.689277E+01 -7.366876E+02 5.212867E+02 5.089030E+02 -7.346482E+02 -1.493735E+02 + 7.136093E+02 -1.998958E+02 -4.551181E+02 3.923821E+02 4.913225E+01 -3.722507E+02 3.329840E+02 5.408657E+01 -3.473178E+02 3.867746E+02 + -3.224303E+02 8.473489E+01 1.346381E+02 -2.202306E+02 1.305043E+02 2.161488E+02 -3.967530E+02 1.685430E+02 1.696556E+02 -3.163128E+02 + 1.097007E+02 1.627595E+02 -1.830732E+02 6.984162E+01 5.971675E+01 -1.123374E+02 4.403826E+01 1.837658E+02 -9.692803E+01 -9.176700E+01 + 2.825599E+02 6.832057E+01 -3.121316E+02 -3.482466E+02 -1.189366E+01 7.975562E+02 1.215194E+03 8.883247E+02 4.267888E+02 1.808699E+02 + 1.060492E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 2 + -1.089876E+05 1.847347E+05 -5.919534E+04 -2.570736E+04 8.971178E+02 8.615286E+03 1.962431E+03 -2.811970E+03 -1.496162E+02 1.488731E+03 + -9.316806E+02 -5.145781E+02 1.050821E+03 -4.447662E+02 -3.631555E+02 6.571466E+02 -2.933462E+02 -2.669748E+02 4.123105E+02 -8.396294E+01 + -2.233408E+02 2.828982E+02 -8.235215E+00 -2.968025E+02 1.338894E+02 2.059370E+02 -1.762193E+02 -9.002593E+01 1.529093E+02 1.608136E+01 + -1.167873E+02 -1.008048E+01 7.597440E+01 3.034877E+01 -3.728478E+01 -5.469158E+01 2.124886E+01 6.278936E+01 -4.787347E+00 -1.128354E+02 + 1.348386E+02 -4.711734E+01 1.757437E+00 6.105034E+01 -9.952682E+01 -5.592744E+01 1.775605E+02 -1.170660E+02 7.652784E-01 1.098231E+02 + -5.749900E+01 -5.376824E+01 1.136471E+02 -5.564962E+01 -6.081571E+01 1.042591E+02 -8.582102E+01 -1.182432E+02 5.184067E+01 3.899843E+01 + -3.774692E+01 -7.017317E+01 7.651552E+01 1.678742E+02 -1.405782E+02 -4.553836E+02 -4.420553E+02 -2.590621E+02 -9.233990E+01 -1.523669E+01 + 3.281344E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 3 + -3.802088E+04 -5.919534E+04 1.932948E+05 -7.778692E+04 -3.564584E+04 9.278388E+03 1.333956E+04 -4.121488E+03 -4.112394E+03 4.472568E+03 + -9.850544E+01 -3.459102E+03 2.775956E+03 1.023841E+02 -2.327173E+03 2.448024E+03 -5.520050E+02 -1.577546E+03 1.796392E+03 -4.686554E+01 + -1.396232E+03 9.388423E+02 5.083612E+02 -1.063346E+03 1.427436E+02 9.596203E+02 -6.719686E+02 -5.719628E+02 8.543786E+02 1.025769E+02 + -7.378739E+02 3.179922E+02 3.945863E+02 -5.447269E+02 7.178412E+01 4.799088E+02 -4.887464E+02 -8.114942E+01 5.471048E+02 -5.959489E+02 + 4.383946E+02 -7.010810E+01 -2.491424E+02 2.602264E+02 -3.832775E+01 -3.262794E+02 4.551379E+02 -2.289353E+02 -1.345973E+02 3.526115E+02 + -1.211192E+02 -2.310690E+02 2.076484E+02 -5.657117E+01 -6.663230E+01 9.787025E+01 -7.390061E+01 -2.979767E+01 1.223651E+02 -7.312985E+01 + -2.205991E+02 -1.022978E+01 1.791844E+02 3.250348E+02 8.883018E+01 -6.180040E+02 -9.448507E+02 -6.611638E+02 -2.862728E+02 -1.041395E+02 + -5.372614E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 4 + 1.814448E+04 -2.570736E+04 -7.778692E+04 1.660722E+05 -7.938937E+04 -1.749793E+04 1.550972E+04 5.412547E+03 -6.394921E+03 1.198082E+02 + 3.639014E+03 -2.612070E+03 -3.046358E+02 1.900000E+03 -1.772094E+03 5.456488E+02 8.387183E+02 -1.238923E+03 3.953329E+02 6.766268E+02 + -7.760528E+02 -5.396981E+01 5.755062E+02 -2.323705E+02 -2.973250E+02 4.591346E+02 -1.182662E+02 -4.169044E+02 4.011325E+02 1.901406E+02 + -4.820572E+02 1.241375E+02 3.451561E+02 -3.500749E+02 -2.554667E+01 3.927796E+02 -3.073183E+02 -1.539550E+02 3.739230E+02 -2.944246E+02 + 2.054795E+02 -5.739072E+01 -1.784621E+02 2.270533E+02 1.251353E+00 -2.882120E+02 2.987052E+02 -5.038078E+01 -1.661220E+02 1.747680E+02 + -7.421315E+01 -7.059689E+01 1.653011E+02 -8.592142E+01 -9.059046E+01 8.744352E+01 9.903547E+01 -4.656407E+01 -3.044408E+01 -5.876654E+00 + -1.269080E+02 5.591402E+01 1.144408E+02 7.873014E+01 1.763447E+02 -9.896059E+01 -3.988680E+02 -3.224262E+02 -1.591019E+02 -8.258308E+01 + -5.703163E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 5 + 2.573815E+04 8.971178E+02 -3.564584E+04 -7.938937E+04 1.661504E+05 -8.064722E+04 -1.471555E+04 2.045852E+04 3.532500E+03 -1.059110E+04 + 2.974577E+03 4.621427E+03 -5.206066E+03 1.188792E+03 2.706319E+03 -3.539785E+03 1.260156E+03 1.692565E+03 -2.460763E+03 4.486210E+02 + 1.652005E+03 -1.390427E+03 -4.457598E+02 1.357061E+03 -3.601978E+02 -9.959922E+02 8.136865E+02 4.329196E+02 -8.449045E+02 6.294632E+01 + 6.007271E+02 -3.896400E+02 -2.281920E+02 5.203199E+02 -1.922999E+02 -3.977704E+02 5.457431E+02 3.869232E-01 -6.537780E+02 8.385802E+02 + -6.197527E+02 1.055519E+02 3.286988E+02 -3.207562E+02 -9.566020E+00 4.665355E+02 -5.486915E+02 2.915767E+02 3.743966E+01 -3.012540E+02 + 1.622027E+02 1.678608E+02 -2.998387E+02 1.629151E+02 1.491523E+02 -2.493254E+02 1.219306E+02 1.788923E+01 -1.409875E+02 2.145463E+02 + 4.853360E+01 -5.950679E+01 1.271154E+02 -1.396452E+02 -1.181389E+02 1.977253E+02 2.602322E+02 1.372004E+02 8.225874E+00 -3.521460E+01 + -3.551858E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 6 + 1.019541E+03 8.615286E+03 9.278388E+03 -1.749793E+04 -8.064722E+04 1.520822E+05 -8.205431E+04 -5.677237E+03 2.074979E+04 -2.278936E+03 + -8.280680E+03 5.932724E+03 4.406444E+02 -3.967955E+03 3.466006E+03 -6.803099E+02 -1.965580E+03 2.388029E+03 -4.863635E+02 -1.513602E+03 + 1.455600E+03 2.015426E+02 -1.117581E+03 4.090027E+02 6.437125E+02 -7.729250E+02 -1.767870E+01 7.019981E+02 -3.978474E+02 -4.131187E+02 + 5.611577E+02 9.835020E+00 -4.925946E+02 3.653575E+02 1.378548E+02 -5.195087E+02 3.440220E+02 3.026852E+02 -6.855842E+02 5.184104E+02 + -1.982064E+02 -5.803625E+01 2.665601E+02 -2.213418E+02 -1.183683E+02 4.249492E+02 -3.883416E+02 1.251592E+02 1.385765E+02 -2.130834E+02 + 6.186514E+01 1.570628E+02 -2.347889E+02 8.313527E+01 1.434341E+02 -9.672595E+01 -3.465348E+01 -6.704561E+01 5.245552E+00 1.782308E+02 + 5.893764E+01 -1.087481E+02 -2.004502E+01 -1.667520E+02 -1.674098E+02 2.692830E+02 4.941029E+02 3.008910E+02 7.537816E+01 7.429135E+00 + -2.040558E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 7 + -8.904638E+03 1.962431E+03 1.333956E+04 1.550972E+04 -1.471555E+04 -8.205431E+04 1.456378E+05 -8.524001E+04 -2.226862E+02 2.388319E+04 + -7.278028E+03 -7.564789E+03 8.696498E+03 -2.262904E+03 -3.733516E+03 5.097330E+03 -2.043112E+03 -2.047625E+03 3.327187E+03 -8.719776E+02 + -1.898129E+03 1.754260E+03 4.523856E+02 -1.597030E+03 4.681363E+02 1.133877E+03 -9.635578E+02 -4.840872E+02 1.008382E+03 -9.076024E+01 + -7.218848E+02 4.716181E+02 2.699502E+02 -6.247469E+02 2.115918E+02 5.081101E+02 -6.354194E+02 -7.919876E+01 8.230816E+02 -8.649930E+02 + 4.955342E+02 -3.342076E+01 -2.721338E+02 2.066248E+02 9.124800E+01 -3.948746E+02 3.934758E+02 -2.139878E+02 -6.341419E+00 1.781906E+02 + -1.147472E+02 -8.839993E+01 2.008892E+02 -1.091779E+02 -1.174847E+02 1.797278E+02 -1.163098E+02 3.959748E+01 1.725230E+02 -3.219742E+02 + -7.228778E+01 1.774435E+02 -1.682542E+02 -4.464647E+01 1.237111E+02 5.695972E+01 9.289240E+00 -2.060261E+01 -7.801420E+00 1.679603E+01 + 1.741721E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 8 + 7.318004E+02 -2.811970E+03 -4.121488E+03 5.412547E+03 2.045852E+04 -5.677237E+03 -8.524001E+04 1.378698E+05 -8.375784E+04 5.784214E+03 + 2.126945E+04 -1.049598E+04 -3.770412E+03 8.204262E+03 -4.630129E+03 -1.054437E+03 4.148238E+03 -2.898138E+03 -6.686278E+02 2.571042E+03 + -1.249739E+03 -1.058414E+03 1.409693E+03 1.539806E+02 -1.226605E+03 5.556652E+02 6.268442E+02 -7.749529E+02 -7.828637E+01 6.866868E+02 + -3.222458E+02 -3.947491E+02 5.511773E+02 -4.288631E+01 -4.061189E+02 4.099399E+02 -8.471470E+01 -3.973211E+02 6.674311E+02 -4.409506E+02 + 9.574261E+00 2.017664E+02 -2.272322E+02 6.763980E+01 2.436472E+02 -4.070204E+02 2.725531E+02 -8.069419E+01 -2.617910E+01 8.684886E+01 + -1.887268E+01 -6.640318E+01 1.445726E+02 -1.286184E+02 -9.035713E+01 1.849494E+02 -5.578498E+01 -1.957831E+01 9.198874E+01 -8.431094E+01 + -4.519169E+00 1.135655E+01 -2.221308E+02 6.991798E+01 3.103952E+02 3.116604E+01 -2.090335E+02 -1.451556E+02 -8.986112E+00 2.759834E+01 + 2.306890E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 9 + 3.243844E+03 -1.496162E+02 -4.112394E+03 -6.394921E+03 3.532500E+03 2.074979E+04 -2.226862E+02 -8.375784E+04 1.308215E+05 -8.447252E+04 + 1.314181E+04 1.843170E+04 -1.427484E+04 7.472023E+02 7.278124E+03 -6.849184E+03 1.258996E+03 3.708892E+03 -3.923809E+03 1.542796E+02 + 2.638512E+03 -1.513531E+03 -1.174747E+03 1.706108E+03 8.989055E+01 -1.487254E+03 6.862447E+02 9.019639E+02 -1.016150E+03 -2.307320E+02 + 9.441842E+02 -3.320797E+02 -5.523192E+02 6.452833E+02 -7.735602E+00 -6.160554E+02 5.054940E+02 2.740476E+02 -8.902402E+02 8.111606E+02 + -4.121648E+02 3.366930E+01 2.220595E+02 -1.921284E+02 -8.922343E+01 3.770705E+02 -3.402666E+02 1.782145E+02 -3.145739E+01 -1.419882E+02 + 1.561193E+02 1.110916E+01 -2.035789E+02 1.791664E+02 1.043166E+02 -2.555868E+02 1.870222E+02 -4.979820E+00 -2.815601E+02 3.628128E+02 + 1.449344E+02 -2.877991E+02 1.636769E+02 2.398753E+02 -5.913726E+01 -1.906675E+02 -1.730476E+02 -6.247409E+01 9.283835E+00 8.908429E+00 + 5.198264E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 10 + -2.255796E+03 1.488731E+03 4.472568E+03 1.198082E+02 -1.059110E+04 -2.278936E+03 2.388319E+04 5.784214E+03 -8.447252E+04 1.245329E+05 + -8.380482E+04 1.819512E+04 1.589425E+04 -1.652149E+04 4.053138E+03 6.057620E+03 -7.596995E+03 2.192966E+03 3.524201E+03 -3.779009E+03 + -1.451669E+02 2.588300E+03 -1.129921E+03 -1.424440E+03 1.688473E+03 2.161594E+02 -1.411664E+03 5.277230E+02 8.527262E+02 -8.571359E+02 + -2.188885E+02 8.188596E+02 -3.530326E+02 -4.258702E+02 5.452219E+02 -8.161204E+01 -2.853432E+02 3.914317E+02 -3.010382E+02 -3.129233E+01 + 3.665593E+02 -3.280306E+02 7.921622E+01 1.516877E+02 -3.066623E+02 2.091556E+02 1.804754E+01 -1.459904E+02 3.910850E+01 1.441068E+02 + -1.838135E+02 1.651674E+01 8.946634E+01 -3.491092E+01 2.547012E+01 -1.331669E+02 2.779572E+01 1.733590E+02 -1.292408E+02 -2.202496E+02 + 8.532648E+00 2.553899E+02 2.514292E+02 -2.009252E+02 -4.433929E+02 -1.019040E+02 2.400575E+02 2.328864E+02 8.834634E+01 1.578966E+01 + 4.671918E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 11 + -6.190664E+02 -9.316806E+02 -9.850544E+01 3.639014E+03 2.974577E+03 -8.280680E+03 -7.278028E+03 2.126945E+04 1.314181E+04 -8.380482E+04 + 1.155355E+05 -8.014854E+04 2.326496E+04 1.079360E+04 -1.614580E+04 6.749243E+03 3.988909E+03 -7.207278E+03 2.620970E+03 2.902618E+03 + -3.258104E+03 -3.150652E+02 2.449495E+03 -9.175062E+02 -1.505359E+03 1.549753E+03 4.204966E+02 -1.458197E+03 4.097465E+02 9.118614E+02 + -8.656181E+02 -1.979358E+02 8.663358E+02 -4.292652E+02 -3.891734E+02 7.011432E+02 -2.323340E+02 -6.197984E+02 1.041909E+03 -6.807398E+02 + 1.115780E+02 1.754348E+02 -2.484704E+02 7.195728E+01 2.753198E+02 -4.385515E+02 2.184746E+02 3.390743E+01 -3.316296E+01 2.233992E-01 + -1.726007E+01 2.337230E+01 1.197468E+02 -2.003555E+02 -4.954411E+01 3.580890E+02 -2.960009E+02 -1.116289E+02 4.437569E+02 -2.279708E+02 + -1.952586E+02 2.491936E+02 -1.467092E+02 -2.152790E+02 1.689496E+00 3.736392E+01 4.662044E+01 1.415575E+01 -2.018882E+01 -1.596886E+01 + -8.590055E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 12 + 2.471122E+03 -5.145781E+02 -3.459102E+03 -2.612070E+03 4.621427E+03 5.932724E+03 -7.564789E+03 -1.049598E+04 1.843170E+04 1.819512E+04 + -8.014854E+04 1.102941E+05 -8.208720E+04 2.936297E+04 6.873007E+03 -1.729624E+04 9.041909E+03 3.222358E+03 -7.583777E+03 2.581735E+03 + 3.274416E+03 -3.189656E+03 -7.730842E+02 2.680098E+03 -8.542597E+02 -1.579055E+03 1.481164E+03 4.917434E+02 -1.470850E+03 3.808343E+02 + 1.038549E+03 -8.905187E+02 -3.579773E+02 9.288821E+02 -2.522202E+02 -6.301007E+02 6.201476E+02 1.251893E+02 -7.211812E+02 7.429222E+02 + -5.032441E+02 1.730106E+02 1.415316E+02 -2.241918E+02 8.292874E+01 2.082967E+02 -2.929162E+02 1.938667E+02 -3.760270E+01 -2.153575E+02 + 2.972998E+02 -9.551246E+01 -2.282252E+02 3.547308E+02 -7.473294E+01 -1.799536E+02 2.312309E+02 -1.318254E+02 -1.731263E+02 3.251488E+02 + 1.559303E+02 -3.525649E+02 -2.216252E+02 3.399599E+02 5.037845E+02 1.121070E+02 -2.384219E+02 -2.433058E+02 -1.123165E+02 -3.843852E+01 + -2.137346E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 13 + -1.562774E+03 1.050821E+03 2.775956E+03 -3.046358E+02 -5.206066E+03 4.406444E+02 8.696498E+03 -3.770412E+03 -1.427484E+04 1.589425E+04 + 2.326496E+04 -8.208720E+04 1.049973E+05 -7.664478E+04 2.964470E+04 5.836032E+03 -1.642160E+04 7.842845E+03 4.147448E+03 -6.750220E+03 + 8.557172E+02 3.838030E+03 -2.304152E+03 -1.702240E+03 2.733808E+03 -1.318171E+02 -2.143999E+03 1.159962E+03 1.181928E+03 -1.429828E+03 + -2.017184E+02 1.150689E+03 -5.326960E+02 -4.917930E+02 6.773418E+02 -1.347782E+02 -4.102109E+02 5.728451E+02 -3.644086E+02 -3.158836E+01 + 3.795056E+02 -3.763394E+02 1.373264E+02 1.718867E+02 -4.069255E+02 2.739720E+02 3.803254E+01 -2.322866E+02 1.568009E+02 1.107577E+02 + -2.414494E+02 1.309313E+02 3.553518E+01 -1.374853E+02 9.262994E+01 -9.070703E+01 4.350318E+01 1.561250E+02 -1.440334E+02 -3.285566E+01 + -1.865246E+01 -3.080170E+01 2.214866E+02 2.915770E+00 -1.789004E+02 4.635498E+01 2.012353E+02 1.881268E+02 1.128441E+02 4.880179E+01 + 2.278694E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 14 + -4.276610E+02 -4.447662E+02 1.023841E+02 1.900000E+03 1.188792E+03 -3.967955E+03 -2.262904E+03 8.204262E+03 7.472023E+02 -1.652149E+04 + 1.079360E+04 2.936297E+04 -7.664478E+04 9.604061E+04 -7.640429E+04 3.222979E+04 4.717141E+03 -1.583964E+04 6.550261E+03 5.023949E+03 + -5.870327E+03 -6.006416E+02 4.120397E+03 -1.385161E+03 -2.343510E+03 2.091409E+03 7.873816E+02 -1.890618E+03 3.271579E+02 1.171962E+03 + -9.436012E+02 -3.196022E+02 1.025153E+03 -4.490509E+02 -5.757078E+02 9.010278E+02 -1.631700E+02 -8.982930E+02 1.313070E+03 -8.404262E+02 + 1.403192E+02 1.703610E+02 -2.071350E+02 4.444427E+01 2.490772E+02 -4.165655E+02 2.836460E+02 -2.517871E+01 -1.082130E+02 1.587622E+02 + -1.037564E+02 -4.489942E+01 2.137679E+02 -2.407027E+02 2.094268E+01 2.587344E+02 -2.821466E+02 3.305486E+01 2.668312E+02 -2.229295E+02 + -6.252343E+01 2.314364E+02 -8.467222E+01 -3.110092E+02 -2.507040E+02 -9.508181E+01 2.933176E+01 5.309958E+01 2.842915E+01 1.276796E+01 + 9.156942E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 15 + 1.786100E+03 -3.631555E+02 -2.327173E+03 -1.772094E+03 2.706319E+03 3.466006E+03 -3.733516E+03 -4.630129E+03 7.278124E+03 4.053138E+03 + -1.614580E+04 6.873007E+03 2.964470E+04 -7.640429E+04 1.011193E+05 -7.943645E+04 3.017930E+04 7.798520E+03 -1.598899E+04 3.412534E+03 + 6.885016E+03 -4.572137E+03 -2.541539E+03 4.143709E+03 -3.358572E+02 -2.707806E+03 1.706471E+03 1.098941E+03 -1.968256E+03 1.930970E+02 + 1.545338E+03 -9.838219E+02 -7.055082E+02 1.157851E+03 -6.152635E+01 -9.462565E+02 6.571910E+02 4.048531E+02 -1.104922E+03 9.834448E+02 + -5.570264E+02 2.417528E+02 5.748360E+01 -2.647806E+02 2.330105E+02 1.038914E+02 -3.983206E+02 4.316254E+02 -1.782754E+02 -2.683343E+02 + 4.113896E+02 -1.438540E+02 -2.290217E+02 4.039069E+02 -1.405237E+02 -2.339039E+02 3.160848E+02 -1.892557E+02 -2.405941E+02 3.221960E+02 + 7.191488E+01 -2.396615E+02 5.998692E+01 3.596201E+02 2.026655E+02 -2.125890E+02 -3.274799E+02 -1.953098E+02 -7.930304E+01 -3.585500E+01 + -2.181845E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 16 + -1.700444E+03 6.571466E+02 2.448024E+03 5.456488E+02 -3.539785E+03 -6.803099E+02 5.097330E+03 -1.054437E+03 -6.849184E+03 6.057620E+03 + 6.749243E+03 -1.729624E+04 5.836032E+03 3.222979E+04 -7.943645E+04 1.002775E+05 -7.620394E+04 2.638553E+04 1.129456E+04 -1.510000E+04 + 1.917383E+02 7.998401E+03 -3.168736E+03 -3.686942E+03 3.958080E+03 6.419428E+02 -3.332940E+03 1.127303E+03 2.078344E+03 -1.778304E+03 + -7.073629E+02 1.661566E+03 -4.459798E+02 -9.259849E+02 9.055955E+02 1.828712E+01 -7.106704E+02 7.775249E+02 -3.905010E+02 -8.748682E+01 + 4.010905E+02 -3.874300E+02 7.379042E+01 2.774720E+02 -4.020580E+02 1.577053E+02 1.856687E+02 -3.701519E+02 2.012026E+02 1.542431E+02 + -2.768237E+02 1.470953E+02 4.253478E+01 -1.556474E+02 9.925860E+01 -3.928460E+01 1.083765E+01 9.251173E+01 -4.672422E+01 -4.904650E+01 + -9.508381E+01 2.133460E+01 9.102041E+01 -9.028037E+01 -2.903747E+01 2.249682E+02 2.723759E+02 1.461704E+02 4.231326E+01 6.101620E+00 + -2.156160E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 17 + 2.416036E+02 -2.933462E+02 -5.520050E+02 8.387183E+02 1.260156E+03 -1.965580E+03 -2.043112E+03 4.148238E+03 1.258996E+03 -7.596995E+03 + 3.988909E+03 9.041909E+03 -1.642160E+04 4.717141E+03 3.017930E+04 -7.620394E+04 1.023397E+05 -7.990586E+04 2.315765E+04 1.576062E+04 + -1.422863E+04 -3.308071E+03 9.262769E+03 -1.777950E+03 -5.179285E+03 3.559691E+03 1.950247E+03 -3.347395E+03 1.388120E+02 2.249839E+03 + -1.322683E+03 -8.889960E+02 1.610696E+03 -3.532150E+02 -1.137629E+03 1.233653E+03 1.032387E+02 -1.558056E+03 1.853178E+03 -9.938564E+02 + 8.132044E+01 2.357417E+02 -1.604330E+02 -4.540502E+01 1.848815E+02 -2.750681E+02 3.366568E+02 -2.721116E+02 7.294835E+01 2.113516E+02 + -2.534093E+02 -2.964481E+01 2.712152E+02 -2.814773E+02 2.022620E+01 3.251180E+02 -3.566159E+02 1.047153E+02 3.163256E+02 -3.313750E+02 + 6.178361E+01 2.845732E+02 -2.453362E+02 -2.607311E+02 2.278715E+01 1.679279E+02 2.741760E+01 -1.280640E+02 -1.017250E+02 -2.691953E+01 + -2.192004E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 18 + 1.221892E+03 -2.669748E+02 -1.577546E+03 -1.238923E+03 1.692565E+03 2.388029E+03 -2.047625E+03 -2.898138E+03 3.708892E+03 2.192966E+03 + -7.207278E+03 3.222358E+03 7.842845E+03 -1.583964E+04 7.798520E+03 2.638553E+04 -7.990586E+04 1.127820E+05 -8.337972E+04 1.833439E+04 + 1.903944E+04 -1.264867E+04 -5.939626E+03 9.055325E+03 -5.395880E+01 -5.655490E+03 2.677252E+03 2.565952E+03 -3.213126E+03 -2.230483E+02 + 2.584096E+03 -1.287199E+03 -1.282976E+03 1.794284E+03 -1.424172E+01 -1.499400E+03 1.025518E+03 5.664752E+02 -1.506666E+03 1.136832E+03 + -3.970802E+02 2.744447E+00 1.849567E+02 -1.687043E+02 4.330122E+01 2.211726E+02 -4.869800E+02 5.041604E+02 -1.027348E+02 -4.136420E+02 + 4.123351E+02 1.058239E+01 -3.282354E+02 3.302786E+02 -3.675528E+01 -2.048802E+02 1.902169E+02 -1.035989E+02 -1.821782E+02 2.245308E+02 + 1.107891E+02 -2.350073E+02 8.026424E+01 2.967854E+02 -2.056679E+01 -2.716495E+02 -1.849937E+02 -1.697341E+01 4.644068E+01 3.630994E+01 + 2.800669E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 19 + -1.212115E+03 4.123105E+02 1.796392E+03 3.953329E+02 -2.460763E+03 -4.863635E+02 3.327187E+03 -6.686278E+02 -3.923809E+03 3.524201E+03 + 2.620970E+03 -7.583777E+03 4.147448E+03 6.550261E+03 -1.598899E+04 1.129456E+04 2.315765E+04 -8.337972E+04 1.167400E+05 -8.210316E+04 + 1.485899E+04 2.109143E+04 -1.205364E+04 -6.515576E+03 9.140155E+03 7.661274E+01 -5.897875E+03 2.569800E+03 3.060727E+03 -3.214310E+03 + -6.783179E+02 2.684687E+03 -1.010780E+03 -1.394400E+03 1.671587E+03 -1.186894E+02 -1.327974E+03 1.538306E+03 -7.388288E+02 -1.159227E+02 + 4.592028E+02 -3.217966E+02 -2.639825E+01 2.414000E+02 -2.557643E+02 1.120775E+02 6.715507E+01 -1.752694E+02 8.864364E+01 3.861404E+01 + -7.269882E+01 1.406494E+02 -1.283131E+02 -1.436966E+01 9.754774E+01 -2.003398E+02 1.611702E+02 2.346188E+01 -1.533968E+02 1.393098E+02 + -1.430981E+02 -1.008900E+02 2.126274E+02 -2.435346E+00 -9.210436E+01 5.040508E+01 2.172934E+02 2.462998E+02 1.265142E+02 3.446812E+01 + 1.006342E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 20 + -5.533356E+01 -8.396294E+01 -4.686554E+01 6.766268E+02 4.486210E+02 -1.513602E+03 -8.719776E+02 2.571042E+03 1.542796E+02 -3.779009E+03 + 2.902618E+03 2.581735E+03 -6.750220E+03 5.023949E+03 3.412534E+03 -1.510000E+04 1.576062E+04 1.833439E+04 -8.210316E+04 1.196608E+05 + -8.468854E+04 1.303766E+04 2.308204E+04 -1.174652E+04 -8.083560E+03 9.598316E+03 1.048412E+03 -6.463948E+03 2.142340E+03 3.278868E+03 + -3.169134E+03 -5.304919E+02 2.721706E+03 -1.352831E+03 -1.408456E+03 2.152837E+03 -3.159714E+02 -1.838446E+03 2.326002E+03 -1.157085E+03 + -9.211150E+01 4.565886E+02 -2.500314E+02 -1.402254E+02 3.445514E+02 -3.695360E+02 3.547477E+02 -2.473516E+02 -6.221867E+01 3.844578E+02 + -2.473510E+02 -2.073629E+02 4.209082E+02 -2.023218E+02 -1.308167E+02 3.041179E+02 -1.447783E+02 -1.041428E+01 2.436160E+02 -2.153530E+02 + -9.238789E+01 3.023495E+02 -1.895316E+02 -3.756150E+02 7.890773E+01 2.567009E+02 5.636020E+01 -1.048673E+02 -1.134724E+02 -6.285847E+01 + -4.008536E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 21 + 9.214406E+02 -2.233408E+02 -1.396232E+03 -7.760528E+02 1.652005E+03 1.455600E+03 -1.898129E+03 -1.249739E+03 2.638512E+03 -1.451669E+02 + -3.258104E+03 3.274416E+03 8.557172E+02 -5.870327E+03 6.885016E+03 1.917383E+02 -1.422863E+04 1.903944E+04 1.485899E+04 -8.468854E+04 + 1.250478E+05 -8.531909E+04 1.213378E+04 2.197415E+04 -1.096839E+04 -7.472009E+03 8.735081E+03 1.038696E+03 -6.128046E+03 2.056206E+03 + 3.358993E+03 -3.202565E+03 -7.770874E+02 2.826744E+03 -1.034625E+03 -1.485601E+03 1.880756E+03 -4.900982E+02 -9.318673E+02 1.162623E+03 + -5.669611E+02 1.436597E+01 2.522156E+02 -1.411705E+02 -8.337317E+00 1.418549E+02 -3.552703E+02 4.844173E+02 -1.996055E+02 -2.382229E+02 + 2.712016E+02 -1.000420E+02 -5.935550E+01 1.478806E+02 -9.438253E+01 4.493056E+01 -4.270738E+01 -6.360098E+01 2.444590E+00 -3.669138E+01 + 1.762411E+02 1.600385E+01 -9.858089E+01 1.654279E+02 9.326353E+01 -1.534187E+02 -2.751188E+02 -2.234126E+02 -9.177968E+01 -2.471162E+01 + -9.510806E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 22 + -5.879604E+02 2.828982E+02 9.388423E+02 -5.396981E+01 -1.390427E+03 2.015426E+02 1.754260E+03 -1.058414E+03 -1.513531E+03 2.588300E+03 + -3.150652E+02 -3.189656E+03 3.838030E+03 -6.006416E+02 -4.572137E+03 7.998401E+03 -3.308071E+03 -1.264867E+04 2.109143E+04 1.303766E+04 + -8.531909E+04 1.239136E+05 -8.412425E+04 1.303870E+04 2.248721E+04 -1.208165E+04 -7.444325E+03 9.396534E+03 8.867620E+02 -6.105768E+03 + 2.187617E+03 2.846854E+03 -3.083950E+03 -6.414536E+01 2.382756E+03 -1.708243E+03 -6.745034E+02 2.290493E+03 -2.023996E+03 6.008923E+02 + 4.913630E+02 -6.208092E+02 1.785587E+02 2.904036E+02 -4.996276E+02 4.178878E+02 -1.325186E+02 -1.421586E+02 1.957982E+02 -1.168807E+02 + -1.887983E+01 2.265732E+02 -2.966768E+02 4.980791E+01 2.017004E+02 -2.845664E+02 8.715709E+01 1.174167E+02 -2.136010E+02 2.029454E+02 + 1.151886E+02 -3.552112E+02 9.802297E+01 3.427091E+02 -2.198538E+01 -1.011013E+02 1.027853E+02 1.836852E+02 1.299530E+02 6.313784E+01 + 3.940877E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 23 + -3.332942E+02 -8.235215E+00 5.083612E+02 5.755062E+02 -4.457598E+02 -1.117581E+03 4.523856E+02 1.409693E+03 -1.174747E+03 -1.129921E+03 + 2.449495E+03 -7.730842E+02 -2.304152E+03 4.120397E+03 -2.541539E+03 -3.168736E+03 9.262769E+03 -5.939626E+03 -1.205364E+04 2.308204E+04 + 1.213378E+04 -8.412425E+04 1.226742E+05 -8.447830E+04 1.284736E+04 2.235378E+04 -1.143818E+04 -7.736581E+03 9.252564E+03 9.290179E+02 + -6.388994E+03 2.540412E+03 3.167818E+03 -3.661279E+03 -2.826640E+02 3.020072E+03 -1.884412E+03 -9.646766E+02 2.614237E+03 -1.989442E+03 + 4.454645E+02 4.311119E+02 -4.686642E+02 -4.502549E+01 4.290962E+02 -5.110260E+02 4.903915E+02 -4.550005E+02 1.528702E+02 2.742899E+02 + -2.870861E+02 2.755282E+01 1.887124E+02 -1.720620E+02 5.006400E+01 3.038993E+01 -2.267232E+01 7.921620E+01 5.091432E+01 -7.884136E+01 + -2.185732E+02 8.320181E+01 1.530733E+02 -2.256697E+02 -2.511239E+02 -8.522603E+00 1.794331E+02 1.708727E+02 6.672145E+01 2.305104E+01 + 1.491841E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 24 + 7.182964E+02 -2.968025E+02 -1.063346E+03 -2.323705E+02 1.357061E+03 4.090027E+02 -1.597030E+03 1.539806E+02 1.706108E+03 -1.424440E+03 + -9.175062E+02 2.680098E+03 -1.702240E+03 -1.385161E+03 4.143709E+03 -3.686942E+03 -1.777950E+03 9.055325E+03 -6.515576E+03 -1.174652E+04 + 2.197415E+04 1.303870E+04 -8.447830E+04 1.238262E+05 -8.538612E+04 1.369244E+04 2.196031E+04 -1.179240E+04 -7.623156E+03 9.061382E+03 + 1.037425E+03 -5.946810E+03 2.158454E+03 2.676031E+03 -2.943974E+03 4.329617E+01 2.108284E+03 -2.013582E+03 6.055805E+02 5.752592E+02 + -8.115654E+02 3.838589E+02 1.715783E+02 -3.592415E+02 2.865857E+02 -1.162451E+02 -1.930240E+02 4.836970E+02 -2.887698E+02 -1.681781E+02 + 2.774353E+02 -1.527896E+02 4.760960E+01 6.778159E+01 -1.582386E+02 1.889150E+02 -3.780277E+01 -1.536330E+02 1.380358E+02 -9.028511E+01 + -5.790854E+01 2.184331E+02 -8.520632E+01 -2.042928E+02 3.517874E+01 7.021015E+01 -6.226534E+00 -8.511726E+00 9.030034E+00 3.316750E+00 + -5.857100E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 25 + -8.689277E+01 1.338894E+02 1.427436E+02 -2.973250E+02 -3.601978E+02 6.437125E+02 4.681363E+02 -1.226605E+03 8.989055E+01 1.688473E+03 + -1.505359E+03 -8.542597E+02 2.733808E+03 -2.343510E+03 -3.358572E+02 3.958080E+03 -5.179285E+03 -5.395880E+01 9.140155E+03 -8.083560E+03 + -1.096839E+04 2.248721E+04 1.284736E+04 -8.538612E+04 1.229540E+05 -8.360528E+04 1.303403E+04 2.217164E+04 -1.158729E+04 -7.330016E+03 + 9.208098E+03 2.969718E+02 -5.963105E+03 3.097210E+03 2.409142E+03 -3.878885E+03 9.389144E+02 2.534526E+03 -3.611525E+03 2.038378E+03 + 5.606016E+01 -8.495081E+02 4.657571E+02 3.087280E+02 -7.347535E+02 7.083122E+02 -4.075288E+02 1.357153E+02 1.380805E+01 -1.361209E+02 + 1.086331E+02 2.992582E+01 -1.981208E+02 1.466251E+02 1.285548E+01 -1.266756E+02 5.221886E+01 -9.120871E-01 -1.203322E+02 8.888288E+01 + 2.436046E+02 -1.761578E+02 -2.051065E+02 3.387625E+02 3.747322E+02 -5.019596E+01 -3.546144E+02 -3.034046E+02 -1.185198E+02 -4.357321E+01 + -3.089299E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 26 + -7.366876E+02 2.059370E+02 9.596203E+02 4.591346E+02 -9.959922E+02 -7.729250E+02 1.133877E+03 5.556652E+02 -1.487254E+03 2.161594E+02 + 1.549753E+03 -1.579055E+03 -1.318171E+02 2.091409E+03 -2.707806E+03 6.419428E+02 3.559691E+03 -5.655490E+03 7.661274E+01 9.598316E+03 + -7.472009E+03 -1.208165E+04 2.235378E+04 1.369244E+04 -8.360528E+04 1.213094E+05 -8.478032E+04 1.350558E+04 2.277696E+04 -1.186510E+04 + -7.914977E+03 9.556388E+03 7.507699E+02 -6.271580E+03 2.632405E+03 2.767070E+03 -3.669544E+03 8.532065E+02 1.844028E+03 -2.129719E+03 + 8.243970E+02 2.968211E+02 -6.592188E+02 2.589134E+02 2.587369E+02 -5.239398E+02 5.892907E+02 -6.037106E+02 2.849070E+02 2.911175E+02 + -4.002850E+02 1.102851E+02 1.610320E+02 -2.218728E+02 9.957100E+01 5.194016E+01 -6.886915E+01 5.140927E+01 5.089980E+01 -1.057203E+00 + -9.731338E+01 1.360861E+01 1.439034E+02 -1.318951E+01 -4.439375E+01 7.403747E+01 2.848009E+01 -8.737412E+01 -1.064982E+02 -5.500522E+01 + -2.411873E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 27 + 5.212867E+02 -1.762193E+02 -6.719686E+02 -1.182662E+02 8.136865E+02 -1.767870E+01 -9.635578E+02 6.268442E+02 6.862447E+02 -1.411664E+03 + 4.204966E+02 1.481164E+03 -2.143999E+03 7.873816E+02 1.706471E+03 -3.332940E+03 1.950247E+03 2.677252E+03 -5.897875E+03 1.048412E+03 + 8.735081E+03 -7.444325E+03 -1.143818E+04 2.196031E+04 1.303403E+04 -8.478032E+04 1.246867E+05 -8.524850E+04 1.240007E+04 2.210861E+04 + -1.123513E+04 -7.151785E+03 8.898331E+03 -4.765080E+01 -5.463151E+03 3.330480E+03 1.476060E+03 -3.866209E+03 2.998219E+03 -6.715091E+02 + -8.945873E+02 9.356660E+02 -1.215328E+02 -5.775850E+02 7.178689E+02 -4.742840E+02 5.651825E+01 2.668302E+02 -1.806575E+02 -8.349680E+01 + 1.832521E+02 -1.346450E+02 1.004857E+02 4.094381E+01 -1.181729E+02 8.865173E+01 6.122630E+00 -6.955904E+01 1.267051E+02 -5.804080E+01 + -2.549084E+02 2.022149E+02 2.161538E+02 -3.980999E+02 -4.316683E+02 3.342695E+01 3.470970E+02 2.911306E+02 1.016294E+02 3.243763E+01 + 2.517410E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 28 + 5.089030E+02 -9.002593E+01 -5.719628E+02 -4.169044E+02 4.329196E+02 7.019981E+02 -4.840872E+02 -7.749529E+02 9.019639E+02 5.277230E+02 + -1.458197E+03 4.917434E+02 1.159962E+03 -1.890618E+03 1.098941E+03 1.127303E+03 -3.347395E+03 2.565952E+03 2.569800E+03 -6.463948E+03 + 1.038696E+03 9.396534E+03 -7.736581E+03 -1.179240E+04 2.217164E+04 1.350558E+04 -8.524850E+04 1.227754E+05 -8.393699E+04 1.351644E+04 + 2.225069E+04 -1.249324E+04 -6.849538E+03 9.871169E+03 -4.209926E+02 -6.198319E+03 4.193891E+03 1.415575E+03 -4.388408E+03 3.058636E+03 + -3.590008E+02 -1.007529E+03 9.308562E+02 3.116098E+01 -8.603734E+02 1.125246E+03 -8.250127E+02 4.128467E+02 -1.214454E+02 -2.323262E+02 + 2.874630E+02 -4.220410E+01 -2.102153E+02 2.242262E+02 -4.857517E+01 -1.665331E+02 1.168194E+02 3.316546E+01 -1.722840E+02 7.154672E+01 + 2.144101E+02 -2.176703E+02 -1.949068E+02 2.685832E+02 1.766454E+02 -1.396625E+02 -1.000074E+02 8.600621E+01 1.259497E+02 6.177182E+01 + 2.580050E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 29 + -7.346482E+02 1.529093E+02 8.543786E+02 4.011325E+02 -8.449045E+02 -3.978474E+02 1.008382E+03 -7.828637E+01 -1.016150E+03 8.527262E+02 + 4.097465E+02 -1.470850E+03 1.181928E+03 3.271579E+02 -1.968256E+03 2.078344E+03 1.388120E+02 -3.213126E+03 3.060727E+03 2.142340E+03 + -6.128046E+03 8.867620E+02 9.252564E+03 -7.623156E+03 -1.158729E+04 2.277696E+04 1.240007E+04 -8.393699E+04 1.229129E+05 -8.481109E+04 + 1.297686E+04 2.214658E+04 -1.191263E+04 -6.516744E+03 9.180787E+03 -8.269711E+02 -5.147144E+03 4.400573E+03 -6.899872E+02 -1.703350E+03 + 1.753289E+03 -6.290790E+02 -5.603335E+02 8.397937E+02 -3.139916E+02 -2.867464E+02 5.033267E+02 -4.553723E+02 1.830018E+02 2.051779E+02 + -3.373525E+02 2.263375E+02 -2.195688E+01 -2.400420E+02 2.339393E+02 5.138226E+00 -1.227588E+02 1.125893E+02 -7.612111E+01 -1.692468E+01 + 2.498590E+02 -1.234331E+02 -2.281537E+02 2.873401E+02 3.127279E+02 -1.092127E+02 -3.711948E+02 -2.821282E+02 -9.082177E+01 -3.192299E+01 + -2.882759E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 30 + -1.493735E+02 1.608136E+01 1.025769E+02 1.901406E+02 6.294632E+01 -4.131187E+02 -9.076024E+01 6.866868E+02 -2.307320E+02 -8.571359E+02 + 9.118614E+02 3.808343E+02 -1.429828E+03 1.171962E+03 1.930970E+02 -1.778304E+03 2.249839E+03 -2.230483E+02 -3.214310E+03 3.278868E+03 + 2.056206E+03 -6.105768E+03 9.290179E+02 9.061382E+03 -7.330016E+03 -1.186510E+04 2.210861E+04 1.351644E+04 -8.481109E+04 1.232200E+05 + -8.487493E+04 1.472714E+04 2.094247E+04 -1.336520E+04 -5.280822E+03 1.022953E+04 -2.804203E+03 -4.997368E+03 6.449016E+03 -2.790852E+03 + -8.240590E+02 1.673390E+03 -6.664444E+02 -6.928338E+02 1.285711E+03 -1.187754E+03 6.681512E+02 -1.366686E+02 -1.292862E+01 8.525474E+01 + -6.568830E+01 -8.273750E+01 1.756052E+02 -3.677490E+01 -8.055384E+01 1.084451E+02 -8.262098E+00 -9.771287E+01 1.921834E+02 -6.843778E+01 + -3.245347E+02 3.006144E+02 3.176971E+02 -3.547501E+02 -2.789965E+02 1.589000E+02 1.803764E+02 -2.013612E+01 -8.519335E+01 -4.192111E+01 + -1.793713E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 31 + 7.136093E+02 -1.167873E+02 -7.378739E+02 -4.820572E+02 6.007271E+02 5.611577E+02 -7.218848E+02 -3.222458E+02 9.441842E+02 -2.188885E+02 + -8.656181E+02 1.038549E+03 -2.017184E+02 -9.436012E+02 1.545338E+03 -7.073629E+02 -1.322683E+03 2.584096E+03 -6.783179E+02 -3.169134E+03 + 3.358993E+03 2.187617E+03 -6.388994E+03 1.037425E+03 9.208098E+03 -7.914977E+03 -1.123513E+04 2.225069E+04 1.297686E+04 -8.487493E+04 + 1.222612E+05 -8.378743E+04 1.461583E+04 2.112358E+04 -1.314977E+04 -5.285592E+03 1.000076E+04 -3.099014E+03 -3.775243E+03 4.657387E+03 + -1.895105E+03 -4.433844E+02 1.334263E+03 -6.658050E+02 -5.426498E+02 1.307602E+03 -1.106379E+03 4.890112E+02 -6.457687E+01 -2.923582E+02 + 3.536504E+02 -1.506114E+02 -1.237084E+02 3.197401E+02 -2.155033E+02 -1.150003E+02 2.138808E+02 -1.135512E+02 -1.988538E+01 1.419226E+02 + -1.580945E+02 -5.448810E+01 1.658875E+02 -5.888644E+01 -1.205668E+02 6.139854E+01 1.716368E+02 1.027041E+02 1.501938E+01 1.273712E+01 + 1.912840E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 32 + -1.998958E+02 -1.008048E+01 3.179922E+02 1.241375E+02 -3.896400E+02 9.835020E+00 4.716181E+02 -3.947491E+02 -3.320797E+02 8.188596E+02 + -1.979358E+02 -8.905187E+02 1.150689E+03 -3.196022E+02 -9.838219E+02 1.661566E+03 -8.889960E+02 -1.287199E+03 2.684687E+03 -5.304919E+02 + -3.202565E+03 2.846854E+03 2.540412E+03 -5.946810E+03 2.969718E+02 9.556388E+03 -7.151785E+03 -1.249324E+04 2.214658E+04 1.472714E+04 + -8.378743E+04 1.192180E+05 -8.368926E+04 1.673352E+04 1.973680E+04 -1.470653E+04 -2.562148E+03 1.011360E+04 -6.672128E+03 2.645705E+02 + 2.707043E+03 -1.843472E+03 -3.392254E+02 1.577586E+03 -1.290299E+03 4.559246E+02 5.816040E+01 -1.830214E+02 5.152282E+00 1.922946E+02 + -2.220685E+02 1.649018E+02 -1.719328E+01 -2.500039E+02 2.368987E+02 6.110155E+01 -2.015191E+02 1.777651E+02 -1.432272E+02 -7.935049E+01 + 4.363822E+02 -1.971188E+02 -5.480867E+02 2.100467E+02 4.029724E+02 -3.419851E+00 -1.918562E+02 -9.791974E+01 -1.185572E+01 -1.263620E+00 + -1.936283E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 33 + -4.551181E+02 7.597440E+01 3.945863E+02 3.451561E+02 -2.281920E+02 -4.925946E+02 2.699502E+02 5.511773E+02 -5.523192E+02 -3.530326E+02 + 8.663358E+02 -3.579773E+02 -5.326960E+02 1.025153E+03 -7.055082E+02 -4.459798E+02 1.610696E+03 -1.282976E+03 -1.010780E+03 2.721706E+03 + -7.770874E+02 -3.083950E+03 3.167818E+03 2.158454E+03 -5.963105E+03 7.507699E+02 8.898331E+03 -6.849538E+03 -1.191263E+04 2.094247E+04 + 1.461583E+04 -8.368926E+04 1.212268E+05 -8.517572E+04 1.674197E+04 2.050576E+04 -1.591646E+04 -2.217076E+03 1.079962E+04 -7.064412E+03 + 3.647146E+02 2.392630E+03 -1.739000E+03 -3.636810E+02 1.867711E+03 -2.171080E+03 1.364047E+03 -3.444898E+02 -8.831308E+01 2.795254E+02 + -1.919108E+02 -9.103660E+01 2.682659E+02 -2.161687E+02 1.070705E+01 2.063311E+02 -2.151926E+02 -6.419634E+00 2.221800E+02 -1.678819E+02 + -1.447760E+02 1.786080E+02 1.344398E+02 -4.290456E+01 -8.871714E+01 -1.981783E+00 8.960364E+01 1.211368E+02 7.586783E+01 1.804048E+01 + 4.152298E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 34 + 3.923821E+02 3.034877E+01 -5.447269E+02 -3.500749E+02 5.203199E+02 3.653575E+02 -6.247469E+02 -4.288631E+01 6.452833E+02 -4.258702E+02 + -4.292652E+02 9.288821E+02 -4.917930E+02 -4.490509E+02 1.157851E+03 -9.259849E+02 -3.532150E+02 1.794284E+03 -1.394400E+03 -1.352831E+03 + 2.826744E+03 -6.414536E+01 -3.661279E+03 2.676031E+03 3.097210E+03 -6.271580E+03 -4.765080E+01 9.871169E+03 -6.516744E+03 -1.336520E+04 + 2.112358E+04 1.673352E+04 -8.517572E+04 1.181884E+05 -8.185116E+04 1.822732E+04 1.724116E+04 -1.607779E+04 2.120296E+03 5.614104E+03 + -4.399250E+03 4.850789E+02 2.159366E+03 -2.030906E+03 3.272736E+02 1.068485E+03 -1.112850E+03 4.571941E+02 1.414363E+02 -5.887445E+02 + 4.872404E+02 -3.332425E+01 -3.354847E+02 5.063124E+02 -2.216135E+02 -3.286254E+02 4.469827E+02 -1.501364E+02 -1.366806E+02 2.459837E+02 + -2.168068E+02 1.950706E+01 4.470608E+02 -1.445900E+02 -4.583665E+02 -7.818160E+01 2.963928E+02 3.033658E+02 1.553366E+02 6.663482E+01 + 4.020247E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 35 + 4.913225E+01 -3.728478E+01 7.178412E+01 -2.554667E+01 -1.922999E+02 1.378548E+02 2.115918E+02 -4.061189E+02 -7.735602E+00 5.452219E+02 + -3.891734E+02 -2.522202E+02 6.773418E+02 -5.757078E+02 -6.152635E+01 9.055955E+02 -1.137629E+03 -1.424172E+01 1.671587E+03 -1.408456E+03 + -1.034625E+03 2.382756E+03 -2.826640E+02 -2.943974E+03 2.409142E+03 2.632405E+03 -5.463151E+03 -4.209926E+02 9.180787E+03 -5.280822E+03 + -1.314977E+04 1.973680E+04 1.674197E+04 -8.185116E+04 1.158442E+05 -8.464081E+04 2.277678E+04 1.642744E+04 -1.983509E+04 6.163568E+03 + 3.881986E+03 -4.559450E+03 8.884201E+02 2.366778E+03 -3.156703E+03 2.214722E+03 -7.987836E+02 -1.828829E+02 2.075575E+02 1.572116E+02 + -3.173768E+02 2.489858E+02 -9.442027E+01 -1.489337E+02 2.214001E+02 -8.077241E+01 -3.816186E+01 1.703369E+02 -2.342472E+02 -2.469169E+01 + 3.057592E+02 -1.300230E+02 -3.149048E+02 8.577973E+01 1.941641E+02 -7.815666E+01 -2.667749E+02 -2.326153E+02 -1.023513E+02 -2.540326E+01 + -1.390908E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 36 + -3.722507E+02 -5.469158E+01 4.799088E+02 3.927796E+02 -3.977704E+02 -5.195087E+02 5.081101E+02 4.099399E+02 -6.160554E+02 -8.161204E+01 + 7.011432E+02 -6.301007E+02 -1.347782E+02 9.010278E+02 -9.462565E+02 1.828712E+01 1.233653E+03 -1.499400E+03 -1.186894E+02 2.152837E+03 + -1.485601E+03 -1.708243E+03 3.020072E+03 4.329617E+01 -3.878885E+03 2.767070E+03 3.330480E+03 -6.198319E+03 -8.269711E+02 1.022953E+04 + -5.285592E+03 -1.470653E+04 2.050576E+04 1.822732E+04 -8.464081E+04 1.169946E+05 -8.273792E+04 2.271208E+04 1.313765E+04 -1.538525E+04 + 3.427424E+03 3.833399E+03 -4.335654E+03 9.367522E+02 2.137330E+03 -2.956973E+03 1.718880E+03 -1.735316E+02 -4.589506E+02 5.824817E+02 + -2.907133E+02 -1.952574E+02 5.483855E+02 -4.760807E+02 -1.149697E+00 4.783510E+02 -4.891441E+02 -1.148899E+01 4.362380E+02 -2.005315E+02 + -1.387715E+02 4.307714E+01 -1.442734E+02 1.248695E+02 3.890208E+02 2.048461E+02 -1.229867E+02 -1.952359E+02 -1.069260E+02 -5.449855E+01 + -4.144577E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 37 + 3.329840E+02 2.124886E+01 -4.887464E+02 -3.073183E+02 5.457431E+02 3.440220E+02 -6.354194E+02 -8.471470E+01 5.054940E+02 -2.853432E+02 + -2.323340E+02 6.201476E+02 -4.102109E+02 -1.631700E+02 6.571910E+02 -7.106704E+02 1.032387E+02 1.025518E+03 -1.327974E+03 -3.159714E+02 + 1.880756E+03 -6.745034E+02 -1.884412E+03 2.108284E+03 9.389144E+02 -3.669544E+03 1.476060E+03 4.193891E+03 -5.147144E+03 -2.804203E+03 + 1.000076E+04 -2.562148E+03 -1.591646E+04 1.724116E+04 2.277678E+04 -8.273792E+04 1.063047E+05 -7.789901E+04 2.894136E+04 5.024008E+03 + -1.137838E+04 4.567414E+03 2.443718E+03 -4.552024E+03 2.985678E+03 -8.420268E+02 -2.324418E+02 4.145778E+02 1.978190E+01 -6.342432E+02 + 7.055050E+02 -2.882947E+02 -2.443430E+02 5.863176E+02 -3.444109E+02 -1.840606E+02 4.285823E+02 -2.908426E+02 -4.964548E+01 2.366267E+02 + -2.238096E+02 3.493164E+01 3.160746E+02 -1.831228E+02 -3.928692E+02 -1.257560E+02 9.419314E+01 1.064746E+02 3.585167E+01 4.125528E+00 + 5.789888E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 38 + 5.408657E+01 6.278936E+01 -8.114942E+01 -1.539550E+02 3.869231E-01 3.026852E+02 -7.919876E+01 -3.973211E+02 2.740476E+02 3.914317E+02 + -6.197984E+02 1.251893E+02 5.728451E+02 -8.982930E+02 4.048531E+02 7.775249E+02 -1.558056E+03 5.664752E+02 1.538306E+03 -1.838446E+03 + -4.900982E+02 2.290493E+03 -9.646766E+02 -2.013582E+03 2.534526E+03 8.532065E+02 -3.866209E+03 1.415575E+03 4.400573E+03 -4.997368E+03 + -3.099014E+03 1.011360E+04 -2.217076E+03 -1.607779E+04 1.642744E+04 2.271208E+04 -7.789901E+04 1.062653E+05 -8.419584E+04 3.049386E+04 + 6.495112E+03 -1.295969E+04 5.453168E+03 3.044804E+03 -6.241718E+03 4.819261E+03 -1.696858E+03 -6.191575E+02 8.508142E+02 -1.435646E+02 + -4.326856E+02 6.617640E+02 -5.412648E+02 -4.365578E+01 4.070105E+02 -4.172982E+02 1.987024E+02 2.947368E+02 -4.723075E+02 3.026648E+01 + 3.652417E+02 -9.314856E+01 -8.590632E+01 1.411158E+02 3.424414E+00 -1.458626E+02 -4.491263E+01 2.751872E+01 2.558788E+01 2.609249E+01 + 2.515792E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 39 + -3.473178E+02 -4.787347E+00 5.471048E+02 3.739230E+02 -6.537780E+02 -6.855842E+02 8.230816E+02 6.674311E+02 -8.902402E+02 -3.010382E+02 + 1.041909E+03 -7.211812E+02 -3.644086E+02 1.313070E+03 -1.104922E+03 -3.905010E+02 1.853178E+03 -1.506666E+03 -7.388288E+02 2.326002E+03 + -9.318673E+02 -2.023996E+03 2.614237E+03 6.055805E+02 -3.611525E+03 1.844028E+03 2.998219E+03 -4.388408E+03 -6.899872E+02 6.449016E+03 + -3.775243E+03 -6.672128E+03 1.079962E+04 2.120296E+03 -1.983509E+04 1.313765E+04 2.894136E+04 -8.419584E+04 1.073930E+05 -7.229257E+04 + 2.209892E+04 3.613105E+03 -9.835918E+03 5.067133E+03 6.708323E+02 -2.501582E+03 1.200211E+03 3.453586E+02 -7.546907E+02 7.267228E+02 + -5.119855E+02 -4.368337E+00 5.496911E+02 -5.668271E+02 2.472258E+02 2.851380E+02 -6.044320E+02 2.395520E+02 2.488744E+02 -2.608036E+02 + 1.026715E+02 -8.618742E+01 -3.985081E+02 1.105188E+02 4.140511E+02 2.474870E+02 3.873463E+01 -1.734288E+01 -2.590702E+00 -5.115937E+00 + -1.347403E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 40 + 3.867746E+02 -1.128354E+02 -5.959489E+02 -2.944246E+02 8.385802E+02 5.184104E+02 -8.649930E+02 -4.409506E+02 8.111606E+02 -3.129233E+01 + -6.807398E+02 7.429222E+02 -3.158836E+01 -8.404262E+02 9.834448E+02 -8.748682E+01 -9.938564E+02 1.136832E+03 -1.159227E+02 -1.157085E+03 + 1.162623E+03 6.008923E+02 -1.989442E+03 5.752592E+02 2.038378E+03 -2.129719E+03 -6.715091E+02 3.058636E+03 -1.703350E+03 -2.790852E+03 + 4.657387E+03 2.645705E+02 -7.064412E+03 5.614104E+03 6.163568E+03 -1.538525E+04 5.024008E+03 3.049386E+04 -7.229257E+04 8.008981E+04 + -5.730538E+04 2.906437E+04 -2.697151E+03 -1.068044E+04 1.126343E+04 -6.782786E+03 2.146636E+03 1.060670E+03 -1.434078E+03 4.335911E+01 + 1.381525E+03 -1.685381E+03 7.652824E+02 5.819232E+02 -1.216608E+03 7.343483E+02 2.028458E+02 -7.958876E+02 6.765184E+02 1.321802E+01 + -7.218146E+02 6.316678E+02 5.967647E+02 -6.345978E+02 -5.094504E+02 1.249876E+02 1.035055E+02 -1.067810E+02 -1.554268E+02 -9.318743E+01 + -5.071019E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 41 + -3.224303E+02 1.348386E+02 4.383946E+02 2.054795E+02 -6.197527E+02 -1.982064E+02 4.955342E+02 9.574261E+00 -4.121648E+02 3.665593E+02 + 1.115780E+02 -5.032441E+02 3.795056E+02 1.403192E+02 -5.570264E+02 4.010905E+02 8.132044E+01 -3.970802E+02 4.592028E+02 -9.211150E+01 + -5.669611E+02 4.913630E+02 4.454645E+02 -8.115654E+02 5.606016E+01 8.243970E+02 -8.945873E+02 -3.590008E+02 1.753289E+03 -8.240590E+02 + -1.895105E+03 2.707043E+03 3.647146E+02 -4.399250E+03 3.881986E+03 3.427424E+03 -1.137838E+04 6.495112E+03 2.209892E+04 -5.730538E+04 + 7.938301E+04 -7.378340E+04 3.767270E+04 6.529549E+02 -1.894574E+04 1.830497E+04 -7.931062E+03 -2.276729E+03 4.833694E+03 -2.549844E+03 + -1.012972E+03 3.376536E+03 -2.902128E+03 2.975957E+02 1.799326E+03 -2.065595E+03 8.698726E+02 9.354317E+02 -1.695503E+03 6.902574E+02 + 9.769195E+02 -1.401296E+03 -2.454658E+02 1.227961E+03 1.546903E+02 -7.268574E+02 -1.133140E+02 5.162419E+02 5.233508E+02 2.834566E+02 + 1.652795E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 42 + 8.473489E+01 -4.711734E+01 -7.010810E+01 -5.739072E+01 1.055519E+02 -5.803625E+01 -3.342076E+01 2.017664E+02 3.366930E+01 -3.280306E+02 + 1.754348E+02 1.730106E+02 -3.763394E+02 1.703610E+02 2.417528E+02 -3.874300E+02 2.357417E+02 2.744447E+00 -3.217966E+02 4.565886E+02 + 1.436597E+01 -6.208092E+02 4.311119E+02 3.838589E+02 -8.495081E+02 2.968211E+02 9.356660E+02 -1.007529E+03 -6.290790E+02 1.673390E+03 + -4.433844E+02 -1.843472E+03 2.392630E+03 4.850789E+02 -4.559450E+03 3.833399E+03 4.567414E+03 -1.295969E+04 3.613105E+03 2.906437E+04 + -7.378340E+04 9.963311E+04 -8.016568E+04 3.250775E+04 7.032190E+03 -2.167026E+04 1.405522E+04 -5.428631E+00 -5.911831E+03 5.434374E+03 + -1.331588E+03 -3.182953E+03 4.256838E+03 -1.802872E+03 -1.251385E+03 2.703689E+03 -1.944607E+03 -4.755947E+02 2.052629E+03 -1.415490E+03 + -7.006297E+02 1.932706E+03 -3.242490E+02 -1.430890E+03 3.387817E+02 9.305417E+02 -1.338067E+02 -8.541638E+02 -7.150331E+02 -3.528832E+02 + -1.983301E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 43 + 1.346381E+02 1.757437E+00 -2.491424E+02 -1.784621E+02 3.286988E+02 2.665601E+02 -2.721338E+02 -2.272322E+02 2.220595E+02 7.921622E+01 + -2.484704E+02 1.415316E+02 1.373264E+02 -2.071350E+02 5.748360E+01 7.379044E+01 -1.604330E+02 1.849567E+02 -2.639826E+01 -2.500314E+02 + 2.522156E+02 1.785587E+02 -4.686642E+02 1.715783E+02 4.657571E+02 -6.592188E+02 -1.215328E+02 9.308562E+02 -5.603335E+02 -6.664444E+02 + 1.334263E+03 -3.392254E+02 -1.739000E+03 2.159366E+03 8.884201E+02 -4.335654E+03 2.443718E+03 5.453168E+03 -9.835918E+03 -2.697151E+03 + 3.767270E+04 -8.016568E+04 9.919157E+04 -7.754311E+04 3.203635E+04 4.126135E+03 -1.324540E+04 6.330860E+03 1.104704E+03 -5.451505E+03 + 4.399550E+03 -6.094722E+00 -2.942886E+03 2.862946E+03 -5.866572E+02 -1.705381E+03 2.097102E+03 -5.019760E+02 -1.131400E+03 1.456890E+03 + -8.070577E+01 -1.425222E+03 6.549809E+02 7.836602E+02 -6.421681E+02 -6.218988E+02 3.343526E+02 7.749947E+02 5.448767E+02 2.246060E+02 + 1.067067E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 44 + -2.202306E+02 6.105034E+01 2.602264E+02 2.270533E+02 -3.207562E+02 -2.213418E+02 2.066248E+02 6.763980E+01 -1.921284E+02 1.516877E+02 + 7.195728E+01 -2.241918E+02 1.718867E+02 4.444427E+01 -2.647806E+02 2.774720E+02 -4.540502E+01 -1.687043E+02 2.414000E+02 -1.402254E+02 + -1.411705E+02 2.904036E+02 -4.502549E+01 -3.592415E+02 3.087280E+02 2.589134E+02 -5.775850E+02 3.116098E+01 8.397937E+02 -6.928338E+02 + -6.658050E+02 1.577586E+03 -3.636810E+02 -2.030906E+03 2.366778E+03 9.367522E+02 -4.552024E+03 3.044804E+03 5.067133E+03 -1.068044E+04 + 6.529549E+02 3.250775E+04 -7.754311E+04 1.001698E+05 -7.817197E+04 3.513316E+04 -2.432438E+03 -1.163881E+04 8.756773E+03 -6.225542E+01 + -4.854962E+03 4.533739E+03 -1.281030E+03 -2.024321E+03 2.491411E+03 -7.303400E+02 -8.521727E+02 1.368529E+03 -7.062476E+02 -5.652038E+02 + 9.217837E+02 -8.428006E+01 -5.266008E+02 4.191912E+02 5.225358E+02 3.649364E+01 -1.938086E+02 -1.948276E+02 -8.607936E+01 6.500740E+00 + 2.897411E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 45 + 1.305043E+02 -9.952682E+01 -3.832775E+01 1.251353E+00 -9.566020E+00 -1.183683E+02 9.124800E+01 2.436472E+02 -8.922343E+01 -3.066623E+02 + 2.753198E+02 8.292874E+01 -4.069255E+02 2.490772E+02 2.330105E+02 -4.020580E+02 1.848815E+02 4.330122E+01 -2.557643E+02 3.445514E+02 + -8.337317E+00 -4.996276E+02 4.290962E+02 2.865857E+02 -7.347535E+02 2.587369E+02 7.178689E+02 -8.603734E+02 -3.139916E+02 1.285711E+03 + -5.426498E+02 -1.290299E+03 1.867711E+03 3.272736E+02 -3.156703E+03 2.137330E+03 2.985678E+03 -6.241718E+03 6.708323E+02 1.126343E+04 + -1.894574E+04 7.032190E+03 3.203635E+04 -7.817197E+04 9.496206E+04 -7.595641E+04 3.309998E+04 5.156989E+03 -1.418635E+04 8.369016E+03 + 1.476845E+02 -5.822225E+03 5.378666E+03 -1.014918E+03 -2.328257E+03 2.775745E+03 -1.251536E+03 -1.034822E+03 1.905217E+03 -8.423136E+02 + -9.274596E+02 1.437032E+03 -1.042693E+02 -1.263835E+03 7.086626E+00 5.731819E+02 -1.410336E+02 -6.353256E+02 -5.130605E+02 -2.494825E+02 + -1.396998E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 46 + 2.161488E+02 -5.592744E+01 -3.262794E+02 -2.882120E+02 4.665355E+02 4.249492E+02 -3.948746E+02 -4.070204E+02 3.770705E+02 2.091556E+02 + -4.385515E+02 2.082967E+02 2.739720E+02 -4.165655E+02 1.038914E+02 1.577053E+02 -2.750681E+02 2.211726E+02 1.120775E+02 -3.695360E+02 + 1.418549E+02 4.178878E+02 -5.110260E+02 -1.162451E+02 7.083122E+02 -5.239398E+02 -4.742840E+02 1.125246E+03 -2.867464E+02 -1.187754E+03 + 1.307602E+03 4.559246E+02 -2.171080E+03 1.068485E+03 2.214722E+03 -2.956973E+03 -8.420268E+02 4.819261E+03 -2.501582E+03 -6.782786E+03 + 1.830497E+04 -2.167026E+04 4.126135E+03 3.513316E+04 -7.595641E+04 9.814932E+04 -7.299794E+04 2.580217E+04 -4.333555E+02 -1.080242E+04 + 9.088770E+03 -1.120988E+03 -4.168710E+03 4.523365E+03 -1.373981E+03 -1.924249E+03 2.863217E+03 -1.198537E+03 -9.507247E+02 2.081684E+03 + -5.534428E+02 -1.624837E+03 1.207901E+03 1.103899E+03 -6.759762E+02 -6.947072E+02 4.609223E+02 1.066681E+03 7.704587E+02 3.003907E+02 + 1.267285E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 47 + -3.967530E+02 1.775605E+02 4.551379E+02 2.987052E+02 -5.486915E+02 -3.883416E+02 3.934758E+02 2.725531E+02 -3.402666E+02 1.804754E+01 + 2.184746E+02 -2.929162E+02 3.803254E+01 2.836460E+02 -3.983206E+02 1.856687E+02 3.366568E+02 -4.869800E+02 6.715507E+01 3.547477E+02 + -3.552703E+02 -1.325186E+02 4.903915E+02 -1.930240E+02 -4.075288E+02 5.892907E+02 5.651825E+01 -8.250127E+02 5.033267E+02 6.681512E+02 + -1.106379E+03 5.816040E+01 1.364047E+03 -1.112850E+03 -7.987836E+02 1.718880E+03 -2.324418E+02 -1.696858E+03 1.200211E+03 2.146636E+03 + -7.931062E+03 1.405522E+04 -1.324540E+04 -2.432438E+03 3.309998E+04 -7.299794E+04 8.285386E+04 -6.234887E+04 3.456835E+04 -2.968378E+03 + -1.236422E+04 1.221497E+04 -4.110528E+03 -4.013345E+03 5.784913E+03 -2.296903E+03 -1.738913E+03 3.451831E+03 -1.852955E+03 -1.697887E+03 + 2.360014E+03 1.469555E+02 -1.748249E+03 2.952257E+02 9.116999E+02 -2.009010E+02 -8.112037E+02 -6.933804E+02 -3.179604E+02 -6.067042E+01 + 7.823540E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 48 + 1.685430E+02 -1.170660E+02 -2.289353E+02 -5.038078E+01 2.915767E+02 1.251592E+02 -2.139878E+02 -8.069419E+01 1.782145E+02 -1.459904E+02 + 3.390743E+01 1.938667E+02 -2.322866E+02 -2.517871E+01 4.316254E+02 -3.701519E+02 -2.721116E+02 5.041604E+02 -1.752694E+02 -2.473516E+02 + 4.844173E+02 -1.421586E+02 -4.550005E+02 4.836970E+02 1.357153E+02 -6.037106E+02 2.668302E+02 4.128467E+02 -4.553723E+02 -1.366686E+02 + 4.890112E+02 -1.830214E+02 -3.444898E+02 4.571941E+02 -1.828829E+02 -1.735316E+02 4.145778E+02 -6.191575E+02 3.453586E+02 1.060670E+03 + -2.276729E+03 -5.428631E+00 6.330860E+03 -1.163881E+04 5.156989E+03 2.580217E+04 -6.234887E+04 8.706601E+04 -7.757130E+04 3.761191E+04 + -2.106858E+03 -1.784986E+04 1.557614E+04 -2.202332E+03 -7.352737E+03 7.691482E+03 -2.152441E+03 -3.986596E+03 4.916333E+03 -4.045246E+02 + -3.201022E+03 2.318339E+03 1.145324E+03 -2.263658E+03 -5.773174E+02 1.643308E+03 1.197351E+03 -9.054374E+00 -4.003546E+02 -2.305133E+02 + -1.165760E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 49 + 1.696556E+02 7.652785E-01 -1.345973E+02 -1.661220E+02 3.743966E+01 1.385765E+02 -6.341419E+00 -2.617910E+01 -3.145739E+01 3.910850E+01 + -3.316296E+01 -3.760270E+01 1.568009E+02 -1.082130E+02 -1.782754E+02 2.012026E+02 7.294835E+01 -1.027348E+02 8.864364E+01 -6.221867E+01 + -1.996055E+02 1.957982E+02 1.528702E+02 -2.887698E+02 1.380805E+01 2.849070E+02 -1.806575E+02 -1.214454E+02 1.830018E+02 -1.292862E+01 + -6.457687E+01 5.152282E+00 -8.831308E+01 1.414363E+02 2.075575E+02 -4.589506E+02 1.978190E+01 8.508142E+02 -7.546907E+02 -1.434078E+03 + 4.833694E+03 -5.911831E+03 1.104704E+03 8.756773E+03 -1.418635E+04 -4.333555E+02 3.456835E+04 -7.757130E+04 9.305435E+04 -7.329167E+04 + 3.465385E+04 5.387627E+03 -1.812757E+04 1.002687E+04 2.789779E+03 -8.565672E+03 5.566498E+03 1.517710E+03 -5.061380E+03 2.544180E+03 + 1.823356E+03 -3.477529E+03 3.437425E+02 2.727679E+03 -1.912691E+02 -1.847658E+03 -6.280330E+02 7.146786E+02 8.195531E+02 3.833059E+02 + 1.841581E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 50 + -3.163128E+02 1.098231E+02 3.526115E+02 1.747680E+02 -3.012540E+02 -2.130834E+02 1.781906E+02 8.684886E+01 -1.419882E+02 1.441068E+02 + 2.233996E-01 -2.153575E+02 1.107577E+02 1.587622E+02 -2.683343E+02 1.542431E+02 2.113516E+02 -4.136420E+02 3.861404E+01 3.844578E+02 + -2.382229E+02 -1.168807E+02 2.742899E+02 -1.681781E+02 -1.361209E+02 2.911175E+02 -8.349680E+01 -2.323262E+02 2.051779E+02 8.525474E+01 + -2.923582E+02 1.922946E+02 2.795254E+02 -5.887445E+02 1.572116E+02 5.824817E+02 -6.342432E+02 -1.435646E+02 7.267228E+02 4.335911E+01 + -2.549844E+03 5.434374E+03 -5.451505E+03 -6.225542E+01 8.369016E+03 -1.080242E+04 -2.968378E+03 3.761191E+04 -7.329167E+04 9.718318E+04 + -8.118241E+04 3.295000E+04 1.140592E+03 -1.419275E+04 8.579543E+03 1.899547E+03 -6.300859E+03 3.812939E+03 1.092611E+03 -3.591282E+03 + 1.666042E+03 2.263150E+03 -2.195246E+03 -1.239664E+03 1.206196E+03 8.338495E+02 -6.404282E+02 -1.188964E+03 -8.090082E+02 -3.329789E+02 + -1.572478E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 51 + 1.097007E+02 -5.749900E+01 -1.211192E+02 -7.421315E+01 1.622027E+02 6.186514E+01 -1.147472E+02 -1.887268E+01 1.561193E+02 -1.838135E+02 + -1.726007E+01 2.972998E+02 -2.414494E+02 -1.037564E+02 4.113896E+02 -2.768237E+02 -2.534093E+02 4.123351E+02 -7.269882E+01 -2.473510E+02 + 2.712016E+02 -1.887983E+01 -2.870861E+02 2.774353E+02 1.086331E+02 -4.002850E+02 1.832521E+02 2.874630E+02 -3.373525E+02 -6.568830E+01 + 3.536504E+02 -2.220685E+02 -1.919108E+02 4.872404E+02 -3.173768E+02 -2.907133E+02 7.055050E+02 -4.326856E+02 -5.119855E+02 1.381525E+03 + -1.012972E+03 -1.331588E+03 4.399550E+03 -4.854962E+03 1.476845E+02 9.088770E+03 -1.236422E+04 -2.106858E+03 3.465385E+04 -8.118241E+04 + 9.814472E+04 -7.240685E+04 3.337937E+04 3.039517E+03 -1.577598E+04 9.119088E+03 1.216073E+03 -6.639310E+03 4.414544E+03 1.531544E+03 + -4.018818E+03 9.488488E+02 2.267978E+03 -1.175896E+03 -1.327382E+03 4.804552E+02 1.050336E+03 5.445574E+02 8.742034E+01 -1.683048E+01 + -1.936988E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 52 + 1.627595E+02 -5.376824E+01 -2.310690E+02 -7.059689E+01 1.678608E+02 1.570628E+02 -8.839993E+01 -6.640318E+01 1.110916E+01 1.651674E+01 + 2.337230E+01 -9.551246E+01 1.309313E+02 -4.489942E+01 -1.438540E+02 1.470953E+02 -2.964481E+01 1.058239E+01 1.406494E+02 -2.073629E+02 + -1.000420E+02 2.265732E+02 2.755282E+01 -1.527896E+02 2.992582E+01 1.102851E+02 -1.346450E+02 -4.220410E+01 2.263375E+02 -8.273750E+01 + -1.506114E+02 1.649018E+02 -9.103660E+01 -3.332425E+01 2.489858E+02 -1.952574E+02 -2.882947E+02 6.617640E+02 -4.368337E+00 -1.685381E+03 + 3.376536E+03 -3.182953E+03 -6.094722E+00 4.533739E+03 -5.822225E+03 -1.120988E+03 1.221497E+04 -1.784986E+04 5.387627E+03 3.295000E+04 + -7.240685E+04 9.098804E+04 -7.563124E+04 3.149069E+04 5.432060E+03 -1.750846E+04 9.904477E+03 2.986846E+03 -8.067245E+03 3.507536E+03 + 3.100322E+03 -4.552136E+03 -3.360372E+01 3.277356E+03 2.124506E+02 -1.677374E+03 -5.544109E+02 7.397852E+02 8.396738E+02 4.064599E+02 + 2.011063E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 53 + -1.830732E+02 1.136471E+02 2.076484E+02 1.653011E+02 -2.998387E+02 -2.347889E+02 2.008892E+02 1.445726E+02 -2.035789E+02 8.946634E+01 + 1.197468E+02 -2.282252E+02 3.553518E+01 2.137679E+02 -2.290217E+02 4.253478E+01 2.712152E+02 -3.282354E+02 -1.283131E+02 4.209082E+02 + -5.935550E+01 -2.966768E+02 1.887124E+02 4.760960E+01 -1.981208E+02 1.610320E+02 1.004857E+02 -2.102153E+02 -2.195688E+01 1.756052E+02 + -1.237084E+02 -1.719328E+01 2.682659E+02 -3.354847E+02 -9.442027E+01 5.483855E+02 -2.443430E+02 -5.412648E+02 5.496911E+02 7.652824E+02 + -2.902128E+03 4.256838E+03 -2.942886E+03 -1.281030E+03 5.378666E+03 -4.168710E+03 -4.110528E+03 1.557614E+04 -1.812757E+04 1.140592E+03 + 3.337937E+04 -7.563124E+04 9.798338E+04 -7.802256E+04 3.045878E+04 8.358104E+03 -1.761517E+04 7.085210E+03 4.665274E+03 -7.458040E+03 + 1.560583E+03 4.999854E+03 -3.101819E+03 -2.992024E+03 1.683844E+03 1.999007E+03 -5.576437E+02 -1.842907E+03 -1.343630E+03 -5.549286E+02 + -2.535251E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 54 + 6.984162E+01 -5.564962E+01 -5.657117E+01 -8.592142E+01 1.629151E+02 8.313527E+01 -1.091779E+02 -1.286184E+02 1.791664E+02 -3.491092E+01 + -2.003555E+02 3.547308E+02 -1.374853E+02 -2.407027E+02 4.039069E+02 -1.556474E+02 -2.814773E+02 3.302786E+02 -1.436966E+01 -2.023218E+02 + 1.478806E+02 4.980791E+01 -1.720620E+02 6.778159E+01 1.466251E+02 -2.218728E+02 4.094381E+01 2.242262E+02 -2.400420E+02 -3.677490E+01 + 3.197401E+02 -2.500039E+02 -2.161687E+02 5.063124E+02 -1.489337E+02 -4.760807E+02 5.863176E+02 -4.365578E+01 -5.668271E+02 5.819232E+02 + 2.975957E+02 -1.802872E+03 2.862946E+03 -2.024321E+03 -1.014918E+03 4.523365E+03 -4.013345E+03 -2.202332E+03 1.002687E+04 -1.419275E+04 + 3.039517E+03 3.149069E+04 -7.802256E+04 1.043893E+05 -7.969924E+04 2.933353E+04 5.283946E+03 -1.567745E+04 7.371194E+03 4.762091E+03 + -7.073033E+03 -1.799208E+02 4.384351E+03 -5.408773E+02 -2.689266E+03 -4.552445E+02 1.646039E+03 1.776085E+03 9.517212E+02 3.501172E+02 + 1.626668E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 55 + 5.971675E+01 -6.081571E+01 -6.663230E+01 -9.059046E+01 1.491523E+02 1.434341E+02 -1.174847E+02 -9.035713E+01 1.043166E+02 2.547012E+01 + -4.954411E+01 -7.473294E+01 9.262994E+01 2.094268E+01 -1.405237E+02 9.925860E+01 2.022620E+01 -3.675528E+01 9.754774E+01 -1.308167E+02 + -9.438253E+01 2.017004E+02 5.006400E+01 -1.582386E+02 1.285548E+01 9.957100E+01 -1.181729E+02 -4.857517E+01 2.339393E+02 -8.055384E+01 + -2.155033E+02 2.368987E+02 1.070705E+01 -2.216135E+02 2.214001E+02 -1.149697E+00 -3.444109E+02 4.070105E+02 2.472258E+02 -1.216608E+03 + 1.799326E+03 -1.251385E+03 -5.866572E+02 2.491411E+03 -2.328257E+03 -1.373981E+03 5.784913E+03 -7.352737E+03 2.789779E+03 8.579543E+03 + -1.577598E+04 5.432060E+03 3.045878E+04 -7.969924E+04 9.984185E+04 -7.653126E+04 3.227783E+04 6.745481E+03 -1.756912E+04 5.962848E+03 + 6.532259E+03 -6.852607E+03 -1.121288E+03 4.616896E+03 7.470761E+02 -2.176145E+03 -8.285324E+02 8.562583E+02 9.849594E+02 4.571945E+02 + 2.086747E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 56 + -1.123374E+02 1.042591E+02 9.787025E+01 8.744352E+01 -2.493254E+02 -9.672595E+01 1.797278E+02 1.849494E+02 -2.555868E+02 -1.331669E+02 + 3.580890E+02 -1.799536E+02 -9.070703E+01 2.587344E+02 -2.339039E+02 -3.928460E+01 3.251180E+02 -2.048802E+02 -2.003398E+02 3.041179E+02 + 4.493056E+01 -2.845664E+02 3.038993E+01 1.889150E+02 -1.266756E+02 5.194016E+01 8.865173E+01 -1.665331E+02 5.138226E+00 1.084451E+02 + -1.150003E+02 6.110155E+01 2.063311E+02 -3.286254E+02 -8.077241E+01 4.783510E+02 -1.840606E+02 -4.172982E+02 2.851380E+02 7.343483E+02 + -2.065595E+03 2.703689E+03 -1.705381E+03 -7.303400E+02 2.775745E+03 -1.924249E+03 -2.296903E+03 7.691482E+03 -8.565672E+03 1.899547E+03 + 9.119088E+03 -1.750846E+04 8.358104E+03 2.933353E+04 -7.653126E+04 1.003254E+05 -7.830424E+04 2.807190E+04 8.873378E+03 -1.682273E+04 + 3.178517E+03 9.070926E+03 -4.713502E+03 -4.809119E+03 2.504148E+03 3.116640E+03 -9.344106E+02 -3.039379E+03 -2.255524E+03 -9.576056E+02 + -4.530701E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 57 + 4.403826E+01 -8.582102E+01 -7.390061E+01 9.903547E+01 1.219306E+02 -3.465348E+01 -1.163098E+02 -5.578498E+01 1.870222E+02 2.779572E+01 + -2.960009E+02 2.312309E+02 4.350318E+01 -2.821466E+02 3.160848E+02 1.083765E+01 -3.566159E+02 1.902169E+02 1.611702E+02 -1.447783E+02 + -4.270738E+01 8.715709E+01 -2.267232E+01 -3.780277E+01 5.221886E+01 -6.886915E+01 6.122630E+00 1.168194E+02 -1.227588E+02 -8.262098E+00 + 2.138808E+02 -2.015191E+02 -2.151926E+02 4.469827E+02 -3.816186E+01 -4.891441E+02 4.285823E+02 1.987024E+02 -6.044320E+02 2.028458E+02 + 8.698726E+02 -1.944607E+03 2.097102E+03 -8.521727E+02 -1.251536E+03 2.863217E+03 -1.738913E+03 -2.152441E+03 5.566498E+03 -6.300859E+03 + 1.216073E+03 9.904477E+03 -1.761517E+04 5.283946E+03 3.227783E+04 -7.830424E+04 9.968264E+04 -7.690846E+04 2.775415E+04 1.233409E+04 + -1.655888E+04 -1.245120E+03 8.509469E+03 -5.130770E+02 -4.667924E+03 -9.852484E+02 2.601317E+03 3.014100E+03 1.757195E+03 7.194266E+02 + 3.734216E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 58 + 1.837658E+02 -1.182432E+02 -2.979767E+01 -4.656407E+01 1.788923E+01 -6.704561E+01 3.959748E+01 -1.957831E+01 -4.979820E+00 1.733590E+02 + -1.116289E+02 -1.318254E+02 1.561250E+02 3.305486E+01 -1.892557E+02 9.251173E+01 1.047153E+02 -1.035989E+02 2.346188E+01 -1.041428E+01 + -6.360098E+01 1.174167E+02 7.921620E+01 -1.536330E+02 -9.120870E-01 5.140927E+01 -6.955904E+01 3.316546E+01 1.125893E+02 -9.771287E+01 + -1.135512E+02 1.777651E+02 -6.419634E+00 -1.501364E+02 1.703369E+02 -1.148899E+01 -2.908426E+02 2.947368E+02 2.395520E+02 -7.958876E+02 + 9.354317E+02 -4.755947E+02 -5.019760E+02 1.368529E+03 -1.034822E+03 -1.198537E+03 3.451831E+03 -3.986596E+03 1.517710E+03 3.812939E+03 + -6.639310E+03 2.986846E+03 7.085210E+03 -1.567745E+04 6.745481E+03 2.807190E+04 -7.690846E+04 1.087098E+05 -8.301605E+04 2.166252E+04 + 1.624648E+04 -1.430344E+04 -3.947761E+03 7.802725E+03 2.221423E+03 -3.240980E+03 -1.725206E+03 9.533746E+02 1.396301E+03 6.921426E+02 + 3.219101E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 59 + -9.692803E+01 5.184067E+01 1.223651E+02 -3.044408E+01 -1.409875E+02 5.245552E+00 1.725230E+02 9.198874E+01 -2.815601E+02 -1.292408E+02 + 4.437569E+02 -1.731263E+02 -1.440334E+02 2.668312E+02 -2.405941E+02 -4.672422E+01 3.163256E+02 -1.821782E+02 -1.533968E+02 2.436160E+02 + 2.444590E+00 -2.136010E+02 5.091432E+01 1.380358E+02 -1.203322E+02 5.089980E+01 1.267051E+02 -1.722840E+02 -7.612111E+01 1.921834E+02 + -1.988538E+01 -1.432272E+02 2.221800E+02 -1.366806E+02 -2.342472E+02 4.362380E+02 -4.964548E+01 -4.723075E+02 2.488744E+02 6.765184E+02 + -1.695503E+03 2.052629E+03 -1.131400E+03 -7.062476E+02 1.905217E+03 -9.507247E+02 -1.852955E+03 4.916333E+03 -5.061380E+03 1.092611E+03 + 4.414544E+03 -8.067245E+03 4.665274E+03 7.371194E+03 -1.756912E+04 8.873378E+03 2.775415E+04 -8.301605E+04 1.108646E+05 -7.871250E+04 + 1.792302E+04 1.887898E+04 -1.102596E+04 -7.275901E+03 4.793064E+03 4.358273E+03 -1.843718E+03 -4.615982E+03 -3.354449E+03 -1.469802E+03 + -7.497116E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 60 + -9.176700E+01 3.899843E+01 -7.312985E+01 -5.876654E+00 2.145463E+02 1.782308E+02 -3.219742E+02 -8.431094E+01 3.628128E+02 -2.202496E+02 + -2.279708E+02 3.251488E+02 -3.285566E+01 -2.229295E+02 3.221960E+02 -4.904650E+01 -3.313750E+02 2.245308E+02 1.393098E+02 -2.153530E+02 + -3.669138E+01 2.029454E+02 -7.884136E+01 -9.028511E+01 8.888288E+01 -1.057203E+00 -5.804080E+01 7.154672E+01 -1.692468E+01 -6.843778E+01 + 1.419226E+02 -7.935049E+01 -1.678819E+02 2.459837E+02 -2.469169E+01 -2.005315E+02 2.366267E+02 3.026648E+01 -2.608036E+02 1.321802E+01 + 6.902574E+02 -1.415490E+03 1.456890E+03 -5.652038E+02 -8.423136E+02 2.081684E+03 -1.697887E+03 -4.045246E+02 2.544180E+03 -3.591282E+03 + 1.531544E+03 3.507536E+03 -7.458040E+03 4.762091E+03 5.962848E+03 -1.682273E+04 1.233409E+04 2.166252E+04 -7.871250E+04 1.208216E+05 + -8.531305E+04 9.633702E+03 2.058162E+04 -4.990325E+03 -9.401934E+03 -1.564426E+02 5.049810E+03 4.175152E+03 1.826137E+03 6.124684E+02 + 3.130326E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 61 + 2.825599E+02 -3.774692E+01 -2.205991E+02 -1.269080E+02 4.853360E+01 5.893764E+01 -7.228778E+01 -4.519169E+00 1.449344E+02 8.532648E+00 + -1.952586E+02 1.559303E+02 -1.865246E+01 -6.252343E+01 7.191488E+01 -9.508381E+01 6.178361E+01 1.107891E+02 -1.430981E+02 -9.238789E+01 + 1.762411E+02 1.151886E+02 -2.185732E+02 -5.790854E+01 2.436046E+02 -9.731338E+01 -2.549084E+02 2.144101E+02 2.498590E+02 -3.245347E+02 + -1.580945E+02 4.363822E+02 -1.447760E+02 -2.168068E+02 3.057592E+02 -1.387715E+02 -2.238096E+02 3.652417E+02 1.026715E+02 -7.218146E+02 + 9.769195E+02 -7.006297E+02 -8.070577E+01 9.217837E+02 -9.274596E+02 -5.534428E+02 2.360014E+03 -3.201022E+03 1.823356E+03 1.666042E+03 + -4.018818E+03 3.100322E+03 1.560583E+03 -7.073033E+03 6.532259E+03 3.178517E+03 -1.655888E+04 1.624648E+04 1.792302E+04 -8.531305E+04 + 1.277628E+05 -7.981537E+04 5.138101E+03 1.727243E+04 -8.246971E+02 -6.544372E+03 -1.035204E+03 3.281412E+03 3.087425E+03 1.458077E+03 + 7.424257E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 62 + 6.832057E+01 -7.017317E+01 -1.022978E+01 5.591402E+01 -5.950679E+01 -1.087481E+02 1.774435E+02 1.135655E+01 -2.877991E+02 2.553899E+02 + 2.491936E+02 -3.525649E+02 -3.080170E+01 2.314364E+02 -2.396615E+02 2.133460E+01 2.845732E+02 -2.350073E+02 -1.008900E+02 3.023495E+02 + 1.600385E+01 -3.552112E+02 8.320181E+01 2.184331E+02 -1.761578E+02 1.360861E+01 2.022149E+02 -2.176703E+02 -1.234331E+02 3.006144E+02 + -5.448810E+01 -1.971188E+02 1.786080E+02 1.950706E+01 -1.300230E+02 4.307714E+01 3.493164E+01 -9.314856E+01 -8.618742E+01 6.316678E+02 + -1.401296E+03 1.932706E+03 -1.425222E+03 -8.428006E+01 1.437032E+03 -1.624837E+03 1.469555E+02 2.318339E+03 -3.477529E+03 2.263150E+03 + 9.488488E+02 -4.552136E+03 4.999854E+03 -1.799208E+02 -6.852607E+03 9.070926E+03 -1.245120E+03 -1.430344E+04 1.887898E+04 9.633702E+03 + -7.981537E+04 1.375087E+05 -8.213952E+04 -5.361293E+03 1.894126E+04 5.944346E+03 -8.471947E+03 -1.065018E+04 -6.059424E+03 -2.335536E+03 + -1.167552E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 63 + -3.121316E+02 7.651552E+01 1.791844E+02 1.144408E+02 1.271154E+02 -2.004502E+01 -1.682542E+02 -2.221308E+02 1.636769E+02 2.514292E+02 + -1.467092E+02 -2.216252E+02 2.214866E+02 -8.467222E+01 5.998692E+01 9.102041E+01 -2.453362E+02 8.026424E+01 2.126274E+02 -1.895316E+02 + -9.858089E+01 9.802297E+01 1.530733E+02 -8.520632E+01 -2.051065E+02 1.439034E+02 2.161538E+02 -1.949068E+02 -2.281537E+02 3.176971E+02 + 1.658875E+02 -5.480867E+02 1.344398E+02 4.470608E+02 -3.149048E+02 -1.442734E+02 3.160746E+02 -8.590632E+01 -3.985081E+02 5.967647E+02 + -2.454658E+02 -3.242490E+02 6.549809E+02 -5.266008E+02 -1.042693E+02 1.207901E+03 -1.748249E+03 1.145324E+03 3.437425E+02 -2.195246E+03 + 2.267978E+03 -3.360372E+01 -3.101819E+03 4.384351E+03 -1.121288E+03 -4.713502E+03 8.509469E+03 -3.947761E+03 -1.102596E+04 2.058162E+04 + 5.138101E+03 -8.213952E+04 1.483638E+05 -7.399736E+04 -1.277214E+04 9.024241E+03 3.755855E+03 -3.158456E+03 -3.678239E+03 -1.767077E+03 + -8.976851E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 64 + -3.482466E+02 1.678742E+02 3.250348E+02 7.873014E+01 -1.396452E+02 -1.667520E+02 -4.464647E+01 6.991798E+01 2.398753E+02 -2.009252E+02 + -2.152790E+02 3.399599E+02 2.915770E+00 -3.110092E+02 3.596201E+02 -9.028037E+01 -2.607311E+02 2.967854E+02 -2.435346E+00 -3.756150E+02 + 1.654279E+02 3.427091E+02 -2.256697E+02 -2.042928E+02 3.387625E+02 -1.318951E+01 -3.980999E+02 2.685832E+02 2.873401E+02 -3.547501E+02 + -5.888644E+01 2.100467E+02 -4.290456E+01 -1.445900E+02 8.577973E+01 1.248695E+02 -1.831228E+02 1.411158E+02 1.105188E+02 -6.345978E+02 + 1.227961E+03 -1.430890E+03 7.836602E+02 4.191912E+02 -1.263835E+03 1.103899E+03 2.952257E+02 -2.263658E+03 2.727679E+03 -1.239664E+03 + -1.175896E+03 3.277356E+03 -2.992024E+03 -5.408773E+02 4.616896E+03 -4.809119E+03 -5.130770E+02 7.802725E+03 -7.275901E+03 -4.990325E+03 + 1.727243E+04 -5.361293E+03 -7.399736E+04 1.673732E+05 -7.643772E+04 -2.533153E+04 7.695376E+03 1.467859E+04 8.934764E+03 3.570306E+03 + 1.831967E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 65 + -1.189366E+01 -1.405782E+02 8.883018E+01 1.763447E+02 -1.181389E+02 -1.674098E+02 1.237111E+02 3.103952E+02 -5.913726E+01 -4.433929E+02 + 1.689497E+00 5.037845E+02 -1.789004E+02 -2.507040E+02 2.026655E+02 -2.903747E+01 2.278715E+01 -2.056679E+01 -9.210436E+01 7.890773E+01 + 9.326353E+01 -2.198538E+01 -2.511239E+02 3.517874E+01 3.747322E+02 -4.439375E+01 -4.316683E+02 1.766454E+02 3.127279E+02 -2.789965E+02 + -1.205668E+02 4.029724E+02 -8.871714E+01 -4.583665E+02 1.941641E+02 3.890208E+02 -3.928692E+02 3.424414E+00 4.140511E+02 -5.094504E+02 + 1.546903E+02 3.387817E+02 -6.421681E+02 5.225358E+02 7.086626E+00 -6.759762E+02 9.116999E+02 -5.773174E+02 -1.912691E+02 1.206196E+03 + -1.327382E+03 2.124506E+02 1.683844E+03 -2.689266E+03 7.470761E+02 2.504148E+03 -4.667924E+03 2.221423E+03 4.793064E+03 -9.401934E+03 + -8.246971E+02 1.894126E+04 -1.277214E+04 -7.643772E+04 1.757459E+05 -6.538894E+04 -2.020236E+04 4.140488E+03 6.997867E+03 3.488035E+03 + 1.926242E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 66 + 7.975562E+02 -4.553836E+02 -6.180040E+02 -9.896059E+01 1.977253E+02 2.692830E+02 5.695970E+01 3.116604E+01 -1.906675E+02 -1.019040E+02 + 3.736392E+01 1.121070E+02 4.635498E+01 -9.508181E+01 -2.125890E+02 2.249682E+02 1.679279E+02 -2.716495E+02 5.040508E+01 2.567009E+02 + -1.534187E+02 -1.011013E+02 -8.522603E+00 7.021015E+01 -5.019596E+01 7.403747E+01 3.342695E+01 -1.396625E+02 -1.092127E+02 1.589000E+02 + 6.139854E+01 -3.419851E+00 -1.981783E+00 -7.818160E+01 -7.815666E+01 2.048461E+02 -1.257560E+02 -1.458626E+02 2.474870E+02 1.249876E+02 + -7.268574E+02 9.305417E+02 -6.218988E+02 3.649364E+01 5.731819E+02 -6.947072E+02 -2.009010E+02 1.643308E+03 -1.847658E+03 8.338495E+02 + 4.804552E+02 -1.677374E+03 1.999007E+03 -4.552445E+02 -2.176145E+03 3.116640E+03 -9.852484E+02 -3.240980E+03 4.358273E+03 -1.564426E+02 + -6.544372E+03 5.944346E+03 9.024241E+03 -2.533153E+04 -6.538894E+04 1.974041E+05 -5.452397E+04 -2.612322E+04 -8.800882E+03 -2.479274E+03 + -9.571018E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 67 + 1.215194E+03 -4.420553E+02 -9.448507E+02 -3.988680E+02 2.602322E+02 4.941029E+02 9.289240E+00 -2.090335E+02 -1.730476E+02 2.400575E+02 + 4.662044E+01 -2.384219E+02 2.012353E+02 2.933176E+01 -3.274799E+02 2.723759E+02 2.741760E+01 -1.849937E+02 2.172934E+02 5.636020E+01 + -2.751188E+02 1.027853E+02 1.794331E+02 -6.226534E+00 -3.546144E+02 2.848009E+01 3.470970E+02 -1.000074E+02 -3.711948E+02 1.803764E+02 + 1.716368E+02 -1.918562E+02 8.960364E+01 2.963928E+02 -2.667749E+02 -1.229867E+02 9.419314E+01 -4.491263E+01 3.873463E+01 1.035055E+02 + -1.133140E+02 -1.338067E+02 3.343526E+02 -1.938086E+02 -1.410336E+02 4.609223E+02 -8.112037E+02 1.197351E+03 -6.280330E+02 -6.404282E+02 + 1.050336E+03 -5.544109E+02 -5.576437E+02 1.646039E+03 -8.285324E+02 -9.344106E+02 2.601317E+03 -1.725206E+03 -1.843718E+03 5.049810E+03 + -1.035204E+03 -8.471947E+03 3.755855E+03 7.695376E+03 -2.020236E+04 -5.452397E+04 2.126452E+05 -4.134630E+04 -1.914959E+04 -6.823954E+03 + -3.153475E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 68 + 8.883247E+02 -2.590621E+02 -6.611638E+02 -3.224262E+02 1.372004E+02 3.008910E+02 -2.060261E+01 -1.451556E+02 -6.247409E+01 2.328864E+02 + 1.415575E+01 -2.433058E+02 1.881268E+02 5.309958E+01 -1.953098E+02 1.461704E+02 -1.280640E+02 -1.697341E+01 2.462998E+02 -1.048673E+02 + -2.234126E+02 1.836852E+02 1.708727E+02 -8.511726E+00 -3.034046E+02 -8.737412E+01 2.911306E+02 8.600621E+01 -2.821282E+02 -2.013612E+01 + 1.027041E+02 -9.791974E+01 1.211368E+02 3.033658E+02 -2.326153E+02 -1.952359E+02 1.064746E+02 2.751872E+01 -1.734288E+01 -1.067810E+02 + 5.162419E+02 -8.541638E+02 7.749947E+02 -1.948276E+02 -6.353256E+02 1.066681E+03 -6.933804E+02 -9.054374E+00 7.146786E+02 -1.188964E+03 + 5.445574E+02 7.397852E+02 -1.842907E+03 1.776085E+03 8.562583E+02 -3.039379E+03 3.014100E+03 9.533746E+02 -4.615982E+03 4.175152E+03 + 3.281412E+03 -1.065018E+04 -3.158456E+03 1.467859E+04 4.140488E+03 -2.612322E+04 -4.134630E+04 2.403790E+05 -1.674715E+04 -6.267446E+03 + -2.976521E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 69 + 4.267888E+02 -9.233990E+01 -2.862728E+02 -1.591019E+02 8.225873E+00 7.537816E+01 -7.801421E+00 -8.986111E+00 9.283832E+00 8.834634E+01 + -2.018881E+01 -1.123165E+02 1.128441E+02 2.842916E+01 -7.930304E+01 4.231326E+01 -1.017250E+02 4.644068E+01 1.265142E+02 -1.134724E+02 + -9.177968E+01 1.299530E+02 6.672145E+01 9.030034E+00 -1.185198E+02 -1.064982E+02 1.016294E+02 1.259497E+02 -9.082177E+01 -8.519335E+01 + 1.501938E+01 -1.185572E+01 7.586783E+01 1.553366E+02 -1.023513E+02 -1.069260E+02 3.585167E+01 2.558788E+01 -2.590703E+00 -1.554268E+02 + 5.233508E+02 -7.150331E+02 5.448767E+02 -8.607936E+01 -5.130605E+02 7.704587E+02 -3.179604E+02 -4.003546E+02 8.195531E+02 -8.090082E+02 + 8.742034E+01 8.396738E+02 -1.343630E+03 9.517212E+02 9.849594E+02 -2.255524E+03 1.757195E+03 1.396301E+03 -3.354449E+03 1.826137E+03 + 3.087425E+03 -6.059424E+03 -3.678239E+03 8.934764E+03 6.997867E+03 -8.800882E+03 -1.914959E+04 -1.674715E+04 2.644138E+05 -3.412493E+03 + -1.638780E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 70 + 1.808699E+02 -1.523669E+01 -1.041395E+02 -8.258309E+01 -3.521460E+01 7.429135E+00 1.679603E+01 2.759834E+01 8.908430E+00 1.578966E+01 + -1.596886E+01 -3.843852E+01 4.880179E+01 1.276796E+01 -3.585500E+01 6.101621E+00 -2.691953E+01 3.630994E+01 3.446812E+01 -6.285847E+01 + -2.471162E+01 6.313784E+01 2.305104E+01 3.316750E+00 -4.357321E+01 -5.500522E+01 3.243763E+01 6.177182E+01 -3.192299E+01 -4.192111E+01 + 1.273712E+01 -1.263620E+00 1.804048E+01 6.663482E+01 -2.540326E+01 -5.449855E+01 4.125528E+00 2.609249E+01 -5.115937E+00 -9.318743E+01 + 2.834566E+02 -3.528832E+02 2.246060E+02 6.500740E+00 -2.494825E+02 3.003907E+02 -6.067042E+01 -2.305133E+02 3.833059E+02 -3.329789E+02 + -1.683048E+01 4.064599E+02 -5.549286E+02 3.501172E+02 4.571945E+02 -9.576056E+02 7.194266E+02 6.921426E+02 -1.469802E+03 6.124684E+02 + 1.458077E+03 -2.335536E+03 -1.767077E+03 3.570306E+03 3.488035E+03 -2.479274E+03 -6.823954E+03 -6.267446E+03 -3.412493E+03 2.720114E+05 + -6.367896E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 71 + 1.060492E+02 3.281344E+00 -5.372614E+01 -5.703163E+01 -3.551858E+01 -2.040558E+00 1.741721E+01 2.306890E+01 5.198264E+00 4.671918E+00 + -8.590055E+00 -2.137346E+01 2.278694E+01 9.156942E+00 -2.181845E+01 -2.156160E+00 -2.192004E+00 2.800669E+01 1.006342E+01 -4.008536E+01 + -9.510806E+00 3.940877E+01 1.491841E+01 -5.857100E+00 -3.089299E+01 -2.411873E+01 2.517410E+01 2.580050E+01 -2.882759E+01 -1.793713E+01 + 1.912840E+01 -1.936283E+00 4.152298E+00 4.020247E+01 -1.390908E+01 -4.144577E+01 5.789888E+00 2.515792E+01 -1.347403E+01 -5.071019E+01 + 1.652795E+02 -1.983301E+02 1.067067E+02 2.897411E+01 -1.396998E+02 1.267285E+02 7.823540E+00 -1.165760E+02 1.841581E+02 -1.572478E+02 + -1.936988E+01 2.011063E+02 -2.535251E+02 1.626668E+02 2.086747E+02 -4.530701E+02 3.734216E+02 3.219101E+02 -7.497116E+02 3.130326E+02 + 7.424257E+02 -1.167552E+03 -8.976851E+02 1.831967E+03 1.926242E+03 -9.571018E+02 -3.153475E+03 -2.976521E+03 -1.638780E+03 -6.367896E+02 + 2.730227E+05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 72 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 8.464837E+02 -1.313876E+03 6.452420E+02 -2.030543E+02 5.649763E+01 -3.767831E+00 -2.395699E+00 5.894200E-01 2.209007E-01 + 2.812877E+00 -2.056928E+00 -1.522516E+00 2.497757E-01 1.509286E+00 -1.404863E+00 -1.498325E+00 4.208795E-01 2.746340E+00 -4.619443E+00 + 6.307662E+00 -8.921365E+00 9.564347E+00 -8.843444E+00 7.795762E+00 -4.989709E+00 3.258925E+00 5.013356E+00 -8.553610E+00 4.255757E+00 + 9.676295E-01 -6.788149E+00 3.466564E+00 7.372045E+00 -1.383407E+01 2.031703E+01 -5.598847E+01 8.413633E+01 -4.093829E+01 6.465635E+01 + -1.080704E+02 2.437774E+02 -1.713667E+02 -2.030893E+02 3.995678E+02 -9.010548E+01 2.675195E+02 2.048927E+03 -1.063898E+02 3.549380E+02 + -4.481237E+02 -1.706708E+03 -6.097746E+02 4.768282E+03 -2.744075E+03 1.326090E+04 -8.090915E+02 2.248934E+03 4.467535E+03 5.859890E+03 + 5.250335E+03 5.405910E+03 8.946998E+02 4.438571E+03 -1.743144E+04 6.535810E+03 -4.878342E+03 6.339721E+02 -1.613020E+03 -1.296932E+03 + 4.011113E+09 + 73 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.313876E+03 2.689582E+03 -2.022733E+03 8.329715E+02 -2.364486E+02 3.774245E+01 -8.067888E+00 2.715118E+00 3.736567E-01 + -8.621935E+00 7.252274E+00 -5.930536E-01 2.889968E+00 -6.143959E+00 5.571652E+00 -1.388172E+00 1.354096E+00 -3.540641E+00 6.327217E+00 + -9.688070E+00 1.335823E+01 -1.360524E+01 7.074924E+00 -1.126568E+00 8.070282E-01 -7.486315E+00 -3.295345E+00 7.509628E+00 -6.953190E-01 + -6.039457E+00 1.350145E+01 -1.250594E+01 -1.151253E+01 4.214761E+01 -5.592301E+01 1.196992E+02 -1.658491E+02 9.168908E+01 -1.017004E+02 + 2.179319E+02 -6.191528E+02 3.873667E+02 6.414994E+02 -7.192082E+02 6.297562E+01 2.033696E+02 -2.232787E+03 8.711249E+02 -1.685486E+03 + 5.498518E+02 1.840967E+03 8.386602E+01 -6.382231E+03 3.634849E+03 -2.407710E+04 4.819792E+03 -1.554944E+03 -6.908639E+03 -5.349845E+03 + 2.866392E+04 -1.152468E+04 2.934136E+03 -7.751189E+03 1.657754E+04 -1.427489E+04 -3.874685E+03 4.770905E+03 3.662418E+03 2.068693E+03 + -7.487201E+09 + 74 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 6.452420E+02 -2.022733E+03 2.556488E+03 -1.705366E+03 6.632291E+02 -1.595225E+02 3.213952E+01 -7.193999E+00 2.271164E+00 + 3.840145E+00 -2.034305E+00 -4.879214E-01 -2.822512E+00 6.734336E+00 -7.111015E+00 5.930521E+00 -4.105417E+00 6.832157E-01 -2.415640E+00 + 4.877270E+00 -5.995885E+00 6.432025E+00 1.487747E+00 -1.016278E+01 8.906843E+00 -3.305017E+00 1.085819E+01 -9.766780E+00 2.253409E+00 + 8.731420E+00 -8.263082E+00 3.284183E+00 2.977595E+01 -7.115065E+01 8.127716E+01 -7.695672E+01 6.205376E+01 -1.542799E+01 -3.719950E+01 + -5.654129E+01 5.573138E+02 -3.010289E+02 -6.256207E+02 3.321300E+02 1.363142E+02 -1.273010E+03 2.011489E+03 -3.286721E+03 2.054575E+03 + -5.343163E+02 7.895544E+02 1.271654E+03 7.516226E+03 -1.863104E+03 2.035020E+04 -5.478936E+03 -3.911369E+02 4.496364E+03 4.544480E+03 + -7.958970E+04 1.696783E+04 -3.743180E+03 5.872752E+03 5.216435E+03 1.990972E+04 1.031580E+04 -1.291674E+04 -7.084843E+02 -2.137770E+03 + 6.424784E+09 + 75 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.030543E+02 8.329715E+02 -1.705366E+03 1.905245E+03 -1.149000E+03 4.003879E+02 -1.044899E+02 2.211100E+01 -5.332943E+00 + 1.569145E+00 -3.312086E+00 2.199701E+00 1.945506E+00 -5.060255E+00 5.269988E+00 -5.037224E+00 2.655517E+00 1.337812E+00 -8.927504E-01 + -1.885093E+00 4.380241E+00 -8.178539E+00 5.931802E+00 2.203363E-01 -2.013787E+00 1.405657E+00 -1.270799E+01 1.169691E+01 -6.239231E+00 + -8.445454E+00 1.272698E+01 -3.187066E+00 -2.115205E+01 4.118725E+01 -5.945668E+01 1.765933E+01 4.385611E+01 -3.049214E+01 5.032457E+01 + 8.059152E+00 -3.119245E+02 -3.361770E+01 4.021932E+02 -4.199192E+02 7.616047E+02 -4.912667E+02 -9.710768E+02 3.437310E+03 6.784237E+02 + 1.180893E+03 -2.023578E+03 3.664475E+02 -8.944481E+03 1.572318E+03 -1.589302E+04 3.714318E+03 2.387914E+02 -1.241980E+03 -5.829398E+03 + 5.249225E+04 -1.548132E+04 -3.230146E+02 -3.526592E+03 -1.422005E+04 -1.712800E+04 -3.893944E+03 1.220807E+04 -3.549098E+03 1.885858E+03 + -5.170914E+09 + 76 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.649763E+01 -2.364486E+02 6.632291E+02 -1.149000E+03 1.108929E+03 -6.058172E+02 2.208546E+02 -6.523002E+01 1.495920E+01 + -2.828206E+00 1.206384E+00 -6.474361E-02 -4.518914E+00 6.385129E+00 -5.577522E+00 3.424385E+00 2.505521E-01 -3.037052E+00 2.893162E+00 + -7.459573E-02 -3.868817E+00 8.653924E+00 -1.040747E+01 6.560600E+00 -3.800459E+00 7.241435E+00 1.808873E+00 -3.291886E+00 1.700990E+00 + 5.880137E+00 -1.756824E+01 1.763490E+01 -1.230977E+01 8.993428E+00 3.095728E+01 -3.933154E+01 1.671182E+01 -1.609567E+01 -8.494583E-01 + -5.495353E+01 1.364818E+02 2.421984E+02 -3.230602E+02 5.642795E+02 -1.471270E+03 2.254301E+03 -5.694646E+02 -4.728791E+02 -2.563145E+03 + -2.719368E+02 1.765321E+03 9.882914E+02 4.194828E+03 -2.447281E+03 8.258280E+03 -4.116820E+03 -3.141856E+03 -1.952293E+03 -2.869759E+02 + -2.474059E+03 2.916328E+03 1.372272E+03 6.353563E+02 2.037613E+04 1.721657E+04 2.396980E+02 -4.874917E+03 3.055636E+03 -5.826336E+02 + 2.806774E+09 + 77 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -3.767831E+00 3.774245E+01 -1.595225E+02 4.003879E+02 -6.058172E+02 5.423568E+02 -2.955323E+02 1.134114E+02 -3.601230E+01 + 1.234381E+01 -7.112714E+00 4.386461E+00 4.463052E-01 -2.815318E+00 2.309520E+00 -1.693078E-01 -3.145133E+00 4.307128E+00 -2.529724E+00 + 1.050875E+00 4.886935E-01 -3.848636E+00 7.199261E+00 -5.441287E+00 1.686162E+00 -1.580797E+00 -2.773770E+00 3.980870E+00 -3.266060E+00 + 1.377857E+00 5.204820E+00 -5.233798E+00 6.381902E+00 -8.476674E+00 -3.024971E+01 6.011389E+01 -4.571282E+01 -1.929634E+01 4.792150E+01 + -3.273833E+01 1.373011E+01 -1.361210E+02 1.608533E+01 2.865304E+01 5.557204E+02 -1.204081E+03 -7.076476E+02 2.567676E+02 1.633813E+03 + -2.785508E+02 -4.551936E+02 -2.305714E+03 -1.138245E+03 2.940221E+03 1.444152E+02 4.540057E+02 4.847824E+03 3.524046E+03 4.533918E+03 + -6.771800E+03 2.818919E+03 1.740361E+02 5.247092E+02 -1.636231E+04 -1.589017E+04 1.685746E+03 -1.190001E+03 -3.348122E+02 1.118272E+02 + 1.112780E+08 + 78 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.395699E+00 -8.067888E+00 3.213952E+01 -1.044899E+02 2.208546E+02 -2.955323E+02 2.665678E+02 -1.521946E+02 6.106727E+01 + -2.538037E+01 1.412963E+01 -9.129718E+00 4.669805E+00 -2.150750E+00 2.069012E+00 -2.514188E+00 3.731233E+00 -3.319920E+00 8.863980E-01 + -4.074630E-01 -3.823958E-01 3.054113E+00 -4.943519E+00 4.087628E+00 -1.802690E+00 3.154526E-01 2.285192E+00 -1.716835E+00 1.181501E+00 + -3.627526E+00 3.906738E+00 -1.093010E+01 1.249250E+01 -1.463533E+01 2.958161E+01 -5.377078E+01 2.920771E+01 3.408403E+01 -2.547056E+01 + 4.767133E+01 -9.229858E+01 9.878430E+01 1.249040E+02 -1.751088E+02 4.183421E+02 -4.469208E+02 1.046518E+03 3.683890E+02 -1.350212E+02 + 5.996035E+02 -9.462874E+02 1.412112E+03 6.777181E+02 -1.777022E+03 -1.799845E+03 2.220686E+03 -1.990134E+03 -2.277200E+03 -9.295022E+02 + -8.545794E+03 -1.948242E+02 -9.588000E+02 -5.186292E+02 6.901538E+03 3.165296E+03 -1.177571E+03 2.622340E+03 -5.044901E+02 -3.865811E+02 + -9.100549E+08 + 79 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.894200E-01 2.715118E+00 -7.193999E+00 2.211100E+01 -6.523002E+01 1.134114E+02 -1.521946E+02 1.505862E+02 -9.036785E+01 + 3.938839E+01 -1.742600E+01 8.751048E+00 -5.005399E+00 3.089800E+00 -3.062173E+00 3.001273E+00 -2.637119E+00 8.545842E-01 1.259872E+00 + -1.200020E+00 1.347019E+00 -2.838283E+00 3.172171E+00 -1.890844E+00 8.831380E-01 -1.466764E+00 2.968632E+00 -3.883602E+00 2.368661E+00 + 2.808688E+00 -3.530699E+00 1.023710E+01 -1.394718E+01 2.520449E+01 -2.889984E+01 4.682012E+01 -4.469828E+01 1.322876E+01 -1.186324E+01 + -1.224260E+01 8.205678E+01 -1.178078E+02 -1.034205E+02 2.380922E+00 -4.947038E+02 6.006642E+02 -1.125637E+02 -1.063278E+03 -3.539815E+02 + -5.323954E+02 7.595648E+02 1.530226E+02 -6.411733E+02 1.112077E+03 -1.177380E+02 -6.956694E+02 -8.577359E+02 1.282698E+03 -3.212375E+03 + 2.622538E+04 -2.795300E+03 5.202617E+02 7.784959E+02 -6.746549E+03 8.459323E+02 1.846261E+03 -2.268244E+03 -1.598764E+01 6.148903E+02 + 4.881502E+08 + 80 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.209007E-01 3.736567E-01 2.271164E+00 -5.332943E+00 1.495920E+01 -3.601230E+01 6.106727E+01 -9.036785E+01 1.025988E+02 + -6.757055E+01 2.883013E+01 -1.088063E+01 3.790746E+00 -1.733158E+00 1.920564E+00 -2.108218E+00 1.759187E+00 -5.259019E-01 -1.167047E+00 + 1.030964E+00 -5.585420E-01 1.670249E+00 -1.850044E+00 6.325403E-01 6.946556E-01 -7.483537E-01 -8.427995E-01 9.988477E-01 3.299237E-01 + -3.108419E+00 1.671680E+00 -4.045878E+00 6.104257E+00 -1.475615E+01 1.858426E+01 -3.231265E+01 4.612068E+01 -4.459806E+01 3.554608E+01 + -2.751701E+00 -4.465727E+01 7.043140E+01 8.517630E+01 7.801976E+01 2.595690E+02 -3.720180E+02 -5.223512E+02 1.190788E+03 3.517942E+02 + 3.084557E+02 -4.696465E+02 -1.139909E+03 1.177479E+03 -1.443818E+03 2.854292E+03 -3.138850E+03 2.807436E+03 -6.951588E+02 3.081125E+03 + -2.295758E+04 3.004354E+03 -5.063971E+02 -3.180064E+02 6.701108E+03 -2.847407E+03 -4.201207E+02 1.615763E+03 4.235039E+02 -2.598532E+02 + -2.920598E+07 + 81 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.812877E+00 -8.621935E+00 3.840145E+00 1.569145E+00 -2.828206E+00 1.234381E+01 -2.538037E+01 3.938839E+01 -6.757055E+01 + 8.679518E+01 -5.955995E+01 2.366496E+01 -6.460463E+00 1.096504E+00 -9.123259E-01 2.025646E+00 -2.737828E+00 1.253489E+00 4.845634E-01 + 1.438080E-01 -2.476702E-01 -2.024285E+00 2.644709E+00 -1.157948E+00 -3.462714E-02 6.491148E-01 2.146802E+00 -5.350343E+00 4.962216E+00 + -3.207396E+00 8.447563E-01 2.138072E+00 -3.314429E+00 9.597212E+00 -1.508627E+01 1.899080E+01 -2.990154E+01 2.566504E+01 -9.905470E-01 + -3.820741E+01 5.928145E+01 -2.836668E+01 -8.871079E+01 3.263125E+01 -1.599764E+02 1.188548E+02 7.571306E+02 -1.001882E+03 -1.466402E+02 + -5.674572E+02 3.569310E+02 5.537898E+02 -6.437408E+02 9.151237E+02 -2.611014E+03 2.681322E+03 -2.569549E+03 2.451814E+02 -3.947339E+03 + 1.172639E+04 -1.959302E+03 -4.163879E+02 9.138428E+02 -5.954803E+02 5.476602E+03 -1.029420E+03 -6.305818E+02 -2.821562E+02 1.060564E+01 + 1.045294E+09 + 82 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.056928E+00 7.252274E+00 -2.034305E+00 -3.312086E+00 1.206384E+00 -7.112714E+00 1.412963E+01 -1.742600E+01 2.883013E+01 + -5.955995E+01 8.272640E+01 -5.511964E+01 1.992359E+01 -5.487732E+00 1.888196E+00 -3.508446E+00 4.844831E+00 -2.403990E+00 -9.934438E-01 + 8.908385E-01 -4.187854E-01 1.651558E+00 -1.497256E+00 3.325674E-02 1.449352E+00 -2.034024E+00 -2.028541E+00 7.438560E+00 -7.279522E+00 + 5.106382E+00 -6.932714E-01 -4.653068E+00 3.430807E+00 -5.465576E+00 7.565767E+00 -6.561426E+00 1.795046E+01 -2.034816E+00 -2.897627E+01 + 7.750346E+01 -5.800571E+01 2.342182E+01 -2.333663E+01 -4.026920E+00 4.257016E+01 -1.042048E+02 -7.768166E+02 5.633448E+02 6.740327E+02 + 3.981420E+02 -1.932277E+02 -3.345794E+02 -3.052495E+01 3.524881E+02 -4.252482E+02 1.085532E+03 3.547022E+02 -3.878070E+02 3.041348E+03 + -2.661104E+03 2.187730E+03 1.580525E+03 -8.752386E+02 -4.272840E+03 -6.412884E+03 8.551598E+02 -3.714032E+02 2.717243E+02 -1.899280E+02 + -7.086943E+08 + 83 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.522516E+00 -5.930536E-01 -4.879214E-01 2.199701E+00 -6.474361E-02 4.386461E+00 -9.129718E+00 8.751048E+00 -1.088063E+01 + 2.366496E+01 -5.511964E+01 7.416660E+01 -4.792585E+01 1.784856E+01 -5.830412E+00 3.331921E+00 -3.965808E+00 1.535965E+00 2.113321E+00 + -2.834084E+00 1.969586E+00 -7.305354E-01 -1.835412E-01 -4.012236E-01 3.745109E-01 -5.753932E-01 4.354316E+00 -7.966440E+00 5.460763E+00 + -4.498690E+00 2.245164E+00 8.112059E+00 -9.208902E+00 1.006770E+01 -2.862086E+00 -1.198988E+01 1.951412E+01 -2.441105E+01 2.405279E+01 + -5.190571E+01 5.407114E+01 -3.640874E+01 8.527706E+01 1.865248E+01 -1.576740E+02 3.476188E+02 5.579626E+02 4.745729E+01 -8.563759E+02 + -2.479214E+02 4.854490E+02 -8.830265E+02 -1.099942E+02 -6.509701E+02 1.285418E+03 -1.760620E+03 2.405706E+01 1.017051E+03 -3.540252E+03 + 5.648071E+03 -1.625398E+03 -6.088552E+02 8.857504E+02 -1.629962E+03 6.935899E+03 -1.131945E+03 3.878537E+02 -3.814358E+02 2.034553E+01 + 7.343957E+08 + 84 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.497757E-01 2.889968E+00 -2.822512E+00 1.945506E+00 -4.518914E+00 4.463052E-01 4.669805E+00 -5.005399E+00 3.790746E+00 + -6.460463E+00 1.992359E+01 -4.792585E+01 6.468164E+01 -4.345421E+01 1.761449E+01 -6.733541E+00 3.474946E+00 -9.730300E-01 -1.341139E+00 + 1.988356E+00 -8.164312E-01 1.356612E-01 -9.495490E-01 2.311031E+00 -2.243810E+00 1.806907E+00 -3.517295E+00 6.192899E+00 -6.349573E+00 + 5.963209E+00 -4.013896E+00 -4.623752E+00 5.888822E+00 -5.244173E+00 -1.709959E+00 5.795616E+00 -9.962136E+00 1.005049E+01 1.007400E+01 + -2.708293E+01 2.443991E+01 -3.755317E+01 -1.187053E+01 -7.109723E+01 2.231117E+01 -2.653760E+02 -3.437633E+02 3.034715E+02 4.995640E+01 + 2.594298E+02 -8.346611E+02 2.612093E+01 3.424240E+02 -7.390996E+01 -5.772481E+01 -7.526878E+02 6.490640E+02 -1.539542E+03 3.058790E+03 + -2.526685E+03 1.200293E+02 -8.703816E+02 -4.206392E+02 6.675469E+02 -4.027940E+03 1.249174E+03 -9.066764E+02 6.434766E+02 -5.671338E+01 + -1.011745E+09 + 85 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.509286E+00 -6.143959E+00 6.734336E+00 -5.060255E+00 6.385129E+00 -2.815318E+00 -2.150750E+00 3.089800E+00 -1.733158E+00 + 1.096504E+00 -5.487732E+00 1.784856E+01 -4.345421E+01 6.193374E+01 -4.446272E+01 1.920779E+01 -7.462970E+00 3.035057E+00 -6.233700E-01 + -5.604234E-01 -8.204951E-01 1.642852E+00 1.583122E-01 -1.495748E+00 1.749988E+00 -7.688459E-01 1.089373E+00 -4.632023E+00 6.841711E+00 + -5.176420E+00 2.137885E+00 8.102407E-01 -5.714832E-01 2.840759E+00 1.080792E+00 2.639701E+00 5.052203E-01 -1.188666E+01 -4.352794E-01 + 2.899344E+01 -7.297177E+01 1.106191E+02 1.911869E+01 -3.937488E+01 2.019136E+02 -1.367736E+02 5.679724E+02 -5.884688E+02 6.014684E+02 + -8.964281E+02 6.065833E+02 4.654506E+01 1.868111E+01 5.000407E+02 -3.267416E+02 1.503791E+03 4.027494E+02 5.756294E+02 -1.195217E+03 + -9.733367E+03 2.494543E+02 1.072781E+03 6.767773E+01 1.172739E+03 7.302545E+02 -1.478340E+03 1.339225E+03 -7.220321E+02 1.417398E+01 + 5.406246E+08 + 86 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.404863E+00 5.571652E+00 -7.111015E+00 5.269988E+00 -5.577522E+00 2.309520E+00 2.069012E+00 -3.062173E+00 1.920564E+00 + -9.123259E-01 1.888196E+00 -5.830412E+00 1.761449E+01 -4.446272E+01 6.634120E+01 -4.903202E+01 2.262431E+01 -1.026796E+01 4.258115E+00 + -1.453466E+00 1.063051E+00 -1.815751E+00 7.663758E-01 4.767893E-01 -9.551010E-01 3.152538E-01 -2.517745E-01 2.432027E+00 -3.627156E+00 + 1.116212E+00 1.248551E+00 3.763895E+00 -9.477486E+00 2.625898E+00 -4.944036E-01 -6.377897E+00 -4.642804E+00 3.763986E+01 -6.148391E+01 + 3.079511E+01 3.070393E+01 -8.706575E+01 -3.547234E+01 5.164596E+01 -8.868149E+00 1.587682E+02 -7.831128E+02 6.504004E+01 -8.033231E+02 + 1.349233E+03 -1.242722E+03 6.107364E+02 8.491198E+02 -7.974214E+02 1.048165E+03 -8.344604E+02 -1.065724E+03 4.645219E+02 -4.901561E+02 + 1.002584E+04 -1.367742E+02 -1.571755E+03 -6.953386E+02 2.041975E+03 -2.128289E+03 1.900258E+02 -2.285335E+02 5.835618E+02 1.418651E+02 + -1.228855E+08 + 87 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.498325E+00 -1.388172E+00 5.930521E+00 -5.037224E+00 3.424385E+00 -1.693078E-01 -2.514188E+00 3.001273E+00 -2.108218E+00 + 2.025646E+00 -3.508446E+00 3.331921E+00 -6.733541E+00 1.920779E+01 -4.903202E+01 7.718394E+01 -6.439141E+01 3.622151E+01 -1.665976E+01 + 6.616307E+00 -2.173001E+00 5.033132E-01 1.150465E+00 -2.673973E-01 -1.989773E+00 2.657903E+00 -3.457358E+00 4.585159E+00 -6.887192E+00 + 9.538753E+00 -1.138394E+01 3.112238E+00 7.584064E+00 -1.143702E+01 2.180194E+01 -1.458270E+01 2.992763E+01 -6.854449E+01 1.020366E+02 + -6.734600E+01 1.343328E+01 2.621722E+01 -5.830541E+01 1.427201E+02 -1.374637E+02 -1.065675E+02 2.121592E+02 1.011467E+03 4.298330E+02 + -7.531564E+02 1.909679E+03 -1.398163E+03 1.664777E+02 -1.354087E+02 -7.738333E+02 -2.334910E+02 1.902875E+03 -2.128684E+03 1.006019E+03 + -9.580391E+03 1.134181E+03 1.697238E+03 7.846502E+02 9.045192E+02 2.062661E+03 3.757625E+02 -4.061857E+02 -9.884442E+01 -1.296608E+02 + 6.515467E+07 + 88 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.208795E-01 1.354096E+00 -4.105417E+00 2.655517E+00 2.505521E-01 -3.145133E+00 3.731233E+00 -2.637119E+00 1.759187E+00 + -2.737828E+00 4.844831E+00 -3.965808E+00 3.474946E+00 -7.462970E+00 2.262431E+01 -6.439141E+01 1.072958E+02 -1.004972E+02 6.222470E+01 + -3.043333E+01 1.066544E+01 -1.685450E+00 -2.021429E+00 1.253976E+00 1.894792E+00 -1.911911E+00 1.489985E+00 -4.895216E+00 9.613681E+00 + -8.533480E+00 7.309412E+00 1.976515E+00 -7.578390E+00 1.456526E+01 -2.493971E+01 1.939372E+01 -1.799000E+01 1.251581E+01 -3.253140E+01 + 6.184092E+01 -5.977151E+01 6.773394E+01 4.576354E+01 -2.026224E+02 2.264926E+02 1.263814E+00 4.965564E+02 -1.396098E+03 1.521731E+02 + -3.608352E+01 -1.007359E+03 1.120353E+03 -4.767761E+02 1.659316E+02 -7.037788E+02 7.249655E+02 -2.352010E+03 1.809738E+03 -2.668892E+03 + 1.014587E+04 -2.982246E+03 -1.448081E+03 -6.418427E+02 -6.798187E+02 -1.893974E+03 6.273860E+02 7.437422E+02 -1.000419E+01 5.362030E+01 + -4.032625E+08 + 89 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.746340E+00 -3.540641E+00 6.832157E-01 1.337812E+00 -3.037052E+00 4.307128E+00 -3.319920E+00 8.545842E-01 -5.259019E-01 + 1.253489E+00 -2.403990E+00 1.535965E+00 -9.730300E-01 3.035057E+00 -1.026796E+01 3.622151E+01 -1.004972E+02 1.725168E+02 -1.670454E+02 + 1.022090E+02 -4.256999E+01 1.233358E+01 -9.709622E-01 -9.987760E-01 -2.930518E+00 2.521580E+00 -4.993302E-01 2.327742E+00 -7.832093E+00 + 8.812513E+00 -4.483302E+00 -7.562176E+00 5.580709E+00 1.074534E+01 -2.649396E+01 4.531745E+01 -5.332094E+01 9.484132E+01 -7.777597E+01 + -2.574220E+01 2.043962E+01 -6.915070E+01 1.080256E+02 -4.381358E+01 -3.707917E+02 4.040712E+02 -1.797536E+03 1.485949E+03 -7.496807E+02 + 3.945972E+01 8.047976E+02 -6.835592E+02 8.076672E+02 -7.577622E+02 1.975459E+03 -2.405668E+03 3.016802E+03 -2.424053E+03 4.007723E+03 + -1.481114E+04 2.000902E+03 6.480028E+02 -2.282344E+02 -7.572577E+02 9.186947E+02 6.153894E+02 -1.750465E+03 5.314829E+02 -4.922418E+02 + 1.177632E+09 + 90 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.619443E+00 6.327217E+00 -2.415640E+00 -8.927504E-01 2.893162E+00 -2.529724E+00 8.863980E-01 1.259872E+00 -1.167047E+00 + 4.845634E-01 -9.934438E-01 2.113321E+00 -1.341139E+00 -6.233700E-01 4.258115E+00 -1.665976E+01 6.222470E+01 -1.670454E+02 2.627092E+02 + -2.317183E+02 1.293046E+02 -5.406293E+01 1.865416E+01 -7.660927E+00 7.625279E+00 -6.146045E+00 2.269013E+00 -3.808814E+00 1.029353E+01 + -1.755298E+01 1.768612E+01 -2.159066E+00 -2.705028E+00 -1.160953E+01 3.217366E+01 -7.013503E+01 6.557498E+01 -1.196706E+02 6.555568E+01 + 1.229770E+02 -6.527047E+01 8.064697E+01 -2.554468E+02 1.585684E+02 5.579850E+02 -9.625110E+02 2.720324E+03 -1.323865E+03 6.946852E+02 + 8.307456E+02 -1.589922E+03 1.931032E+03 -1.541646E+03 1.772129E+03 -1.856915E+03 3.808422E+03 -3.420762E+03 3.563480E+03 -2.401072E+03 + -1.146017E+04 -1.580566E+03 -3.003532E+02 -1.871932E+02 4.577851E+03 -2.954722E+03 -1.259899E+03 7.337816E+02 -2.408048E+02 5.572954E+02 + -2.858447E+09 + 91 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 6.307662E+00 -9.688070E+00 4.877270E+00 -1.885093E+00 -7.459573E-02 1.050875E+00 -4.074630E-01 -1.200020E+00 1.030964E+00 + 1.438080E-01 8.908385E-01 -2.834084E+00 1.988356E+00 -5.604234E-01 -1.453466E+00 6.616307E+00 -3.043333E+01 1.022090E+02 -2.317183E+02 + 3.252346E+02 -2.738423E+02 1.539077E+02 -7.149226E+01 3.508619E+01 -2.055533E+01 1.580725E+01 -6.408341E+00 2.176748E+00 -9.299806E+00 + 1.980911E+01 -2.284213E+01 6.958072E+00 -1.110567E-01 4.683301E+00 -2.722852E+01 7.840399E+01 -5.654248E+01 6.630922E+01 3.168245E+01 + -2.337926E+02 2.213785E+02 -3.003347E+02 4.976426E+02 -3.551773E+02 -4.562856E+02 9.003467E+02 -2.436866E+03 1.259464E+03 2.513122E+01 + -2.212988E+03 3.372097E+03 -2.463990E+03 9.351094E+02 -1.124162E+03 2.065090E+02 -8.522706E+02 3.845628E+03 -2.794184E+03 1.161228E+02 + 8.486509E+03 1.379401E+03 1.056180E+03 1.186836E+03 -4.620953E+03 6.461425E+03 -2.358245E+02 1.168465E+03 -6.480966E+02 -8.936616E+00 + 4.140634E+09 + 92 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -8.921365E+00 1.335823E+01 -5.995885E+00 4.380241E+00 -3.868817E+00 4.886935E-01 -3.823958E-01 1.347019E+00 -5.585420E-01 + -2.476702E-01 -4.187854E-01 1.969586E+00 -8.164312E-01 -8.204951E-01 1.063051E+00 -2.173001E+00 1.066544E+01 -4.256999E+01 1.293046E+02 + -2.738423E+02 3.628175E+02 -2.911601E+02 1.671959E+02 -9.093458E+01 5.161822E+01 -3.633787E+01 2.024568E+01 -6.833212E+00 5.618171E+00 + -4.451651E+00 1.292186E+00 9.995315E+00 -1.163684E+01 1.090550E+01 1.408423E+01 -5.540114E+01 5.024480E+01 -1.311280E+01 -1.072804E+02 + 2.409757E+02 -2.675384E+02 5.520550E+02 -7.659460E+02 7.764586E+02 -3.845017E+02 -2.979340E+02 1.267241E+03 -1.200785E+03 -6.115292E+02 + 2.451818E+03 -2.457332E+03 1.620024E+03 4.087948E+02 -2.073042E+02 1.525626E+03 -2.226890E+03 -7.555279E+02 5.501128E+02 4.415566E+03 + 2.184196E+04 -4.199015E+02 -8.638492E-01 -2.131758E+03 2.675411E+03 -8.494333E+03 1.501568E+03 -1.211416E+03 9.558911E+02 -3.715837E+02 + -2.353446E+09 + 93 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 9.564347E+00 -1.360524E+01 6.432025E+00 -8.178539E+00 8.653924E+00 -3.848636E+00 3.054113E+00 -2.838283E+00 1.670249E+00 + -2.024285E+00 1.651558E+00 -7.305354E-01 1.356612E-01 1.642852E+00 -1.815751E+00 5.033132E-01 -1.685450E+00 1.233358E+01 -5.406293E+01 + 1.539077E+02 -2.911601E+02 3.649145E+02 -3.038741E+02 1.979885E+02 -1.145204E+02 6.404032E+01 -3.473666E+01 1.381462E+01 -3.430068E+00 + -5.220869E+00 9.747151E+00 -7.834048E+00 -3.760277E+00 1.017239E+01 -1.462258E+01 1.060661E+01 9.511024E+00 -4.778706E+01 1.880140E+02 + -2.866588E+02 3.088751E+02 -6.622840E+02 9.511535E+02 -1.101318E+03 1.099799E+03 -1.142643E+02 -6.839531E+02 1.507580E+03 4.717284E+02 + -1.632085E+03 -4.294224E+01 -6.482634E+02 -9.696427E+02 1.122998E+03 -7.603348E+02 -2.989322E+02 -1.417238E+03 -2.129386E+02 -8.369840E+03 + -1.937794E+04 -1.707127E+03 -1.277504E+03 2.805497E+03 -4.676051E+03 7.640401E+03 -9.603808E+02 4.607624E+02 -1.121968E+03 4.767205E+02 + 6.715513E+08 + 94 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -8.843444E+00 7.074924E+00 1.487747E+00 5.931802E+00 -1.040747E+01 7.199261E+00 -4.943519E+00 3.172171E+00 -1.850044E+00 + 2.644709E+00 -1.497256E+00 -1.835412E-01 -9.495490E-01 1.583122E-01 7.663758E-01 1.150465E+00 -2.021429E+00 -9.709622E-01 1.865416E+01 + -7.149226E+01 1.671959E+02 -3.038741E+02 4.201640E+02 -3.910012E+02 2.596079E+02 -1.441416E+02 7.618752E+01 -3.718519E+01 1.904644E+01 + -9.837462E+00 4.239772E+00 -8.717820E+00 4.221275E+01 -7.903014E+01 7.466678E+01 -6.289285E+01 1.426153E+00 6.399490E+01 -1.919254E+02 + 2.806128E+02 -2.979407E+02 5.483046E+02 -9.888182E+02 1.253299E+03 -1.192979E+03 4.705099E+02 3.047257E+02 -1.213946E+03 1.892474E+02 + 9.500870E+02 8.365032E+02 6.582816E+02 1.070160E+03 -9.953057E+02 -1.233953E+03 4.494287E+03 -9.184572E+02 2.816552E+03 7.888855E+03 + 1.206479E+04 7.891339E+02 2.213146E+01 -2.730137E+03 -5.907288E+02 -3.700924E+03 -1.024186E+03 -1.115604E+03 9.649541E+02 1.237175E+02 + -3.826648E+08 + 95 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 7.795762E+00 -1.126568E+00 -1.016278E+01 2.203363E-01 6.560600E+00 -5.441287E+00 4.087628E+00 -1.890844E+00 6.325403E-01 + -1.157948E+00 3.325674E-02 -4.012236E-01 2.311031E+00 -1.495748E+00 4.767893E-01 -2.673973E-01 1.253976E+00 -9.987760E-01 -7.660927E+00 + 3.508619E+01 -9.093458E+01 1.979885E+02 -3.910012E+02 5.691544E+02 -5.351416E+02 3.665246E+02 -2.167588E+02 1.057001E+02 -4.845978E+01 + 2.976139E+01 -1.981638E+01 2.915015E+00 -3.571870E+01 9.574045E+01 -9.205120E+01 9.679529E+01 -1.922549E+01 -1.150765E+02 1.983184E+02 + -2.581330E+02 2.255105E+02 -2.967007E+02 6.209706E+02 -9.084277E+02 1.008553E+03 -9.252016E+01 -9.513602E+02 1.820485E+03 -1.644310E+03 + -4.591505E+02 -1.573291E+02 -4.679929E+02 -9.307330E+02 1.344160E+03 3.647308E+01 -5.469199E+03 3.608453E+03 -4.608989E+03 -5.781827E+03 + 1.028261E+03 -9.080000E+02 3.115216E+03 3.438552E+03 4.272536E+03 -2.074247E+03 2.487464E+03 -1.283014E+03 4.774877E+02 -5.772406E+02 + 1.013393E+09 + 96 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.989709E+00 8.070282E-01 8.906843E+00 -2.013787E+00 -3.800459E+00 1.686162E+00 -1.802690E+00 8.831380E-01 6.946556E-01 + -3.462714E-02 1.449352E+00 3.745109E-01 -2.243810E+00 1.749988E+00 -9.551010E-01 -1.989773E+00 1.894792E+00 -2.930518E+00 7.625279E+00 + -2.055533E+01 5.161822E+01 -1.145204E+02 2.596079E+02 -5.351416E+02 7.789051E+02 -7.394191E+02 5.191810E+02 -2.769672E+02 1.216192E+02 + -6.006502E+01 4.142263E+01 -7.029961E+00 2.910726E+01 -8.037083E+01 6.297916E+01 -9.015462E+01 8.711550E+01 5.562983E+01 -1.557928E+02 + 2.597725E+02 -1.723572E+02 2.151763E+02 -2.943317E+02 3.424213E+02 -8.720029E+02 -7.229062E+01 1.531079E+03 -1.687171E+03 1.268959E+03 + 2.030339E+02 -6.416263E+02 -1.581494E+02 1.137017E+03 -9.176288E+02 8.756198E+02 5.139020E+03 -4.436476E+03 4.157293E+03 4.520904E+03 + -3.577195E+04 4.985232E+03 -3.637981E+03 -4.433653E+03 -4.341252E+03 5.170471E+03 -2.332988E+03 1.117268E+03 -2.457972E+02 4.311095E+02 + 2.128331E+08 + 97 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 3.258925E+00 -7.486315E+00 -3.305017E+00 1.405657E+00 7.241435E+00 -1.580797E+00 3.154526E-01 -1.466764E+00 -7.483537E-01 + 6.491148E-01 -2.034024E+00 -5.753932E-01 1.806907E+00 -7.688459E-01 3.152538E-01 2.657903E+00 -1.911911E+00 2.521580E+00 -6.146045E+00 + 1.580725E+01 -3.633787E+01 6.404032E+01 -1.441416E+02 3.665246E+02 -7.394191E+02 1.044603E+03 -9.850031E+02 6.349752E+02 -3.188550E+02 + 1.426585E+02 -7.249097E+01 1.582997E+01 -9.912211E+00 -2.561149E-01 3.892658E+01 2.408921E+01 -8.449694E+01 2.640266E+01 6.368245E+01 + -2.021984E+02 1.041393E+02 -3.552667E+02 7.541575E+02 -3.691649E+02 2.696011E+02 5.245712E+02 -2.476672E+03 3.013668E+03 -5.827616E+02 + -1.101753E+03 9.504947E+02 -1.312607E+03 2.851948E+03 -2.321530E+03 4.187915E+03 -5.758644E+03 8.142820E+03 -4.111819E+03 -2.595277E+03 + 5.012212E+04 -6.929915E+03 1.114545E+03 3.640913E+03 4.163839E+03 -4.570336E+03 5.512133E+02 -1.793488E+03 1.057377E+02 2.101421E+02 + -1.927745E+09 + 98 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.013356E+00 -3.295345E+00 1.085819E+01 -1.270799E+01 1.808873E+00 -2.773770E+00 2.285192E+00 2.968632E+00 -8.427995E-01 + 2.146802E+00 -2.028541E+00 4.354316E+00 -3.517295E+00 1.089373E+00 -2.517745E-01 -3.457358E+00 1.489985E+00 -4.993302E-01 2.269013E+00 + -6.408341E+00 2.024568E+01 -3.473666E+01 7.618752E+01 -2.167588E+02 5.191810E+02 -9.850031E+02 1.375595E+03 -1.233976E+03 7.895455E+02 + -4.080346E+02 1.863091E+02 -5.853682E+01 2.207033E+01 9.077105E+00 -6.542762E+01 -3.752975E+01 1.124931E+02 -1.742360E+02 1.846324E+02 + 1.294984E+00 1.826946E+02 5.268938E+02 -1.098582E+03 5.857535E+02 1.000500E+03 -2.495206E+03 4.149334E+03 -4.710545E+03 -4.786450E+02 + 1.886057E+03 -1.759436E+02 9.349801E+02 -5.258676E+03 3.544903E+03 -5.608160E+03 4.011859E+03 -9.252584E+03 5.711387E+03 -1.222014E+03 + -7.505071E+04 5.717467E+03 2.435944E+02 -2.152241E+03 -1.037737E+04 4.256690E+03 3.167900E+03 1.003987E+03 7.971562E+01 -7.192423E+02 + 2.046834E+09 + 99 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -8.553610E+00 7.509628E+00 -9.766780E+00 1.169691E+01 -3.291886E+00 3.980870E+00 -1.716835E+00 -3.883602E+00 9.988477E-01 + -5.350343E+00 7.438560E+00 -7.966440E+00 6.192899E+00 -4.632023E+00 2.432027E+00 4.585159E+00 -4.895216E+00 2.327742E+00 -3.808814E+00 + 2.176748E+00 -6.833212E+00 1.381462E+01 -3.718519E+01 1.057001E+02 -2.769672E+02 6.349752E+02 -1.233976E+03 1.731415E+03 -1.613004E+03 + 1.037036E+03 -5.528050E+02 2.376414E+02 -1.244681E+02 8.393489E+01 -1.790520E+01 7.926256E+01 -1.604462E+02 2.597761E+02 -3.800792E+02 + 1.478309E+02 -2.574662E+02 -6.360023E+02 6.612421E+02 -2.027783E+02 -1.932482E+03 2.919287E+03 -4.109544E+03 2.549519E+03 -2.605669E+02 + -1.154324E+03 -7.393710E+02 -2.227067E+02 6.013134E+03 -4.121102E+03 4.718467E+03 -8.521180E+02 4.985569E+03 -4.304232E+03 -2.877523E+02 + 6.986396E+04 -2.764774E+03 2.256870E+01 2.019236E+03 1.774254E+04 -2.005112E+03 -7.749392E+03 3.028288E+03 -1.699746E+03 6.918679E+02 + 1.334524E+09 + 100 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.255757E+00 -6.953190E-01 2.253409E+00 -6.239231E+00 1.700990E+00 -3.266060E+00 1.181501E+00 2.368661E+00 3.299237E-01 + 4.962216E+00 -7.279522E+00 5.460763E+00 -6.349573E+00 6.841711E+00 -3.627156E+00 -6.887192E+00 9.613681E+00 -7.832093E+00 1.029353E+01 + -9.299806E+00 5.618171E+00 -3.430068E+00 1.904644E+01 -4.845978E+01 1.216192E+02 -3.188550E+02 7.895455E+02 -1.613004E+03 2.353585E+03 + -2.115150E+03 1.395107E+03 -7.289939E+02 4.082143E+02 -3.077802E+02 1.716200E+02 -8.128112E+01 1.736784E+01 4.652798E+01 4.917337E+01 + 7.071473E+00 1.009101E+02 9.869597E+01 8.118448E+01 -9.886951E+01 1.960044E+03 -1.491596E+03 3.584509E+02 2.516756E+03 2.263174E+03 + 1.487551E+03 1.051045E+03 3.880193E+03 7.872967E+02 -1.044142E+03 4.796680E+03 -6.866918E+03 3.515638E+03 8.481593E+02 9.838136E+02 + -3.039314E+04 -3.478752E+03 -1.009503E+03 -3.691847E+03 -9.753667E+03 -3.141926E+03 5.439299E+03 -2.309784E+03 7.827059E+02 -2.277984E+02 + -4.902092E+09 + 101 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 9.676295E-01 -6.039457E+00 8.731420E+00 -8.445454E+00 5.880137E+00 1.377857E+00 -3.627526E+00 2.808688E+00 -3.108419E+00 + -3.207396E+00 5.106382E+00 -4.498690E+00 5.963209E+00 -5.176420E+00 1.116212E+00 9.538753E+00 -8.533480E+00 8.812513E+00 -1.755298E+01 + 1.980911E+01 -4.451651E+00 -5.220869E+00 -9.837462E+00 2.976139E+01 -6.006502E+01 1.426585E+02 -4.080346E+02 1.037036E+03 -2.115150E+03 + 2.954494E+03 -2.712242E+03 1.745741E+03 -1.080433E+03 7.429854E+02 -4.204460E+02 1.679471E+02 4.009643E+01 -2.690713E+02 2.697420E+02 + -7.322887E+01 -3.457939E+02 4.766008E+02 -4.334509E+02 -4.146773E+02 -1.249841E+03 1.627435E+03 8.633332E+02 -6.988961E+03 -5.531198E+03 + -2.758844E+03 2.416336E+03 -1.046839E+04 -9.894930E+02 -1.649977E+03 -1.849043E+03 -2.032758E+03 -5.400278E+03 -2.240766E+03 -3.839471E+03 + 8.278140E+03 5.333082E+03 2.835598E+02 3.377446E+03 1.389348E+04 3.265982E+03 -5.149162E+02 4.902236E+03 -4.264657E+02 -1.026184E+03 + 3.905113E+09 + 102 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -6.788149E+00 1.350145E+01 -8.263082E+00 1.272698E+01 -1.756824E+01 5.204820E+00 3.906738E+00 -3.530699E+00 1.671680E+00 + 8.447563E-01 -6.932714E-01 2.245164E+00 -4.013896E+00 2.137885E+00 1.248551E+00 -1.138394E+01 7.309412E+00 -4.483302E+00 1.768612E+01 + -2.284213E+01 1.292186E+00 9.747151E+00 4.239772E+00 -1.981638E+01 4.142263E+01 -7.249097E+01 1.863091E+02 -5.528050E+02 1.395107E+03 + -2.712242E+03 3.785068E+03 -3.360610E+03 2.328282E+03 -1.563180E+03 8.903513E+02 -5.727137E+02 2.768984E+02 5.666461E+01 -1.321390E+02 + -1.521364E+02 4.317467E+02 -5.792450E+02 4.444513E+02 9.269984E+02 1.136971E+03 -3.096318E+03 2.449657E+01 7.019237E+03 5.937058E+03 + 4.158818E+03 -3.633289E+03 1.373261E+04 1.014089E+03 5.105909E+03 -2.539978E+03 1.018023E+04 7.005496E+03 6.386590E+02 1.235814E+04 + -5.246891E+03 -9.018704E+03 2.999527E+03 -3.176531E+03 -1.838039E+04 2.141951E+03 -1.724524E+03 -4.784876E+03 2.470571E+02 1.694194E+03 + -9.921984E+08 + 103 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 3.466564E+00 -1.250594E+01 3.284183E+00 -3.187066E+00 1.763490E+01 -5.233798E+00 -1.093010E+01 1.023710E+01 -4.045878E+00 + 2.138072E+00 -4.653068E+00 8.112059E+00 -4.623752E+00 8.102407E-01 3.763895E+00 3.112238E+00 1.976515E+00 -7.562176E+00 -2.159066E+00 + 6.958072E+00 9.995315E+00 -7.834048E+00 -8.717820E+00 2.915015E+00 -7.029961E+00 1.582997E+01 -5.853682E+01 2.376414E+02 -7.289939E+02 + 1.745741E+03 -3.360610E+03 4.781246E+03 -4.658848E+03 3.553272E+03 -2.231785E+03 1.307071E+03 -6.022675E+02 -6.649571E+01 1.480216E+02 + 4.072172E+02 -7.793935E+02 2.338998E+02 -9.695447E+01 -3.144294E+02 -2.723808E+03 2.906638E+03 -2.011099E+02 3.855983E+01 -6.202034E+03 + -6.508422E+03 1.718018E+03 -4.694399E+03 -1.913707E+03 -3.845611E+03 2.806181E+03 -1.267638E+04 -5.702506E+03 2.093041E+03 -1.050182E+04 + -1.537540E+04 1.198711E+04 -4.691786E+03 -1.511155E+03 3.466664E+03 -6.765018E+03 3.925464E+03 -1.236503E+03 4.634489E+02 -2.319098E+03 + -5.393300E+08 + 104 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 7.372045E+00 -1.151253E+01 2.977595E+01 -2.115205E+01 -1.230977E+01 6.381902E+00 1.249250E+01 -1.394718E+01 6.104257E+00 + -3.314429E+00 3.430807E+00 -9.208902E+00 5.888822E+00 -5.714832E-01 -9.477486E+00 7.584064E+00 -7.578390E+00 5.580709E+00 -2.705028E+00 + -1.110567E-01 -1.163684E+01 -3.760277E+00 4.221275E+01 -3.571870E+01 2.910726E+01 -9.912211E+00 2.207033E+01 -1.244681E+02 4.082143E+02 + -1.080433E+03 2.328282E+03 -4.658848E+03 7.398976E+03 -7.759866E+03 5.725994E+03 -3.427871E+03 1.690303E+03 -2.871655E+02 -5.851806E+01 + -5.449924E+02 8.937173E+02 -4.720516E+02 4.590004E+02 -8.555291E+02 4.847863E+03 -2.019744E+03 5.918410E+02 -9.068905E+03 8.948879E+03 + 3.422432E+03 1.162868E+03 -3.561992E+03 6.510539E+03 -5.735454E+03 6.720452E+03 -3.868093E+02 6.187447E+03 -5.695548E+03 9.579348E+03 + -5.112550E+04 -7.809577E+03 1.552378E+03 2.403960E+02 1.529302E+04 9.863789E+03 -9.142885E+03 7.209595E+03 -1.667699E+03 4.164289E+03 + -3.881100E+06 + 105 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.383407E+01 4.214761E+01 -7.115065E+01 4.118725E+01 8.993428E+00 -8.476674E+00 -1.463533E+01 2.520449E+01 -1.475615E+01 + 9.597212E+00 -5.465576E+00 1.006770E+01 -5.244173E+00 2.840759E+00 2.625898E+00 -1.143702E+01 1.456526E+01 1.074534E+01 -1.160953E+01 + 4.683301E+00 1.090550E+01 1.017239E+01 -7.903014E+01 9.574045E+01 -8.037083E+01 -2.561149E-01 9.077105E+00 8.393489E+01 -3.077802E+02 + 7.429854E+02 -1.563180E+03 3.553272E+03 -7.759866E+03 1.308569E+04 -1.320528E+04 9.507863E+03 -5.896846E+03 2.372490E+03 -1.030804E+03 + 1.245857E+03 -1.582171E+03 2.791596E+03 -2.324027E+03 1.685755E+03 -3.305609E+03 -4.804355E+02 3.764800E+02 8.931424E+03 -9.430694E+03 + 6.072884E+03 -2.244788E+03 1.629450E+04 -3.618937E+03 9.345270E+03 -9.087637E+03 5.030482E+03 -3.240025E+03 5.649041E+03 1.602988E+04 + 1.780186E+05 7.575872E+03 8.914978E+03 3.048325E+03 -2.813720E+04 1.087257E+04 8.897150E+02 -1.137890E+04 2.369968E+03 -4.064923E+02 + -6.461924E+08 + 106 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.031703E+01 -5.592301E+01 8.127716E+01 -5.945668E+01 3.095728E+01 -3.024971E+01 2.958161E+01 -2.889984E+01 1.858426E+01 + -1.508627E+01 7.565767E+00 -2.862086E+00 -1.709959E+00 1.080792E+00 -4.944036E-01 2.180194E+01 -2.493971E+01 -2.649396E+01 3.217366E+01 + -2.722852E+01 1.408423E+01 -1.462258E+01 7.466678E+01 -9.205120E+01 6.297916E+01 3.892658E+01 -6.542762E+01 -1.790520E+01 1.716200E+02 + -4.204460E+02 8.903513E+02 -2.231785E+03 5.725994E+03 -1.320528E+04 2.071134E+04 -2.082101E+04 1.505938E+04 -7.463005E+03 3.087608E+03 + -1.549632E+03 1.859023E+03 -3.710771E+03 3.538105E+03 -2.154028E+03 9.553412E+02 -1.894558E+02 2.032682E+03 -7.560251E+03 1.022638E+04 + -8.853881E+03 -1.900394E+03 -8.084659E+02 5.226341E+03 -1.408883E+04 1.463417E+04 -1.202052E+04 -1.132682E+04 1.941497E+03 -3.733686E+04 + -2.441010E+05 -5.920546E+03 -1.709306E+04 -3.178177E+02 -4.660672E+03 1.135546E+04 -8.201543E+03 5.959045E+03 2.240551E+03 -2.181179E+03 + -3.150266E+08 + 107 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -5.598847E+01 1.196992E+02 -7.695672E+01 1.765933E+01 -3.933154E+01 6.011389E+01 -5.377078E+01 4.682012E+01 -3.231265E+01 + 1.899080E+01 -6.561426E+00 -1.198988E+01 5.795616E+00 2.639701E+00 -6.377897E+00 -1.458270E+01 1.939372E+01 4.531745E+01 -7.013503E+01 + 7.840399E+01 -5.540114E+01 1.060661E+01 -6.289285E+01 9.679529E+01 -9.015462E+01 2.408921E+01 -3.752975E+01 7.926256E+01 -8.128112E+01 + 1.679471E+02 -5.727137E+02 1.307071E+03 -3.427871E+03 9.507863E+03 -2.082101E+04 3.477043E+04 -3.566039E+04 2.229385E+04 -1.159551E+04 + 6.785542E+03 -4.937844E+03 4.347942E+03 -3.599663E+03 1.791710E+03 3.199790E+02 1.615822E+03 -1.013607E+04 6.231908E+03 -9.443004E+03 + 3.818221E+03 7.329014E+03 -1.109442E+04 1.920800E+04 -5.442757E+03 9.977680E+03 -6.968929E+03 3.644791E+04 -9.774169E+03 3.784470E+04 + 3.574080E+05 2.349607E+04 1.387814E+04 1.057100E+02 -6.729367E+02 -5.497272E+04 1.624658E+04 3.857818E+03 -8.561447E+03 3.775457E+02 + 1.114083E+09 + 108 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 8.413633E+01 -1.658491E+02 6.205376E+01 4.385611E+01 1.671182E+01 -4.571282E+01 2.920771E+01 -4.469828E+01 4.612068E+01 + -2.990154E+01 1.795046E+01 1.951412E+01 -9.962136E+00 5.052203E-01 -4.642804E+00 2.992763E+01 -1.799000E+01 -5.332094E+01 6.557498E+01 + -5.654248E+01 5.024480E+01 9.511024E+00 1.426153E+00 -1.922549E+01 8.711550E+01 -8.449694E+01 1.124931E+02 -1.604462E+02 1.736784E+01 + 4.009643E+01 2.768984E+02 -6.022675E+02 1.690303E+03 -5.896846E+03 1.505938E+04 -3.566039E+04 6.061426E+04 -5.493473E+04 3.478746E+04 + -2.208425E+04 1.252843E+04 -6.805165E+03 3.303508E+03 -5.393665E+03 2.156257E+03 1.698068E+03 7.306570E+03 5.197350E+03 3.695104E+01 + 2.138878E+03 1.106819E+03 -5.116459E+03 -2.576197E+04 9.428965E+03 -2.241595E+04 1.885128E+04 -5.731015E+04 2.314350E+04 -2.748209E+04 + -2.196748E+05 -2.672730E+04 -1.832945E+04 4.306352E+02 2.121902E+04 7.343047E+04 -1.635707E+04 -1.468648E+04 1.668930E+04 2.748673E+03 + -3.012060E+09 + 109 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.093829E+01 9.168908E+01 -1.542799E+01 -3.049214E+01 -1.609567E+01 -1.929634E+01 3.408403E+01 1.322876E+01 -4.459806E+01 + 2.566504E+01 -2.034816E+00 -2.441105E+01 1.005049E+01 -1.188666E+01 3.763986E+01 -6.854449E+01 1.251581E+01 9.484132E+01 -1.196706E+02 + 6.630922E+01 -1.311280E+01 -4.778706E+01 6.399490E+01 -1.150765E+02 5.562983E+01 2.640266E+01 -1.742360E+02 2.597761E+02 4.652798E+01 + -2.690713E+02 5.666461E+01 -6.649571E+01 -2.871655E+02 2.372490E+03 -7.463005E+03 2.229385E+04 -5.493473E+04 8.919272E+04 -8.447598E+04 + 5.854169E+04 -3.362605E+04 1.503935E+04 -6.202718E+03 4.967419E+03 -7.213271E+03 1.157610E+04 2.427043E+03 -7.155101E+03 4.554230E+03 + -1.984596E+04 -7.063765E+03 1.270084E+04 6.202679E+04 -3.529787E+04 6.134598E+04 -1.397010E+04 5.413919E+04 1.838647E+03 7.582645E+03 + -1.854192E+05 5.156782E+04 -1.620782E+03 2.370353E+03 -3.993623E+04 -3.504371E+04 9.668291E+03 1.403200E+04 -2.536559E+04 -7.703866E+03 + 1.002104E+10 + 110 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 6.465635E+01 -1.017004E+02 -3.719950E+01 5.032457E+01 -8.494583E-01 4.792150E+01 -2.547056E+01 -1.186324E+01 3.554608E+01 + -9.905470E-01 -2.897627E+01 2.405279E+01 1.007400E+01 -4.352794E-01 -6.148391E+01 1.020366E+02 -3.253140E+01 -7.777597E+01 6.555568E+01 + 3.168245E+01 -1.072804E+02 1.880140E+02 -1.919254E+02 1.983184E+02 -1.557928E+02 6.368245E+01 1.846324E+02 -3.800792E+02 4.917337E+01 + 2.697420E+02 -1.321390E+02 1.480216E+02 -5.851806E+01 -1.030804E+03 3.087608E+03 -1.159551E+04 3.478746E+04 -8.447598E+04 1.463851E+05 + -1.435462E+05 8.853916E+04 -4.203614E+04 1.585483E+04 -1.309024E+03 4.435272E+03 -3.028918E+04 1.883168E+04 -1.528505E+04 -1.326544E+04 + 5.924725E+04 1.937681E+04 -2.811432E+04 -3.632060E+04 6.797263E+03 -6.747133E+04 -9.924622E+03 -6.005646E+04 -4.165559E+04 -1.133617E+04 + 3.146714E+05 -5.101146E+04 4.165988E+03 -1.560292E+04 7.455487E+04 2.539224E+04 -2.593297E+04 2.552111E+03 1.237207E+04 1.815965E+04 + -8.692603E+09 + 111 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.080704E+02 2.179319E+02 -5.654129E+01 8.059153E+00 -5.495353E+01 -3.273833E+01 4.767133E+01 -1.224260E+01 -2.751701E+00 + -3.820741E+01 7.750346E+01 -5.190571E+01 -2.708293E+01 2.899344E+01 3.079511E+01 -6.734600E+01 6.184092E+01 -2.574220E+01 1.229770E+02 + -2.337926E+02 2.409757E+02 -2.866588E+02 2.806128E+02 -2.581330E+02 2.597725E+02 -2.021984E+02 1.294984E+00 1.478309E+02 7.071473E+00 + -7.322887E+01 -1.521364E+02 4.072172E+02 -5.449924E+02 1.245857E+03 -1.549632E+03 6.785542E+03 -2.208425E+04 5.854169E+04 -1.435462E+05 + 2.617920E+05 -2.370739E+05 1.251343E+05 -4.049551E+04 5.562755E+03 1.761355E+04 1.993152E+04 -1.645880E+04 1.753718E+04 1.853284E+04 + -3.726266E+04 -4.475056E+04 2.651406E+04 6.352309E+03 5.725866E+03 1.505803E+05 -7.323541E+04 5.248992E+04 1.174233E+05 1.198366E+05 + -5.122135E+05 3.375563E+04 1.508970E+04 1.075372E+04 -6.958571E+04 -6.152323E+04 5.860180E+04 -1.737577E+04 -1.108584E+04 -8.774213E+03 + -8.462974E+10 + 112 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.437774E+02 -6.191528E+02 5.573138E+02 -3.119245E+02 1.364818E+02 1.373011E+01 -9.229858E+01 8.205678E+01 -4.465727E+01 + 5.928145E+01 -5.800571E+01 5.407114E+01 2.443991E+01 -7.297177E+01 3.070393E+01 1.343328E+01 -5.977151E+01 2.043962E+01 -6.527047E+01 + 2.213785E+02 -2.675384E+02 3.088751E+02 -2.979407E+02 2.255105E+02 -1.723572E+02 1.041393E+02 1.826946E+02 -2.574662E+02 1.009101E+02 + -3.457939E+02 4.317467E+02 -7.793935E+02 8.937173E+02 -1.582171E+03 1.859023E+03 -4.937844E+03 1.252843E+04 -3.362605E+04 8.853916E+04 + -2.370739E+05 4.138626E+05 -3.563310E+05 1.466851E+05 -4.220322E+04 -1.557636E+04 -2.697365E+04 -8.898827E+03 1.150964E+04 -6.930535E+03 + 3.908168E+04 1.976478E+04 -2.811565E+04 1.076257E+05 -6.253700E+04 -1.215916E+05 2.010245E+05 -6.290508E+04 -5.652143E+04 -1.566743E+05 + 2.966560E+05 -6.870128E+04 -7.462583E+04 -1.358843E+04 3.515120E+04 1.386169E+05 -3.355956E+04 -2.941697E+03 2.890001E+03 -8.411687E+02 + 1.159599E+11 + 113 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.713667E+02 3.873667E+02 -3.010289E+02 -3.361770E+01 2.421984E+02 -1.361210E+02 9.878430E+01 -1.178078E+02 7.043140E+01 + -2.836668E+01 2.342182E+01 -3.640874E+01 -3.755317E+01 1.106191E+02 -8.706575E+01 2.621722E+01 6.773394E+01 -6.915070E+01 8.064697E+01 + -3.003347E+02 5.520550E+02 -6.622840E+02 5.483046E+02 -2.967007E+02 2.151763E+02 -3.552667E+02 5.268938E+02 -6.360023E+02 9.869597E+01 + 4.766008E+02 -5.792450E+02 2.338998E+02 -4.720516E+02 2.791596E+03 -3.710771E+03 4.347942E+03 -6.805165E+03 1.503935E+04 -4.203614E+04 + 1.251343E+05 -3.563310E+05 7.631497E+05 -5.295304E+05 2.081696E+05 -8.177414E+04 7.091989E+04 1.917252E+04 -9.454146E+04 -3.832783E+04 + -4.354890E+04 3.017060E+04 1.080953E+05 -2.299025E+05 8.409400E+04 -3.347995E+04 -3.262627E+05 3.053930E+04 -8.602486E+04 -2.629787E+04 + 3.608207E+05 9.598552E+04 1.010932E+05 1.830872E+04 1.079383E+05 -1.542905E+05 -1.443977E+05 9.566868E+04 -3.287147E+04 6.775555E+03 + 1.673701E+10 + 114 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.030893E+02 6.414994E+02 -6.256207E+02 4.021932E+02 -3.230602E+02 1.608533E+01 1.249040E+02 -1.034205E+02 8.517630E+01 + -8.871079E+01 -2.333663E+01 8.527706E+01 -1.187053E+01 1.911869E+01 -3.547234E+01 -5.830541E+01 4.576354E+01 1.080256E+02 -2.554468E+02 + 4.976426E+02 -7.659460E+02 9.511535E+02 -9.888182E+02 6.209706E+02 -2.943317E+02 7.541575E+02 -1.098582E+03 6.612421E+02 8.118448E+01 + -4.334509E+02 4.444513E+02 -9.695447E+01 4.590004E+02 -2.324027E+03 3.538105E+03 -3.599663E+03 3.303508E+03 -6.202718E+03 1.585483E+04 + -4.049551E+04 1.466851E+05 -5.295304E+05 1.090381E+06 -8.250880E+05 3.520007E+05 -1.339306E+05 1.068179E+04 7.065710E+04 4.862340E+04 + -8.392546E+04 7.102966E+04 -1.108816E+05 3.302494E+05 -2.059207E+05 3.559402E+05 -5.921876E+03 4.927468E+04 1.057699E+05 5.589569E+04 + -3.904099E+05 -1.310900E+05 5.082769E+04 -8.988574E+04 -6.303368E+05 1.145240E+05 1.642667E+05 -3.779156E+05 1.553554E+05 1.057538E+04 + -1.924465E+11 + 115 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 3.995678E+02 -7.192082E+02 3.321300E+02 -4.199192E+02 5.642795E+02 2.865304E+01 -1.751088E+02 2.380922E+00 7.801976E+01 + 3.263125E+01 -4.026920E+00 1.865248E+01 -7.109723E+01 -3.937488E+01 5.164596E+01 1.427201E+02 -2.026224E+02 -4.381358E+01 1.585684E+02 + -3.551773E+02 7.764586E+02 -1.101318E+03 1.253299E+03 -9.084277E+02 3.424213E+02 -3.691649E+02 5.857535E+02 -2.027783E+02 -9.886951E+01 + -4.146773E+02 9.269984E+02 -3.144294E+02 -8.555291E+02 1.685755E+03 -2.154028E+03 1.791710E+03 -5.393665E+03 4.967419E+03 -1.309024E+03 + 5.562755E+03 -4.220322E+04 2.081696E+05 -8.250880E+05 2.307614E+06 -1.965100E+06 4.567864E+05 -2.967367E+05 1.851115E+05 -2.143345E+05 + 1.532986E+03 -8.435742E+04 1.005062E+05 1.705104E+05 2.395381E+04 -2.565007E+04 1.273285E+05 1.198199E+05 -1.654682E+05 1.648854E+05 + 2.715822E+05 6.058282E+04 4.957458E+04 7.967364E+04 6.634150E+05 -3.733252E+05 -2.847974E+05 4.975238E+05 -2.005411E+05 2.063016E+04 + 6.538914E+10 + 116 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -9.010548E+01 6.297562E+01 1.363142E+02 7.616047E+02 -1.471270E+03 5.557204E+02 4.183421E+02 -4.947038E+02 2.595690E+02 + -1.599764E+02 4.257016E+01 -1.576740E+02 2.231117E+01 2.019136E+02 -8.868149E+00 -1.374637E+02 2.264926E+02 -3.707917E+02 5.579850E+02 + -4.562856E+02 -3.845017E+02 1.099799E+03 -1.192979E+03 1.008553E+03 -8.720029E+02 2.696011E+02 1.000500E+03 -1.932482E+03 1.960044E+03 + -1.249841E+03 1.136971E+03 -2.723808E+03 4.847863E+03 -3.305609E+03 9.553412E+02 3.199790E+02 2.156257E+03 -7.213271E+03 4.435272E+03 + 1.761355E+04 -1.557636E+04 -8.177414E+04 3.520007E+05 -1.965100E+06 7.076166E+06 -4.247473E+06 1.336031E+06 -8.586341E+05 9.107670E+05 + -3.234725E+05 -1.045505E+05 6.919090E+05 -2.279994E+05 -1.249889E+05 -4.067111E+05 3.249743E+04 -4.675272E+04 -1.692143E+05 2.520479E+05 + -1.537918E+06 -8.712475E+04 -1.105898E+05 -1.478202E+05 -1.104642E+06 3.513424E+05 5.853673E+05 -3.186263E+03 4.965395E+04 -5.356799E+04 + -2.122148E+11 + 117 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.675195E+02 2.033696E+02 -1.273010E+03 -4.912667E+02 2.254301E+03 -1.204081E+03 -4.469208E+02 6.006642E+02 -3.720180E+02 + 1.188548E+02 -1.042048E+02 3.476188E+02 -2.653760E+02 -1.367736E+02 1.587682E+02 -1.065675E+02 1.263814E+00 4.040712E+02 -9.625110E+02 + 9.003467E+02 -2.979340E+02 -1.142643E+02 4.705099E+02 -9.252016E+01 -7.229062E+01 5.245712E+02 -2.495206E+03 2.919287E+03 -1.491596E+03 + 1.627435E+03 -3.096318E+03 2.906638E+03 -2.019744E+03 -4.804355E+02 -1.894558E+02 1.615822E+03 1.698068E+03 1.157610E+04 -3.028918E+04 + 1.993152E+04 -2.697365E+04 7.091989E+04 -1.339306E+05 4.567864E+05 -4.247473E+06 1.732607E+07 -1.111645E+07 4.799292E+06 -3.665180E+06 + 3.479208E+05 -3.615673E+05 3.045326E+05 1.393280E+06 -7.906610E+05 1.248310E+06 -4.081326E+05 8.497277E+05 -3.296869E+05 9.804462E+05 + 4.637155E+06 2.729531E+05 4.818398E+05 2.393332E+05 -9.467072E+05 3.316616E+06 -9.252444E+05 4.197095E+04 -2.116240E+05 -3.779218E+04 + 6.075234E+11 + 118 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.048927E+03 -2.232787E+03 2.011489E+03 -9.710768E+02 -5.694646E+02 -7.076476E+02 1.046518E+03 -1.125637E+02 -5.223512E+02 + 7.571306E+02 -7.768166E+02 5.579626E+02 -3.437633E+02 5.679724E+02 -7.831128E+02 2.121592E+02 4.965564E+02 -1.797536E+03 2.720324E+03 + -2.436866E+03 1.267241E+03 -6.839531E+02 3.047257E+02 -9.513602E+02 1.531079E+03 -2.476672E+03 4.149334E+03 -4.109544E+03 3.584509E+02 + 8.633332E+02 2.449657E+01 -2.011099E+02 5.918410E+02 3.764800E+02 2.032682E+03 -1.013607E+04 7.306570E+03 2.427043E+03 1.883168E+04 + -1.645880E+04 -8.898827E+03 1.917252E+04 1.068179E+04 -2.967367E+05 1.336031E+06 -1.111645E+07 4.502644E+07 -2.658980E+07 6.776228E+06 + -1.782246E+06 5.292605E+05 8.480108E+05 1.389748E+06 -7.164934E+05 3.609430E+05 7.579810E+05 -1.347830E+06 5.542356E+05 -1.426691E+06 + 5.468401E+06 -2.092297E+06 4.470947E+05 -8.660768E+05 -4.023032E+05 1.441286E+06 -1.099476E+06 4.676035E+05 -6.197327E+04 1.344322E+05 + -4.235944E+11 + 119 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.063898E+02 8.711249E+02 -3.286721E+03 3.437310E+03 -4.728791E+02 2.567676E+02 3.683890E+02 -1.063278E+03 1.190788E+03 + -1.001882E+03 5.633448E+02 4.745729E+01 3.034715E+02 -5.884688E+02 6.504004E+01 1.011467E+03 -1.396098E+03 1.485949E+03 -1.323865E+03 + 1.259464E+03 -1.200785E+03 1.507580E+03 -1.213946E+03 1.820485E+03 -1.687171E+03 3.013668E+03 -4.710545E+03 2.549519E+03 2.516756E+03 + -6.988961E+03 7.019237E+03 3.855983E+01 -9.068905E+03 8.931424E+03 -7.560251E+03 6.231908E+03 5.197350E+03 -7.155101E+03 -1.528505E+04 + 1.753718E+04 1.150964E+04 -9.454146E+04 7.065710E+04 1.851115E+05 -8.586341E+05 4.799292E+06 -2.658980E+07 7.976807E+07 -1.426567E+07 + 3.171942E+06 -1.149664E+06 4.449488E+06 3.804804E+06 -2.982068E+06 4.465505E+06 -2.940136E+06 3.301498E+06 -1.708998E+06 1.555608E+06 + 3.779249E+06 2.648472E+06 1.384006E+06 7.094230E+05 3.731768E+06 -3.874933E+06 1.057209E+06 -2.570734E+05 4.290236E+05 -5.604270E+05 + -2.279375E+11 + 120 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 3.549380E+02 -1.685486E+03 2.054575E+03 6.784237E+02 -2.563145E+03 1.633813E+03 -1.350212E+02 -3.539815E+02 3.517942E+02 + -1.466402E+02 6.740327E+02 -8.563759E+02 4.995640E+01 6.014684E+02 -8.033231E+02 4.298330E+02 1.521731E+02 -7.496807E+02 6.946852E+02 + 2.513122E+01 -6.115292E+02 4.717284E+02 1.892474E+02 -1.644310E+03 1.268959E+03 -5.827616E+02 -4.786450E+02 -2.605669E+02 2.263174E+03 + -5.531198E+03 5.937058E+03 -6.202034E+03 8.948879E+03 -9.430694E+03 1.022638E+04 -9.443004E+03 3.695104E+01 4.554230E+03 -1.326544E+04 + 1.853284E+04 -6.930535E+03 -3.832783E+04 4.862340E+04 -2.143345E+05 9.107670E+05 -3.665180E+06 6.776228E+06 -1.426567E+07 6.371492E+07 + -2.479632E+07 9.135517E+06 -8.499121E+05 4.061525E+06 -3.878696E+06 6.805087E+05 -5.316588E+05 -1.085719E+06 3.140536E+05 -1.056661E+06 + 7.354798E+06 -1.003308E+06 -8.834036E+05 -9.947365E+05 8.473115E+06 -3.280633E+06 2.095535E+06 1.804336E+06 -4.622034E+05 2.204521E+05 + -2.624262E+11 + 121 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.481237E+02 5.498518E+02 -5.343163E+02 1.180893E+03 -2.719368E+02 -2.785508E+02 5.996035E+02 -5.323954E+02 3.084557E+02 + -5.674572E+02 3.981420E+02 -2.479214E+02 2.594298E+02 -8.964281E+02 1.349233E+03 -7.531564E+02 -3.608352E+01 3.945972E+01 8.307456E+02 + -2.212988E+03 2.451818E+03 -1.632085E+03 9.500870E+02 -4.591505E+02 2.030339E+02 -1.101753E+03 1.886057E+03 -1.154324E+03 1.487551E+03 + -2.758844E+03 4.158818E+03 -6.508422E+03 3.422432E+03 6.072884E+03 -8.853881E+03 3.818221E+03 2.138878E+03 -1.984596E+04 5.924725E+04 + -3.726266E+04 3.908168E+04 -4.354890E+04 -8.392546E+04 1.532986E+03 -3.234725E+05 3.479208E+05 -1.782246E+06 3.171942E+06 -2.479632E+07 + 1.470796E+08 -5.602553E+07 3.054364E+07 -1.434874E+07 1.483463E+07 -6.725838E+05 2.052359E+06 -1.297195E+06 6.554868E+05 1.762967E+06 + -8.617058E+06 8.482109E+06 4.379632E+05 3.267703E+06 -2.554183E+06 -4.572318E+06 7.952671E+06 -2.068831E+06 2.180628E+06 -1.141030E+06 + 1.012992E+12 + 122 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.706708E+03 1.840967E+03 7.895544E+02 -2.023578E+03 1.765321E+03 -4.551936E+02 -9.462874E+02 7.595648E+02 -4.696465E+02 + 3.569310E+02 -1.932277E+02 4.854490E+02 -8.346611E+02 6.065833E+02 -1.242722E+03 1.909679E+03 -1.007359E+03 8.047976E+02 -1.589922E+03 + 3.372097E+03 -2.457332E+03 -4.294224E+01 8.365032E+02 -1.573291E+02 -6.416263E+02 9.504947E+02 -1.759436E+02 -7.393710E+02 1.051045E+03 + 2.416336E+03 -3.633289E+03 1.718018E+03 1.162868E+03 -2.244788E+03 -1.900394E+03 7.329014E+03 1.106819E+03 -7.063765E+03 1.937681E+04 + -4.475056E+04 1.976478E+04 3.017060E+04 7.102966E+04 -8.435742E+04 -1.045505E+05 -3.615673E+05 5.292605E+05 -1.149664E+06 9.135517E+06 + -5.602553E+07 1.427645E+08 -1.177958E+08 2.785186E+07 -3.336127E+07 -1.356088E+07 2.724347E+07 9.555448E+05 1.661597E+06 8.193541E+05 + 1.410408E+07 1.372408E+05 7.298288E+05 -6.825691E+05 -8.307800E+06 5.553245E+06 -7.873235E+06 -2.759831E+06 9.405461E+04 2.405264E+05 + -9.481667E+11 + 123 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -6.097746E+02 8.386602E+01 1.271654E+03 3.664475E+02 9.882914E+02 -2.305714E+03 1.412112E+03 1.530226E+02 -1.139909E+03 + 5.537898E+02 -3.345794E+02 -8.830265E+02 2.612093E+01 4.654506E+01 6.107364E+02 -1.398163E+03 1.120353E+03 -6.835592E+02 1.931032E+03 + -2.463990E+03 1.620024E+03 -6.482634E+02 6.582816E+02 -4.679929E+02 -1.581494E+02 -1.312607E+03 9.349801E+02 -2.227067E+02 3.880193E+03 + -1.046839E+04 1.373261E+04 -4.694399E+03 -3.561992E+03 1.629450E+04 -8.084659E+02 -1.109442E+04 -5.116459E+03 1.270084E+04 -2.811432E+04 + 2.651406E+04 -2.811565E+04 1.080953E+05 -1.108816E+05 1.005062E+05 6.919090E+05 3.045326E+05 8.480108E+05 4.449488E+06 -8.499121E+05 + 3.054364E+07 -1.177958E+08 3.351913E+08 -2.039977E+08 1.436869E+08 -9.391057E+07 -4.565212E+07 -1.348450E+07 -8.881220E+06 -1.551757E+07 + 2.819273E+07 -7.697120E+06 -4.103683E+06 -1.273379E+06 2.796715E+07 -2.253728E+05 1.859086E+06 2.917231E+06 -1.703621E+06 -1.611880E+06 + 2.678100E+10 + 124 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.768282E+03 -6.382231E+03 7.516226E+03 -8.944481E+03 4.194828E+03 -1.138245E+03 6.777181E+02 -6.411733E+02 1.177479E+03 + -6.437408E+02 -3.052495E+01 -1.099942E+02 3.424240E+02 1.868111E+01 8.491198E+02 1.664777E+02 -4.767761E+02 8.076672E+02 -1.541646E+03 + 9.351094E+02 4.087948E+02 -9.696427E+02 1.070160E+03 -9.307330E+02 1.137017E+03 2.851948E+03 -5.258676E+03 6.013134E+03 7.872967E+02 + -9.894930E+02 1.014089E+03 -1.913707E+03 6.510539E+03 -3.618937E+03 5.226341E+03 1.920800E+04 -2.576197E+04 6.202679E+04 -3.632060E+04 + 6.352309E+03 1.076257E+05 -2.299025E+05 3.302494E+05 1.705104E+05 -2.279994E+05 1.393280E+06 1.389748E+06 3.804804E+06 4.061525E+06 + -1.434874E+07 2.785186E+07 -2.039977E+08 5.182691E+08 -3.448344E+08 3.123403E+08 -1.851438E+06 4.370298E+07 1.347523E+07 3.693046E+07 + -1.475032E+07 1.349462E+07 7.147589E+06 1.924488E+06 -4.256738E+07 1.483183E+07 2.459398E+05 -7.210895E+06 1.731683E+06 2.929526E+06 + -8.446832E+10 + 125 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.744075E+03 3.634849E+03 -1.863104E+03 1.572318E+03 -2.447281E+03 2.940221E+03 -1.777022E+03 1.112077E+03 -1.443818E+03 + 9.151237E+02 3.524881E+02 -6.509701E+02 -7.390996E+01 5.000407E+02 -7.974214E+02 -1.354087E+02 1.659316E+02 -7.577622E+02 1.772129E+03 + -1.124162E+03 -2.073042E+02 1.122998E+03 -9.953057E+02 1.344160E+03 -9.176288E+02 -2.321530E+03 3.544903E+03 -4.121102E+03 -1.044142E+03 + -1.649977E+03 5.105909E+03 -3.845611E+03 -5.735454E+03 9.345270E+03 -1.408883E+04 -5.442757E+03 9.428965E+03 -3.529787E+04 6.797263E+03 + 5.725866E+03 -6.253700E+04 8.409400E+04 -2.059207E+05 2.395381E+04 -1.249889E+05 -7.906610E+05 -7.164934E+05 -2.982068E+06 -3.878696E+06 + 1.483463E+07 -3.336127E+07 1.436869E+08 -3.448344E+08 8.317386E+08 -4.815174E+08 2.952800E+08 -2.477233E+07 2.740638E+07 4.964179E+07 + -8.276801E+07 8.166857E+06 -5.823062E+06 2.012748E+06 1.656708E+07 9.527296E+06 1.450236E+07 1.250172E+07 -4.066486E+06 -3.011816E+06 + 1.733338E+12 + 126 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.326090E+04 -2.407710E+04 2.035020E+04 -1.589302E+04 8.258280E+03 1.444152E+02 -1.799845E+03 -1.177380E+02 2.854292E+03 + -2.611014E+03 -4.252482E+02 1.285418E+03 -5.772481E+01 -3.267416E+02 1.048165E+03 -7.738333E+02 -7.037788E+02 1.975459E+03 -1.856915E+03 + 2.065090E+02 1.525626E+03 -7.603348E+02 -1.233953E+03 3.647308E+01 8.756198E+02 4.187915E+03 -5.608160E+03 4.718467E+03 4.796680E+03 + -1.849043E+03 -2.539978E+03 2.806181E+03 6.720452E+03 -9.087637E+03 1.463417E+04 9.977680E+03 -2.241595E+04 6.134598E+04 -6.747133E+04 + 1.505803E+05 -1.215916E+05 -3.347995E+04 3.559402E+05 -2.565007E+04 -4.067111E+05 1.248310E+06 3.609430E+05 4.465505E+06 6.805087E+05 + -6.725838E+05 -1.356088E+07 -9.391057E+07 3.123403E+08 -4.815174E+08 1.972945E+09 -8.421806E+08 3.846679E+08 -9.316554E+06 1.479965E+08 + -4.813416E+07 4.305821E+07 1.025726E+07 -1.276544E+07 -1.810573E+07 2.980831E+07 -4.463714E+06 -2.403054E+07 6.186527E+06 4.641694E+05 + -1.479694E+12 + 127 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -8.090915E+02 4.819792E+03 -5.478936E+03 3.714318E+03 -4.116820E+03 4.540057E+02 2.220686E+03 -6.956694E+02 -3.138850E+03 + 2.681322E+03 1.085532E+03 -1.760620E+03 -7.526878E+02 1.503791E+03 -8.344604E+02 -2.334910E+02 7.249655E+02 -2.405668E+03 3.808422E+03 + -8.522706E+02 -2.226890E+03 -2.989322E+02 4.494287E+03 -5.469199E+03 5.139020E+03 -5.758644E+03 4.011859E+03 -8.521180E+02 -6.866918E+03 + -2.032758E+03 1.018023E+04 -1.267638E+04 -3.868093E+02 5.030482E+03 -1.202052E+04 -6.968929E+03 1.885128E+04 -1.397010E+04 -9.924622E+03 + -7.323541E+04 2.010245E+05 -3.262627E+05 -5.921876E+03 1.273285E+05 3.249743E+04 -4.081326E+05 7.579810E+05 -2.940136E+06 -5.316588E+05 + 2.052359E+06 2.724347E+07 -4.565212E+07 -1.851438E+06 2.952800E+08 -8.421806E+08 1.863245E+09 -4.454483E+08 4.605416E+08 1.903043E+08 + -5.082827E+08 8.760660E+07 5.333651E+06 2.874725E+07 5.850362E+07 4.944091E+07 -4.482593E+06 2.789360E+06 7.444649E+06 -8.632319E+05 + -3.873666E+12 + 128 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.248934E+03 -1.554944E+03 -3.911369E+02 2.387914E+02 -3.141856E+03 4.847824E+03 -1.990134E+03 -8.577359E+02 2.807436E+03 + -2.569549E+03 3.547022E+02 2.405706E+01 6.490640E+02 4.027494E+02 -1.065724E+03 1.902875E+03 -2.352010E+03 3.016802E+03 -3.420762E+03 + 3.845628E+03 -7.555279E+02 -1.417238E+03 -9.184572E+02 3.608453E+03 -4.436476E+03 8.142820E+03 -9.252584E+03 4.985569E+03 3.515638E+03 + -5.400278E+03 7.005496E+03 -5.702506E+03 6.187447E+03 -3.240025E+03 -1.132682E+04 3.644791E+04 -5.731015E+04 5.413919E+04 -6.005646E+04 + 5.248992E+04 -6.290508E+04 3.053930E+04 4.927468E+04 1.198199E+05 -4.675272E+04 8.497277E+05 -1.347830E+06 3.301498E+06 -1.085719E+06 + -1.297195E+06 9.555448E+05 -1.348450E+07 4.370298E+07 -2.477233E+07 3.846679E+08 -4.454483E+08 9.511723E+08 -3.492863E+08 4.845241E+08 + -1.943194E+08 7.133198E+07 2.141176E+06 4.746761E+06 -2.320897E+07 -5.677230E+06 -1.424618E+07 -5.465396E+06 -4.911494E+06 1.059325E+06 + 3.030551E+12 + 129 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.467535E+03 -6.908639E+03 4.496364E+03 -1.241980E+03 -1.952293E+03 3.524046E+03 -2.277200E+03 1.282698E+03 -6.951588E+02 + 2.451814E+02 -3.878070E+02 1.017051E+03 -1.539542E+03 5.756294E+02 4.645219E+02 -2.128684E+03 1.809738E+03 -2.424053E+03 3.563480E+03 + -2.794184E+03 5.501128E+02 -2.129386E+02 2.816552E+03 -4.608989E+03 4.157293E+03 -4.111819E+03 5.711387E+03 -4.304232E+03 8.481593E+02 + -2.240766E+03 6.386590E+02 2.093041E+03 -5.695548E+03 5.649041E+03 1.941497E+03 -9.774169E+03 2.314350E+04 1.838647E+03 -4.165559E+04 + 1.174233E+05 -5.652143E+04 -8.602486E+04 1.057699E+05 -1.654682E+05 -1.692143E+05 -3.296869E+05 5.542356E+05 -1.708998E+06 3.140536E+05 + 6.554868E+05 1.661597E+06 -8.881220E+06 1.347523E+07 2.740638E+07 -9.316554E+06 4.605416E+08 -3.492863E+08 6.217373E+08 -2.146999E+07 + -4.423984E+08 1.290388E+08 -2.779579E+06 4.814580E+06 -6.571604E+06 8.670852E+06 -1.582210E+07 -5.232665E+06 2.109740E+06 1.827773E+06 + -4.257134E+12 + 130 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.859890E+03 -5.349845E+03 4.544480E+03 -5.829398E+03 -2.869759E+02 4.533918E+03 -9.295022E+02 -3.212375E+03 3.081125E+03 + -3.947339E+03 3.041348E+03 -3.540252E+03 3.058790E+03 -1.195217E+03 -4.901561E+02 1.006019E+03 -2.668892E+03 4.007723E+03 -2.401072E+03 + 1.161228E+02 4.415566E+03 -8.369840E+03 7.888855E+03 -5.781827E+03 4.520904E+03 -2.595277E+03 -1.222014E+03 -2.877523E+02 9.838136E+02 + -3.839471E+03 1.235814E+04 -1.050182E+04 9.579348E+03 1.602988E+04 -3.733686E+04 3.784470E+04 -2.748209E+04 7.582645E+03 -1.133617E+04 + 1.198366E+05 -1.566743E+05 -2.629787E+04 5.589569E+04 1.648854E+05 2.520479E+05 9.804462E+05 -1.426691E+06 1.555608E+06 -1.056661E+06 + 1.762967E+06 8.193541E+05 -1.551757E+07 3.693046E+07 4.964179E+07 1.479965E+08 1.903043E+08 4.845241E+08 -2.146999E+07 1.873170E+09 + -9.108258E+08 6.771445E+08 -7.511338E+07 4.325716E+07 1.186946E+08 1.982408E+07 -7.061702E+07 9.352964E+06 2.924540E+06 -2.733769E+06 + -2.396878E+12 + 131 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.250335E+03 2.866392E+04 -7.958970E+04 5.249225E+04 -2.474059E+03 -6.771800E+03 -8.545794E+03 2.622538E+04 -2.295758E+04 + 1.172639E+04 -2.661104E+03 5.648071E+03 -2.526685E+03 -9.733367E+03 1.002584E+04 -9.580391E+03 1.014587E+04 -1.481114E+04 -1.146017E+04 + 8.486509E+03 2.184196E+04 -1.937794E+04 1.206479E+04 1.028261E+03 -3.577195E+04 5.012212E+04 -7.505071E+04 6.986396E+04 -3.039314E+04 + 8.278140E+03 -5.246891E+03 -1.537540E+04 -5.112550E+04 1.780186E+05 -2.441010E+05 3.574080E+05 -2.196748E+05 -1.854192E+05 3.146714E+05 + -5.122135E+05 2.966560E+05 3.608207E+05 -3.904099E+05 2.715822E+05 -1.537918E+06 4.637155E+06 5.468401E+06 3.779249E+06 7.354798E+06 + -8.617058E+06 1.410408E+07 2.819273E+07 -1.475032E+07 -8.276801E+07 -4.813416E+07 -5.082827E+08 -1.943194E+08 -4.423984E+08 -9.108258E+08 + 4.911082E+10 -1.368372E+09 9.127207E+08 -1.419558E+08 -3.656681E+08 -9.932873E+07 1.107018E+08 7.061885E+07 -7.120414E+07 -5.611240E+06 + 4.889515E+12 + 132 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.405910E+03 -1.152468E+04 1.696783E+04 -1.548132E+04 2.916328E+03 2.818919E+03 -1.948242E+02 -2.795300E+03 3.004354E+03 + -1.959302E+03 2.187730E+03 -1.625398E+03 1.200293E+02 2.494543E+02 -1.367742E+02 1.134181E+03 -2.982246E+03 2.000902E+03 -1.580566E+03 + 1.379401E+03 -4.199015E+02 -1.707127E+03 7.891339E+02 -9.080000E+02 4.985232E+03 -6.929915E+03 5.717467E+03 -2.764774E+03 -3.478752E+03 + 5.333082E+03 -9.018704E+03 1.198711E+04 -7.809577E+03 7.575872E+03 -5.920546E+03 2.349607E+04 -2.672730E+04 5.156782E+04 -5.101146E+04 + 3.375563E+04 -6.870128E+04 9.598552E+04 -1.310900E+05 6.058282E+04 -8.712475E+04 2.729531E+05 -2.092297E+06 2.648472E+06 -1.003308E+06 + 8.482109E+06 1.372408E+05 -7.697120E+06 1.349462E+07 8.166857E+06 4.305821E+07 8.760660E+07 7.133198E+07 1.290388E+08 6.771445E+08 + -1.368372E+09 2.062621E+09 -7.749725E+07 2.353051E+08 2.384668E+08 1.547599E+07 -4.880243E+07 3.229019E+07 1.614308E+07 -5.889744E+06 + -6.587993E+12 + 133 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 8.946998E+02 2.934136E+03 -3.743180E+03 -3.230146E+02 1.372272E+03 1.740361E+02 -9.588000E+02 5.202617E+02 -5.063971E+02 + -4.163879E+02 1.580525E+03 -6.088552E+02 -8.703816E+02 1.072781E+03 -1.571755E+03 1.697238E+03 -1.448081E+03 6.480028E+02 -3.003532E+02 + 1.056180E+03 -8.638493E-01 -1.277504E+03 2.213146E+01 3.115216E+03 -3.637981E+03 1.114545E+03 2.435944E+02 2.256870E+01 -1.009503E+03 + 2.835598E+02 2.999527E+03 -4.691786E+03 1.552378E+03 8.914978E+03 -1.709306E+04 1.387814E+04 -1.832945E+04 -1.620782E+03 4.165988E+03 + 1.508970E+04 -7.462583E+04 1.010932E+05 5.082769E+04 4.957458E+04 -1.105898E+05 4.818398E+05 4.470947E+05 1.384006E+06 -8.834036E+05 + 4.379632E+05 7.298288E+05 -4.103683E+06 7.147589E+06 -5.823062E+06 1.025726E+07 5.333651E+06 2.141176E+06 -2.779579E+06 -7.511338E+07 + 9.127207E+08 -7.749725E+07 6.543079E+08 -4.259588E+07 -4.599991E+08 9.869202E+05 -1.362109E+07 -3.405684E+07 -2.682767E+06 -2.381573E+06 + 4.084291E+12 + 134 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.438571E+03 -7.751189E+03 5.872752E+03 -3.526592E+03 6.353563E+02 5.247092E+02 -5.186292E+02 7.784959E+02 -3.180064E+02 + 9.138428E+02 -8.752386E+02 8.857504E+02 -4.206392E+02 6.767773E+01 -6.953386E+02 7.846502E+02 -6.418427E+02 -2.282344E+02 -1.871932E+02 + 1.186836E+03 -2.131758E+03 2.805497E+03 -2.730137E+03 3.438552E+03 -4.433653E+03 3.640913E+03 -2.152241E+03 2.019236E+03 -3.691847E+03 + 3.377446E+03 -3.176531E+03 -1.511155E+03 2.403960E+02 3.048325E+03 -3.178177E+02 1.057100E+02 4.306352E+02 2.370353E+03 -1.560292E+04 + 1.075372E+04 -1.358843E+04 1.830872E+04 -8.988574E+04 7.967364E+04 -1.478202E+05 2.393332E+05 -8.660768E+05 7.094230E+05 -9.947365E+05 + 3.267703E+06 -6.825691E+05 -1.273379E+06 1.924488E+06 2.012748E+06 -1.276544E+07 2.874725E+07 4.746761E+06 4.814580E+06 4.325716E+07 + -1.419558E+08 2.353051E+08 -4.259588E+07 3.695779E+08 -3.415494E+08 3.246995E+08 9.514562E+07 -7.198477E+06 -1.253482E+07 2.139964E+06 + -3.316523E+10 + 135 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.743144E+04 1.657754E+04 5.216435E+03 -1.422005E+04 2.037613E+04 -1.636231E+04 6.901538E+03 -6.746549E+03 6.701108E+03 + -5.954803E+02 -4.272840E+03 -1.629962E+03 6.675469E+02 1.172739E+03 2.041975E+03 9.045192E+02 -6.798187E+02 -7.572577E+02 4.577851E+03 + -4.620953E+03 2.675411E+03 -4.676051E+03 -5.907288E+02 4.272536E+03 -4.341252E+03 4.163839E+03 -1.037737E+04 1.774254E+04 -9.753667E+03 + 1.389348E+04 -1.838039E+04 3.466664E+03 1.529302E+04 -2.813720E+04 -4.660672E+03 -6.729367E+02 2.121902E+04 -3.993623E+04 7.455487E+04 + -6.958571E+04 3.515120E+04 1.079383E+05 -6.303368E+05 6.634150E+05 -1.104642E+06 -9.467072E+05 -4.023032E+05 3.731768E+06 8.473115E+06 + -2.554183E+06 -8.307800E+06 2.796715E+07 -4.256738E+07 1.656708E+07 -1.810573E+07 5.850362E+07 -2.320897E+07 -6.571604E+06 1.186946E+08 + -3.656681E+08 2.384668E+08 -4.599991E+08 -3.415494E+08 1.345595E+10 -3.089312E+09 1.013143E+09 6.804556E+08 8.098204E+07 1.896862E+06 + -1.365548E+13 + 136 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 6.535810E+03 -1.427489E+04 1.990972E+04 -1.712800E+04 1.721657E+04 -1.589017E+04 3.165296E+03 8.459323E+02 -2.847407E+03 + 5.476602E+03 -6.412884E+03 6.935899E+03 -4.027940E+03 7.302545E+02 -2.128289E+03 2.062661E+03 -1.893974E+03 9.186947E+02 -2.954722E+03 + 6.461425E+03 -8.494333E+03 7.640401E+03 -3.700924E+03 -2.074247E+03 5.170471E+03 -4.570336E+03 4.256690E+03 -2.005112E+03 -3.141926E+03 + 3.265982E+03 2.141951E+03 -6.765018E+03 9.863789E+03 1.087257E+04 1.135546E+04 -5.497272E+04 7.343047E+04 -3.504371E+04 2.539224E+04 + -6.152323E+04 1.386169E+05 -1.542905E+05 1.145240E+05 -3.733252E+05 3.513424E+05 3.316616E+06 1.441286E+06 -3.874933E+06 -3.280633E+06 + -4.572318E+06 5.553245E+06 -2.253728E+05 1.483183E+07 9.527296E+06 2.980831E+07 4.944091E+07 -5.677230E+06 8.670852E+06 1.982408E+07 + -9.932873E+07 1.547599E+07 9.869202E+05 3.246995E+08 -3.089312E+09 1.598116E+10 -2.282048E+09 3.848768E+08 -5.139691E+06 -5.808793E+05 + 1.888382E+13 + 137 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.878342E+03 -3.874685E+03 1.031580E+04 -3.893944E+03 2.396980E+02 1.685746E+03 -1.177571E+03 1.846261E+03 -4.201207E+02 + -1.029420E+03 8.551598E+02 -1.131945E+03 1.249174E+03 -1.478340E+03 1.900258E+02 3.757625E+02 6.273860E+02 6.153894E+02 -1.259899E+03 + -2.358245E+02 1.501568E+03 -9.603808E+02 -1.024186E+03 2.487464E+03 -2.332988E+03 5.512133E+02 3.167900E+03 -7.749392E+03 5.439299E+03 + -5.149162E+02 -1.724524E+03 3.925464E+03 -9.142885E+03 8.897150E+02 -8.201543E+03 1.624658E+04 -1.635707E+04 9.668291E+03 -2.593297E+04 + 5.860180E+04 -3.355956E+04 -1.443977E+05 1.642667E+05 -2.847974E+05 5.853673E+05 -9.252444E+05 -1.099476E+06 1.057209E+06 2.095535E+06 + 7.952671E+06 -7.873235E+06 1.859086E+06 2.459398E+05 1.450236E+07 -4.463714E+06 -4.482593E+06 -1.424618E+07 -1.582210E+07 -7.061702E+07 + 1.107018E+08 -4.880243E+07 -1.362109E+07 9.514562E+07 1.013143E+09 -2.282048E+09 2.664432E+09 -9.259218E+08 1.762716E+08 -5.639467E+06 + -3.387520E+13 + 138 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 6.339721E+02 4.770905E+03 -1.291674E+04 1.220807E+04 -4.874917E+03 -1.190001E+03 2.622340E+03 -2.268244E+03 1.615763E+03 + -6.305818E+02 -3.714032E+02 3.878537E+02 -9.066764E+02 1.339225E+03 -2.285335E+02 -4.061857E+02 7.437422E+02 -1.750465E+03 7.337816E+02 + 1.168465E+03 -1.211416E+03 4.607624E+02 -1.115604E+03 -1.283014E+03 1.117268E+03 -1.793488E+03 1.003987E+03 3.028288E+03 -2.309784E+03 + 4.902236E+03 -4.784876E+03 -1.236503E+03 7.209595E+03 -1.137890E+04 5.959045E+03 3.857818E+03 -1.468648E+04 1.403200E+04 2.552111E+03 + -1.737577E+04 -2.941697E+03 9.566868E+04 -3.779156E+05 4.975238E+05 -3.186263E+03 4.197095E+04 4.676035E+05 -2.570734E+05 1.804336E+06 + -2.068831E+06 -2.759831E+06 2.917231E+06 -7.210895E+06 1.250172E+07 -2.403054E+07 2.789360E+06 -5.465396E+06 -5.232665E+06 9.352964E+06 + 7.061885E+07 3.229019E+07 -3.405684E+07 -7.198477E+06 6.804556E+08 3.848768E+08 -9.259218E+08 1.551474E+09 -3.469849E+08 -2.502581E+07 + 2.873137E+11 + 139 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.613020E+03 3.662418E+03 -7.084843E+02 -3.549098E+03 3.055636E+03 -3.348122E+02 -5.044901E+02 -1.598764E+01 4.235039E+02 + -2.821562E+02 2.717243E+02 -3.814358E+02 6.434766E+02 -7.220321E+02 5.835618E+02 -9.884442E+01 -1.000419E+01 5.314829E+02 -2.408048E+02 + -6.480966E+02 9.558911E+02 -1.121968E+03 9.649541E+02 4.774877E+02 -2.457972E+02 1.057377E+02 7.971562E+01 -1.699746E+03 7.827059E+02 + -4.264657E+02 2.470571E+02 4.634489E+02 -1.667699E+03 2.369968E+03 2.240551E+03 -8.561447E+03 1.668930E+04 -2.536559E+04 1.237207E+04 + -1.108584E+04 2.890001E+03 -3.287147E+04 1.553554E+05 -2.005411E+05 4.965395E+04 -2.116240E+05 -6.197327E+04 4.290236E+05 -4.622034E+05 + 2.180628E+06 9.405461E+04 -1.703621E+06 1.731683E+06 -4.066486E+06 6.186527E+06 7.444649E+06 -4.911494E+06 2.109740E+06 2.924540E+06 + -7.120414E+07 1.614308E+07 -2.682767E+06 -1.253482E+07 8.098204E+07 -5.139691E+06 1.762716E+08 -3.469849E+08 1.960241E+08 1.101366E+07 + -7.620530E+12 + 140 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.296932E+03 2.068693E+03 -2.137770E+03 1.885858E+03 -5.826336E+02 1.118272E+02 -3.865811E+02 6.148903E+02 -2.598532E+02 + 1.060564E+01 -1.899280E+02 2.034553E+01 -5.671338E+01 1.417398E+01 1.418651E+02 -1.296608E+02 5.362030E+01 -4.922418E+02 5.572954E+02 + -8.936615E+00 -3.715837E+02 4.767205E+02 1.237175E+02 -5.772406E+02 4.311095E+02 2.101421E+02 -7.192423E+02 6.918679E+02 -2.277984E+02 + -1.026184E+03 1.694194E+03 -2.319098E+03 4.164289E+03 -4.064923E+02 -2.181179E+03 3.775457E+02 2.748673E+03 -7.703866E+03 1.815965E+04 + -8.774213E+03 -8.411687E+02 6.775555E+03 1.057538E+04 2.063016E+04 -5.356799E+04 -3.779218E+04 1.344322E+05 -5.604270E+05 2.204521E+05 + -1.141030E+06 2.405264E+05 -1.611880E+06 2.929526E+06 -3.011816E+06 4.641694E+05 -8.632319E+05 1.059325E+06 1.827773E+06 -2.733769E+06 + -5.611240E+06 -5.889744E+06 -2.381573E+06 2.139964E+06 1.896862E+06 -5.808793E+05 -5.639467E+06 -2.502581E+07 1.101366E+07 4.921944E+07 + 4.070291E+12 + 141 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.011113E+09 -7.487201E+09 6.424784E+09 -5.170914E+09 2.806774E+09 1.112780E+08 -9.100549E+08 4.881502E+08 -2.920598E+07 + 1.045294E+09 -7.086943E+08 7.343957E+08 -1.011745E+09 5.406246E+08 -1.228855E+08 6.515467E+07 -4.032625E+08 1.177632E+09 -2.858447E+09 + 4.140634E+09 -2.353446E+09 6.715513E+08 -3.826648E+08 1.013393E+09 2.128331E+08 -1.927745E+09 2.046834E+09 1.334524E+09 -4.902092E+09 + 3.905113E+09 -9.921984E+08 -5.393300E+08 -3.881100E+06 -6.461924E+08 -3.150266E+08 1.114083E+09 -3.012060E+09 1.002104E+10 -8.692603E+09 + -8.462974E+10 1.159599E+11 1.673701E+10 -1.924465E+11 6.538914E+10 -2.122148E+11 6.075234E+11 -4.235944E+11 -2.279375E+11 -2.624262E+11 + 1.012992E+12 -9.481667E+11 2.678100E+10 -8.446832E+10 1.733338E+12 -1.479694E+12 -3.873666E+12 3.030551E+12 -4.257134E+12 -2.396878E+12 + 4.889515E+12 -6.587993E+12 4.084291E+12 -3.316523E+10 -1.365548E+13 1.888382E+13 -3.387520E+13 2.873137E+11 -7.620530E+12 4.070291E+12 + 4.296412E+20 + 1 2 + 4.195724E-01 4.171864E-01 4.135930E-01 4.089600E-01 4.033876E-01 3.971887E-01 3.905575E-01 3.835752E-01 3.762912E-01 3.684467E-01 + 3.597572E-01 3.500982E-01 3.398227E-01 3.285695E-01 3.171696E-01 3.055330E-01 2.931043E-01 2.808137E-01 2.689471E-01 2.569991E-01 + 2.455438E-01 2.349385E-01 2.249357E-01 2.157680E-01 2.071204E-01 1.986485E-01 1.909778E-01 1.847100E-01 1.794586E-01 1.749392E-01 + 1.710950E-01 1.679863E-01 1.656863E-01 1.637675E-01 1.615469E-01 1.587115E-01 1.551102E-01 1.506883E-01 1.464582E-01 1.428642E-01 + 1.397762E-01 1.377796E-01 1.361504E-01 1.331470E-01 1.273891E-01 1.194895E-01 1.120839E-01 1.014972E-01 9.009076E-02 7.632089E-02 + 6.581483E-02 5.439858E-02 4.619287E-02 3.912948E-02 3.313914E-02 2.734475E-02 2.264932E-02 1.830388E-02 1.480757E-02 1.168130E-02 + 9.308201E-03 7.071761E-03 5.319191E-03 3.960552E-03 3.019211E-03 2.233516E-03 1.622261E-03 1.115258E-03 6.267235E-04 2.676320E-04 + 1.543262E-04 7.281560E-01 7.410349E-01 7.517588E-01 7.588513E-01 7.614846E-01 7.634233E-01 7.853376E-01 8.419909E-01 8.974963E-01 + 9.665870E-01 1.033117E+00 1.091874E+00 1.154667E+00 1.197726E+00 1.231908E+00 1.246114E+00 1.231974E+00 1.195795E+00 1.147992E+00 + 1.089539E+00 1.016458E+00 9.438683E-01 8.919964E-01 8.259427E-01 7.605864E-01 6.608704E-01 5.896492E-01 5.157502E-01 4.619746E-01 + 4.049822E-01 3.617566E-01 3.073625E-01 2.471382E-01 1.929484E-01 1.529465E-01 1.178071E-01 9.194710E-02 6.866960E-02 5.161838E-02 + 3.691519E-02 2.403058E-02 1.643477E-02 1.133541E-02 6.629399E-03 3.529460E-03 1.738492E-03 7.853156E-04 4.318216E-04 3.950658E-04 + 2.266997E-04 1.661443E-04 1.328605E-04 1.064169E-04 2.005721E-05 8.952470E-06 2.961107E-05 4.481708E-05 5.076370E-05 2.540084E-05 + 3.985505E-06 1.768763E-05 2.923266E-05 3.202950E-05 4.498114E-06 3.228534E-06 7.726339E-06 1.340372E-05 5.923807E-05 1.075270E-04 + 2.107420E-11 + 1 + 3.007064E+05 -2.789843E+05 -1.189079E+05 2.191212E+04 7.673677E+04 3.142421E+04 -3.102767E+04 -1.963343E+04 2.116313E+04 4.186663E+03 + -1.517284E+04 9.128456E+03 1.622341E+03 -8.020994E+03 7.696571E+03 -2.171381E+03 -3.838002E+03 6.116009E+03 -3.619476E+03 -1.516726E+03 + 4.578316E+03 -2.589736E+03 -1.828415E+03 3.840229E+03 -1.426894E+03 -2.326556E+03 3.080928E+03 -4.400904E+02 -2.187145E+03 2.271790E+03 + -1.009467E+02 -2.415270E+03 2.222546E+03 8.286865E+02 -2.635207E+03 1.319142E+03 1.082717E+03 -2.210694E+03 1.536458E+03 -5.588026E+01 + -5.707036E+02 5.181592E+01 1.911940E+02 8.446044E+01 -1.257590E+02 -1.893059E+02 1.412876E+02 2.746734E+02 -3.985927E+02 2.895858E+02 + 7.316291E+01 -6.115992E+02 5.008016E+02 1.835648E+02 -5.187432E+02 1.578406E+02 5.187007E+01 -4.797283E+00 1.578641E+02 -3.618314E+02 + -4.617368E+02 4.602168E+02 -1.204998E+02 -1.080640E+03 7.087309E+02 2.939702E+03 3.319769E+03 2.300556E+03 1.156645E+03 5.226168E+02 + 3.101771E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 2 + -2.789843E+05 4.748302E+05 -1.514452E+05 -6.815460E+04 -1.409024E+03 2.418998E+04 1.053291E+04 -8.883301E+03 -5.118192E+03 5.651946E+03 + 3.356272E+02 -3.414278E+03 2.581354E+03 -1.238105E+02 -1.678631E+03 1.907707E+03 -7.852410E+02 -8.681257E+02 1.695702E+03 -8.567849E+02 + -8.818320E+02 1.444248E+03 -1.938181E+02 -1.054460E+03 7.564670E+02 2.845493E+02 -6.149256E+02 3.037360E+02 2.918184E+01 -3.607873E+02 + 4.126932E+02 1.720966E+02 -4.751226E+02 -5.583527E+01 3.281069E+02 -5.602440E+01 -2.043244E+02 1.850381E+02 -1.928686E+01 -1.181380E+02 + 3.154145E+02 -3.548773E+02 9.903656E+01 2.275933E+02 -4.640156E+02 5.402209E+02 -3.053520E+02 7.384358E+01 7.017424E+01 -1.941749E+02 + 1.787215E+02 -3.519725E+01 -1.447181E+02 2.539069E+02 -1.588338E+02 -2.081624E+01 1.377419E+02 -1.357054E+02 7.017451E+01 1.147905E+02 + -2.610235E+02 -1.396634E+02 2.466428E+02 -3.857980E+01 -6.819067E+02 -8.879407E+02 -4.847204E+02 -8.270830E+01 7.351868E+01 9.029161E+01 + 7.570924E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 3 + -1.189079E+05 -1.514452E+05 5.201452E+05 -1.766863E+05 -1.091954E+05 -5.630347E+03 4.975621E+04 1.232077E+04 -2.859222E+04 9.945083E+02 + 1.675534E+04 -1.249555E+04 -9.661060E+00 8.445426E+03 -9.009293E+03 3.234551E+03 3.587334E+03 -6.575581E+03 4.432393E+03 1.002162E+03 + -4.887174E+03 3.389714E+03 1.385167E+03 -4.261050E+03 2.163078E+03 2.149608E+03 -3.512527E+03 9.541723E+02 2.081994E+03 -2.485136E+03 + 3.759335E+02 2.181412E+03 -2.302039E+03 -4.995758E+02 2.612304E+03 -1.557571E+03 -1.226027E+03 2.565785E+03 -1.246418E+03 -8.789123E+02 + 1.407845E+03 -3.576121E+02 -4.778858E+02 4.173228E+02 -1.631531E+02 3.174864E+02 -3.201877E+02 -4.823024E+00 1.715780E+02 -2.744306E+02 + 1.529122E+02 2.706263E+02 -3.396040E+02 -2.138756E-01 1.376053E+02 1.044367E+02 -1.335428E+01 -2.589112E+02 6.124970E+01 3.796565E+02 + 1.845409E+02 -4.398188E+02 2.443276E+02 1.069717E+03 -8.155964E+02 -2.960137E+03 -2.990083E+03 -1.830480E+03 -8.090984E+02 -3.192425E+02 + -1.737529E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 4 + 2.191212E+04 -6.815460E+04 -1.766863E+05 4.562132E+05 -2.111491E+05 -7.811146E+04 4.158235E+04 3.820499E+04 -2.296888E+04 -1.223947E+04 + 1.864727E+04 -6.618670E+03 -5.460214E+03 9.651617E+03 -6.463207E+03 -1.988522E+02 5.245882E+03 -5.635625E+03 1.776155E+03 2.740391E+03 + -3.786383E+03 1.142024E+03 1.945067E+03 -2.807081E+03 9.673536E+02 1.856436E+03 -2.696350E+03 4.779007E+02 2.026938E+03 -1.976725E+03 + -1.548308E+02 2.037878E+03 -1.629872E+03 -6.353633E+02 2.103925E+03 -1.140283E+03 -8.912525E+02 1.827941E+03 -1.222640E+03 -3.316622E+01 + 5.559246E+02 -8.886180E+01 -1.452192E+02 -6.718733E+00 5.479250E+01 9.789500E+01 -1.341349E+02 -1.908151E+02 2.968303E+02 -1.520575E+02 + -1.406309E+02 4.304804E+02 -2.492464E+02 -2.525287E+02 3.407005E+02 -1.390388E+02 -1.322444E+02 1.822033E+02 -1.634129E+02 1.523960E+02 + 8.877884E+02 -3.142424E+02 -6.939821E+02 9.043022E+02 2.231723E+02 -1.685345E+03 -2.322916E+03 -1.714532E+03 -9.203634E+02 -4.651970E+02 + -2.978274E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 5 + 7.673677E+04 -1.409024E+03 -1.091954E+05 -2.111491E+05 4.485635E+05 -1.920616E+05 -5.784745E+04 3.999604E+04 2.549184E+04 -2.179139E+04 + -4.863540E+03 1.412638E+04 -7.768590E+03 -1.842312E+03 6.861571E+03 -5.751232E+03 8.933616E+02 3.742945E+03 -4.789412E+03 1.638407E+03 + 2.723654E+03 -3.816599E+03 5.693173E+02 2.915694E+03 -2.458358E+03 -5.970859E+02 2.275234E+03 -1.303661E+03 -7.639962E+02 1.661965E+03 + -7.359089E+02 -9.569780E+02 1.402766E+03 5.396624E+01 -1.370198E+03 8.505709E+02 9.250748E+02 -1.529088E+03 1.437662E+02 1.380118E+03 + -1.586113E+03 6.680452E+02 3.593281E+02 -7.957866E+02 7.125498E+02 -6.125462E+02 4.534478E+02 -2.403460E+02 8.232835E+00 3.226830E+02 + -4.769502E+02 3.074604E+02 1.260773E+02 -6.506912E+02 7.708631E+02 -1.811333E+02 -6.131383E+02 6.973186E+02 -2.589181E+02 -6.551771E+02 + 7.838502E+02 6.051266E+02 -6.178579E+02 -1.445489E+02 1.099618E+03 1.499556E+03 7.932439E+02 8.561668E+01 -2.067510E+02 -2.322491E+02 + -1.875107E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 6 + 3.142421E+04 2.418998E+04 -5.630347E+03 -7.811146E+04 -1.920616E+05 4.266329E+05 -2.204716E+05 -3.861313E+04 6.641034E+04 3.478390E+03 + -3.362537E+04 1.934598E+04 3.820228E+03 -1.533632E+04 1.269130E+04 -2.413448E+03 -6.870676E+03 9.401015E+03 -4.623745E+03 -2.856637E+03 + 6.294862E+03 -3.371615E+03 -2.043716E+03 4.912784E+03 -2.663023E+03 -2.381862E+03 4.475975E+03 -1.409990E+03 -2.584970E+03 2.946196E+03 + -3.304388E+02 -2.230427E+03 2.335122E+03 2.788705E+02 -2.706845E+03 1.836504E+03 1.231303E+03 -2.863802E+03 1.495494E+03 1.093254E+03 + -2.244365E+03 1.201842E+03 3.769159E+02 -1.041051E+03 9.966101E+02 -9.311712E+02 6.150696E+02 -3.943660E+01 -1.989666E+02 2.847715E+02 + -1.732489E+02 -1.322513E+02 3.362046E+02 -1.332076E+02 8.208362E+01 1.545113E+01 6.440292E+01 8.447350E+01 -9.411511E+01 -2.101754E+02 + -6.705053E+02 4.266724E+02 7.187396E+02 -1.270789E+03 -2.449422E+02 2.427293E+03 2.857950E+03 1.698515E+03 6.574920E+02 2.119234E+02 + 1.028806E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 7 + -3.102767E+04 1.053291E+04 4.975621E+04 4.158235E+04 -5.784745E+04 -2.204716E+05 3.890514E+05 -2.100283E+05 -9.286540E+03 5.632639E+04 + -1.319444E+04 -1.699296E+04 1.784510E+04 -5.482258E+03 -5.549405E+03 9.034728E+03 -5.087245E+03 -1.532310E+03 5.304756E+03 -3.733124E+03 + -9.530615E+02 3.671040E+03 -1.747721E+03 -1.519765E+03 1.991160E+03 -3.977436E+02 -9.753498E+02 1.051913E+03 1.693117E+02 -1.045404E+03 + 5.460701E+02 5.294041E+02 -8.216858E+02 8.982532E+01 6.885530E+02 -6.046638E+02 -4.286970E+02 1.005792E+03 -2.721060E+02 -6.570037E+02 + 7.201448E+02 -1.310124E+02 -3.794716E+02 5.000720E+02 -2.138214E+02 -2.002274E+02 3.107704E+02 -1.058421E+02 -7.996314E+01 1.613701E+01 + 2.189924E+02 -2.876959E+02 -6.653014E+01 5.791678E+02 -5.408707E+02 -1.884378E+02 7.279435E+02 -4.806623E+02 2.404795E+01 9.057554E+02 + -1.158694E+03 -9.443174E+02 1.419388E+03 2.758576E+02 -1.139488E+03 -7.796483E+02 -4.045898E+01 2.058629E+02 2.695362E+02 2.758012E+02 + 2.288636E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 8 + -1.963343E+04 -8.883301E+03 1.232077E+04 3.820499E+04 3.999604E+04 -3.861313E+04 -2.100283E+05 3.634048E+05 -2.251057E+05 1.944342E+04 + 5.983334E+04 -3.836068E+04 -2.576627E+03 2.294892E+04 -1.910825E+04 3.827570E+03 9.225322E+03 -1.240633E+04 5.849106E+03 3.611154E+03 + -7.602245E+03 3.949324E+03 2.383212E+03 -5.537267E+03 2.918299E+03 2.613949E+03 -4.882138E+03 1.486753E+03 2.945248E+03 -3.184100E+03 + 2.793544E+02 2.229196E+03 -2.390603E+03 -1.554263E+02 2.810375E+03 -2.043002E+03 -1.258219E+03 3.260723E+03 -1.727975E+03 -1.635515E+03 + 3.247126E+03 -1.779334E+03 -6.727020E+02 1.616755E+03 -1.351508E+03 9.797008E+02 -4.092662E+02 -1.593292E+02 3.774077E+02 -4.511228E+02 + 3.000757E+02 8.157139E+01 -5.669192E+02 5.574422E+02 -4.168895E+02 -9.079405E+01 4.855991E+02 -4.203758E+02 -3.200165E+01 5.119939E+02 + 4.486350E+02 -5.636608E+02 -6.776783E+02 1.122306E+03 4.981636E+02 -1.700074E+03 -2.119844E+03 -1.276855E+03 -4.116613E+02 -1.501302E+01 + 5.130991E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 9 + 2.116313E+04 -5.118192E+03 -2.859222E+04 -2.296888E+04 2.549184E+04 6.641034E+04 -9.286540E+03 -2.251057E+05 3.387570E+05 -2.116836E+05 + 3.386719E+04 4.296199E+04 -3.571675E+04 5.176399E+03 1.436678E+04 -1.579722E+04 5.529619E+03 5.344991E+03 -8.858387E+03 4.072786E+03 + 3.030438E+03 -5.364446E+03 1.447945E+03 3.051857E+03 -2.824925E+03 -3.020348E+02 2.190174E+03 -1.287691E+03 -1.122567E+03 1.987188E+03 + -3.896512E+02 -1.556568E+03 1.618496E+03 3.875838E+01 -1.524620E+03 1.417003E+03 3.158346E+02 -2.027362E+03 1.698840E+03 1.875266E+01 + -7.562344E+02 -2.321956E+01 8.204024E+02 -6.041818E+02 -1.636700E+02 8.028352E+02 -8.239633E+02 3.145625E+02 1.860373E+02 -3.411104E+02 + 1.229393E+02 1.066562E+02 1.987499E+01 -2.172296E+02 3.218791E+01 5.251978E+02 -5.745812E+02 -3.408731E+01 4.775443E+02 -1.214588E+03 + 5.027108E+02 1.524900E+03 -1.653359E+03 -1.151685E+03 1.331460E+03 1.821766E+03 1.145458E+03 5.268259E+02 8.637178E+01 -1.079091E+02 + -1.207789E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 10 + 4.186663E+03 5.651946E+03 9.945083E+02 -1.223947E+04 -2.179139E+04 3.478390E+03 5.632639E+04 1.944342E+04 -2.116836E+05 3.010693E+05 + -2.113343E+05 6.663071E+04 2.279341E+04 -4.233712E+04 2.267016E+04 3.220951E+03 -1.638587E+04 1.378091E+04 -2.309051E+03 -7.357514E+03 + 7.798549E+03 -1.084179E+03 -4.402279E+03 4.382207E+03 -6.875876E+02 -3.035694E+03 3.607273E+03 -4.126932E+02 -2.839560E+03 2.429275E+03 + 2.030479E+02 -1.994723E+03 1.832330E+03 3.971708E+02 -2.496164E+03 1.518984E+03 1.325269E+03 -2.586035E+03 9.074425E+02 1.847873E+03 + -2.878684E+03 1.608433E+03 4.160669E+02 -1.475546E+03 1.369794E+03 -7.226645E+02 -1.960576E+01 5.927827E+02 -8.402548E+02 7.861205E+02 + -4.203222E+02 -3.309248E+02 9.894223E+02 -9.092774E+02 3.766868E+02 2.873498E+02 -7.550924E+02 4.383002E+02 2.032319E+02 -1.781747E+02 + -3.366467E+02 3.098322E+02 8.913410E+02 -5.416091E+02 -8.121072E+02 8.232148E+01 3.414098E+02 2.639525E+02 7.706790E+01 -4.148624E+01 + -4.739513E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 11 + -1.517284E+04 3.356272E+02 1.675534E+04 1.864727E+04 -4.863540E+03 -3.362537E+04 -1.319444E+04 5.983334E+04 3.386719E+04 -2.113343E+05 + 2.894104E+05 -2.097401E+05 7.087112E+04 2.250179E+04 -4.424177E+04 2.369981E+04 4.692038E+03 -1.901138E+04 1.407995E+04 9.884851E+02 + -1.027980E+04 6.872366E+03 2.373374E+03 -6.779696E+03 3.049580E+03 2.917800E+03 -4.678542E+03 1.172025E+03 3.293141E+03 -3.506122E+03 + -1.254031E+02 3.242807E+03 -2.925248E+03 -4.604130E+02 3.396614E+03 -2.625786E+03 -1.113349E+03 3.981458E+03 -2.766532E+03 -6.464285E+02 + 1.991879E+03 -7.014959E+02 -1.027550E+03 1.384265E+03 -4.323270E+02 -6.468067E+02 7.429124E+02 -7.703605E+01 -2.851726E+02 2.144473E+02 + 6.986558E+01 -3.883396E+01 -2.339194E+02 2.739700E+02 1.462570E+02 -6.156582E+02 4.162378E+02 3.824184E+02 -7.107952E+02 9.426883E+02 + 4.321136E+01 -1.813607E+03 8.993760E+02 1.523101E+03 -6.187602E+02 -1.625020E+03 -1.322321E+03 -6.314203E+02 -2.585498E+01 1.693066E+02 + 1.409848E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 12 + 9.128456E+03 -3.414278E+03 -1.249555E+04 -6.618670E+03 1.412638E+04 1.934598E+04 -1.699296E+04 -3.836068E+04 4.296199E+04 6.663071E+04 + -2.097401E+05 2.644130E+05 -1.942910E+05 7.541627E+04 6.714480E+03 -3.380026E+04 2.257717E+04 -3.403434E+02 -1.264313E+04 9.885502E+03 + 2.307018E+02 -6.344820E+03 4.149270E+03 1.374676E+03 -3.411181E+03 1.586896E+03 8.455511E+02 -1.773808E+03 7.525974E+02 9.035941E+02 + -1.140643E+03 -1.858722E+02 1.094181E+03 -6.429200E+02 -3.811830E+02 1.077543E+03 -4.056594E+02 -1.306325E+03 1.863511E+03 -1.184033E+03 + 7.617136E+02 -6.923828E+02 6.107972E+02 -1.064718E+02 -8.060197E+02 1.184511E+03 -3.933476E+02 -9.266528E+02 1.417729E+03 -1.089081E+03 + 3.326483E+02 5.444882E+02 -1.041534E+03 8.474141E+02 -3.979956E+02 -1.805982E+01 5.756522E+02 -6.122891E+02 8.966098E+01 -4.352462E+02 + 2.432833E+02 8.249576E+02 -8.660368E+02 -6.755738E+02 2.632861E+02 8.948504E+02 1.216842E+03 8.562205E+02 3.513750E+02 7.416137E+01 + -3.189028E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 13 + 1.622341E+03 2.581354E+03 -9.661057E+00 -5.460214E+03 -7.768590E+03 3.820228E+03 1.784510E+04 -2.576627E+03 -3.571675E+04 2.279341E+04 + 7.087112E+04 -1.942910E+05 2.461574E+05 -1.918232E+05 8.862410E+04 -1.288142E+03 -3.609013E+04 3.020215E+04 -5.207071E+03 -1.378189E+04 + 1.387320E+04 -8.718256E+02 -8.928191E+03 6.874280E+03 1.333194E+03 -6.178194E+03 4.042856E+03 1.886527E+03 -4.973191E+03 2.031848E+03 + 2.472806E+03 -3.422045E+03 1.089181E+03 1.837904E+03 -2.804327E+03 8.604719E+02 1.712882E+03 -2.316125E+03 7.817868E+02 1.292742E+03 + -1.901842E+03 8.547571E+02 2.267933E+02 -7.565380E+02 1.077032E+03 -8.948117E+02 2.679703E+02 3.900030E+02 -6.878274E+02 6.472534E+02 + -3.427102E+02 -3.538027E+02 8.228053E+02 -6.542195E+02 7.321921E+01 3.335936E+02 -5.052344E+02 -5.359784E+01 5.793866E+02 -1.994578E+02 + -4.013998E+02 6.453893E+02 1.668547E+02 -1.089473E+02 3.182195E+02 1.048050E+02 -1.398005E+02 -1.761964E+02 -2.953501E+02 -2.867639E+02 + -1.912595E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 14 + -8.020994E+03 -1.238104E+02 8.445426E+03 9.651617E+03 -1.842312E+03 -1.533632E+04 -5.482258E+03 2.294892E+04 5.176399E+03 -4.233712E+04 + 2.250179E+04 7.541627E+04 -1.918232E+05 2.441804E+05 -1.977349E+05 8.903688E+04 4.212679E+03 -4.190898E+04 2.976264E+04 2.334968E+03 + -1.945115E+04 1.117059E+04 5.541806E+03 -1.107951E+04 3.513914E+03 5.529869E+03 -6.594205E+03 4.513054E+02 4.977509E+03 -3.829379E+03 + -1.145032E+03 4.051099E+03 -2.594935E+03 -1.397588E+03 3.678794E+03 -2.029532E+03 -1.760059E+03 4.341320E+03 -3.173048E+03 1.012999E+01 + 1.263613E+03 -3.482832E+02 -7.070146E+02 1.014070E+03 -7.541964E+02 2.561284E+02 -1.810477E+02 5.482874E+02 -5.778606E+02 3.153652E+02 + -2.315522E+01 -6.329654E+01 1.952648E+02 -2.222762E+02 2.708149E+02 -2.324666E+02 6.532598E+01 4.199814E+02 -7.509257E+02 8.617918E+02 + 3.225042E+02 -1.354958E+03 1.581258E+02 5.849044E+02 1.432765E+02 -5.230156E+02 -1.194115E+03 -9.375172E+02 -2.759095E+02 6.561980E+01 + 9.990696E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 15 + 7.696571E+03 -1.678631E+03 -9.009293E+03 -6.463207E+03 6.861571E+03 1.269130E+04 -5.549405E+03 -1.910825E+04 1.436678E+04 2.267016E+04 + -4.424177E+04 6.714480E+03 8.862410E+04 -1.977349E+05 2.482595E+05 -1.940324E+05 8.319198E+04 6.467518E+03 -3.874996E+04 2.101673E+04 + 7.566906E+03 -1.672502E+04 5.766844E+03 6.902683E+03 -8.093936E+03 9.087941E+02 4.779480E+03 -4.119724E+03 -5.869012E+02 3.672316E+03 + -2.249626E+03 -1.493194E+03 3.008492E+03 -7.471780E+02 -1.976993E+03 2.186110E+03 3.616141E+01 -2.846789E+03 3.415325E+03 -1.722452E+03 + 2.667625E+02 7.074223E+01 1.919148E+02 -3.651697E+02 1.056854E+00 4.408474E+02 -1.646449E+02 -6.846734E+02 9.323676E+02 -6.879562E+02 + 3.017098E+02 2.141704E+02 -7.259474E+02 7.147561E+02 -2.808835E+02 -1.289935E+02 2.474657E+02 -1.008484E+02 5.328636E+01 -9.491417E+02 + -4.916642E+01 1.010357E+03 -1.577878E+02 -9.873124E+02 -2.248378E+02 1.220924E+03 1.621300E+03 1.063701E+03 5.086637E+02 2.338403E+02 + 1.258634E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 16 + -2.171381E+03 1.907707E+03 3.234551E+03 -1.988522E+02 -5.751232E+03 -2.413448E+03 9.034728E+03 3.827570E+03 -1.579722E+04 3.220951E+03 + 2.369981E+04 -3.380026E+04 -1.288142E+03 8.903688E+04 -1.940324E+05 2.371826E+05 -1.855416E+05 8.333652E+04 4.786344E+03 -3.614136E+04 + 1.928452E+04 8.072165E+03 -1.614598E+04 4.935754E+03 7.142278E+03 -8.353136E+03 1.293013E+03 5.209906E+03 -4.990883E+03 -5.304617E+02 + 4.309259E+03 -2.503314E+03 -1.356737E+03 2.792688E+03 -1.379422E+03 -9.827606E+02 2.232973E+03 -1.206703E+03 -1.061103E+03 2.194825E+03 + -1.433606E+03 1.145665E+02 4.396169E+02 -4.060160E+02 5.839170E+02 -7.087482E+02 3.994010E+02 1.118986E+02 -3.125598E+02 3.237511E+02 + -2.917801E+02 -4.894492E+01 4.219584E+02 -4.430387E+02 -4.870195E+01 4.524224E+02 -2.791255E+02 -4.380493E+02 8.492558E+02 1.301752E+02 + -3.511892E+02 1.781551E+02 5.336528E+01 5.285522E+02 -1.542295E+02 -9.950596E+02 -5.293674E+02 -1.348758E+02 -2.041762E+02 -2.412395E+02 + -1.656348E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 17 + -3.838002E+03 -7.852410E+02 3.587334E+03 5.245882E+03 8.933616E+02 -6.870676E+03 -5.087245E+03 9.225322E+03 5.529619E+03 -1.638587E+04 + 4.692038E+03 2.257717E+04 -3.609013E+04 4.212679E+03 8.319198E+04 -1.855416E+05 2.378381E+05 -1.986101E+05 8.656252E+04 1.433750E+04 + -4.416962E+04 1.743875E+04 1.420672E+04 -1.844214E+04 2.666417E+03 1.026948E+04 -9.078757E+03 -8.769124E+02 7.755184E+03 -4.960850E+03 + -2.552856E+03 5.794813E+03 -2.511820E+03 -2.762220E+03 4.517400E+03 -1.491730E+03 -2.845415E+03 4.604143E+03 -2.463134E+03 -7.615294E+02 + 1.724783E+03 -5.936828E+02 -5.575644E+02 8.669632E+02 -7.554769E+02 2.690681E+02 8.576045E+01 -3.370348E+01 -1.936921E+01 1.023792E+02 + -7.342350E+01 1.042985E+02 -9.626980E+01 -3.182576E+01 4.336901E+02 -5.150740E+02 4.625632E+01 6.064994E+02 -7.669501E+02 3.852582E+02 + 5.258554E+02 -8.796696E+02 -5.538371E+02 3.002062E+02 4.721767E+02 -3.049051E+02 -8.939550E+02 -7.013254E+02 -2.149750E+02 -4.278936E+00 + 6.686011E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 18 + 6.116009E+03 -8.681257E+02 -6.575581E+03 -5.635625E+03 3.742945E+03 9.401015E+03 -1.532310E+03 -1.240633E+04 5.344991E+03 1.378091E+04 + -1.901138E+04 -3.403434E+02 3.020215E+04 -4.190898E+04 6.467518E+03 8.333652E+04 -1.986101E+05 2.637577E+05 -2.056058E+05 6.938786E+04 + 3.013390E+04 -4.364856E+04 7.000918E+03 2.032726E+04 -1.618140E+04 -2.330662E+03 1.201084E+04 -6.695672E+03 -4.234164E+03 8.043001E+03 + -2.182892E+03 -4.972195E+03 5.270027E+03 2.511179E+02 -4.430294E+03 3.393752E+03 9.320369E+02 -4.776946E+03 4.874521E+03 -1.837572E+03 + -6.430057E+02 8.080781E+02 1.656412E+02 -8.954959E+02 8.341775E+02 -1.117365E+02 -2.971842E+02 1.839522E+02 -2.825948E+01 -3.865583E+02 + 6.760476E+02 -5.376386E+02 3.760631E+01 4.672014E+02 -7.603964E+02 3.526937E+02 2.677273E+02 -3.944750E+02 9.529512E+01 -2.794277E+02 + -2.746483E+02 9.328236E+02 8.477364E+02 -1.030824E+03 -6.680245E+02 1.061312E+03 1.296658E+03 7.532803E+02 3.543349E+02 1.989733E+02 + 1.186130E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 19 + -3.619476E+03 1.695702E+03 4.432393E+03 1.776155E+03 -4.789412E+03 -4.623745E+03 5.304756E+03 5.849106E+03 -8.858387E+03 -2.309051E+03 + 1.407995E+04 -1.264313E+04 -5.207071E+03 2.976264E+04 -3.874996E+04 4.786344E+03 8.656252E+04 -2.056058E+05 2.600632E+05 -1.936109E+05 + 6.337412E+04 2.864076E+04 -3.815921E+04 4.364558E+03 1.791497E+04 -1.301080E+04 -2.292275E+03 1.011534E+04 -5.598424E+03 -3.288666E+03 + 6.246136E+03 -1.793441E+03 -3.124380E+03 3.535501E+03 -5.866781E+02 -2.451204E+03 2.670010E+03 1.478555E+02 -2.987396E+03 3.313657E+03 + -1.627282E+03 3.545148E+01 4.233889E+02 -1.129870E+02 -8.246382E+01 6.860767E+01 -7.791521E+01 7.739537E+01 -8.461286E+01 3.487432E+02 + -6.298529E+02 4.134659E+02 2.093770E+02 -6.377236E+02 4.663964E+02 1.095143E+02 -4.546178E+02 -9.889580E+00 3.943020E+02 1.343696E+02 + -4.571290E+01 -3.728794E+02 7.135108E+01 9.357845E+02 1.679002E+02 -5.262697E+02 -2.401627E+02 -8.950693E+00 -1.427068E+02 -1.561116E+02 + -7.751671E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 20 + -1.516726E+03 -8.567849E+02 1.002162E+03 2.740391E+03 1.638407E+03 -2.856637E+03 -3.733124E+03 3.611154E+03 4.072786E+03 -7.357514E+03 + 9.884851E+02 9.885502E+03 -1.378189E+04 2.334968E+03 2.101673E+04 -3.614136E+04 1.433750E+04 6.938786E+04 -1.936109E+05 2.677189E+05 + -2.115886E+05 6.742586E+04 3.628963E+04 -4.471657E+04 5.873938E+03 2.073054E+04 -1.695564E+04 -2.126730E+03 1.344746E+04 -7.686745E+03 + -4.600693E+03 9.075541E+03 -3.256531E+03 -4.654552E+03 6.427660E+03 -1.471886E+03 -4.253911E+03 5.976014E+03 -3.226403E+03 -6.506802E+02 + 1.906352E+03 -7.172333E+02 -6.635968E+02 1.233010E+03 -1.011291E+03 2.542220E+02 1.583515E+02 -2.251703E+01 -1.502468E+02 2.518376E+02 + -2.042472E+02 2.195104E+02 -2.492236E+02 4.324386E+01 4.608926E+02 -5.026626E+02 1.654759E+02 4.409340E+02 -5.245067E+02 -2.893952E+01 + 1.823389E+02 -7.280188E+02 -9.163272E+02 2.411676E+02 3.370530E+02 -4.984348E+02 -6.444252E+02 -3.849158E+02 -9.959844E+01 -2.664689E+01 + -2.173267E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 21 + 4.578316E+03 -8.818320E+02 -4.887174E+03 -3.786383E+03 2.723654E+03 6.294862E+03 -9.530615E+02 -7.602245E+03 3.030438E+03 7.798549E+03 + -1.027980E+04 2.307018E+02 1.387320E+04 -1.945115E+04 7.566906E+03 1.928452E+04 -4.416962E+04 3.013390E+04 6.337412E+04 -2.115886E+05 + 2.929183E+05 -2.137722E+05 5.398555E+04 4.371178E+04 -4.266034E+04 -5.827273E+02 2.433862E+04 -1.430840E+04 -6.887646E+03 1.381093E+04 + -3.948919E+03 -7.479886E+03 8.028271E+03 -7.550327E+01 -6.117929E+03 5.186605E+03 7.827479E+02 -6.414943E+03 7.317396E+03 -3.968802E+03 + 4.744715E+02 6.810541E+02 -1.159281E+02 -9.648445E+02 1.313142E+03 -7.146773E+02 2.473322E+02 -3.224082E+02 4.780037E+02 -6.521950E+02 + 6.166434E+02 -2.204922E+02 -2.754349E+02 5.976511E+02 -6.435554E+02 7.468124E+01 4.494995E+02 -4.448030E+02 1.541485E+02 6.504836E+01 + -3.679584E+02 8.944190E+02 8.248985E+02 -8.772888E+02 -4.748742E+02 5.334000E+02 4.025416E+02 8.513971E+01 1.209497E+02 1.217372E+02 + 6.002556E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 22 + -2.589736E+03 1.444248E+03 3.389714E+03 1.142024E+03 -3.816599E+03 -3.371615E+03 3.671040E+03 3.949324E+03 -5.364446E+03 -1.084179E+03 + 6.872366E+03 -6.344820E+03 -8.718256E+02 1.117059E+04 -1.672502E+04 8.072165E+03 1.743875E+04 -4.364856E+04 2.864076E+04 6.742586E+04 + -2.137722E+05 2.837356E+05 -2.036736E+05 5.514418E+04 3.693344E+04 -3.816071E+04 2.149315E+03 1.934986E+04 -1.306415E+04 -3.935268E+03 + 1.083168E+04 -4.454872E+03 -4.153903E+03 6.259816E+03 -1.866198E+03 -3.568630E+03 4.506091E+03 -1.033158E+03 -2.905052E+03 4.014007E+03 + -1.984820E+03 -4.282762E+02 1.299210E+03 -5.453242E+02 -5.655821E+02 9.664208E+02 -6.090701E+02 1.529046E+02 -3.164962E+01 1.222194E+02 + -1.013905E+02 -2.009566E+02 4.084717E+02 -2.957568E+02 -1.422080E+02 4.639968E+02 -3.857191E+02 -1.139047E+02 1.518398E+02 -5.781680E+01 + 4.113800E+02 1.139570E+02 -3.379433E+02 1.401668E+02 6.133615E+02 4.456981E+02 5.244984E+01 -7.760893E+01 -1.310418E+02 -6.715684E+01 + -1.208096E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 23 + -1.828415E+03 -1.938181E+02 1.385167E+03 1.945067E+03 5.693173E+02 -2.043716E+03 -1.747721E+03 2.383212E+03 1.447945E+03 -4.402279E+03 + 2.373374E+03 4.149270E+03 -8.928191E+03 5.541806E+03 5.766844E+03 -1.614598E+04 1.420672E+04 7.000918E+03 -3.815921E+04 3.628963E+04 + 5.398555E+04 -2.036736E+05 2.844802E+05 -2.137135E+05 6.265727E+04 3.781206E+04 -4.608016E+04 5.816237E+03 2.325307E+04 -1.698697E+04 + -4.349560E+03 1.431416E+04 -7.200170E+03 -5.233924E+03 9.731027E+03 -3.864398E+03 -4.796759E+03 8.971418E+03 -6.658634E+03 1.531514E+03 + 1.115056E+03 -5.276903E+02 -9.518772E+02 1.575409E+03 -9.301072E+02 1.480360E+00 7.261002E+01 4.376270E+02 -6.805856E+02 5.771980E+02 + -3.545099E+02 5.941344E+01 2.649908E+02 -5.049864E+02 5.116120E+02 2.292664E+01 -5.855478E+02 4.818589E+02 2.375208E+02 -3.077329E+02 + -1.583465E+02 -4.502767E+02 -1.185570E+02 5.850112E+02 -3.330973E+02 -9.717697E+02 -3.051648E+02 1.745900E+02 8.661791E+01 -1.168987E+01 + -5.716556E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 24 + 3.840229E+03 -1.054460E+03 -4.261050E+03 -2.807081E+03 2.915694E+03 4.912784E+03 -1.519765E+03 -5.537267E+03 3.051857E+03 4.382207E+03 + -6.779696E+03 1.374676E+03 6.874280E+03 -1.107951E+04 6.902683E+03 4.935754E+03 -1.844214E+04 2.032726E+04 4.364558E+03 -4.471657E+04 + 4.371178E+04 5.514418E+04 -2.137135E+05 2.947096E+05 -2.135770E+05 5.631389E+04 4.187292E+04 -4.316957E+04 -4.160255E+02 2.418178E+04 + -1.317596E+04 -7.552279E+03 1.371272E+04 -4.198397E+03 -7.198979E+03 9.074832E+03 -1.434509E+03 -7.333202E+03 9.833333E+03 -5.721206E+03 + 3.237977E+02 1.854062E+03 -8.517587E+02 -1.169036E+03 2.074319E+03 -1.669210E+03 9.163709E+02 -5.473583E+02 5.602717E+02 -4.709922E+02 + 1.549714E+02 2.589277E+02 -5.711405E+02 6.251659E+02 -6.159936E+01 -7.501234E+02 1.024688E+03 -1.628674E+02 -6.230641E+02 5.486200E+02 + -1.125142E+02 -3.585083E+02 3.275250E+02 -1.807132E+02 -5.684761E+02 4.701060E+01 5.150383E+02 4.253987E+02 2.489598E+02 7.433609E+01 + -1.053061E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 25 + -1.426894E+03 7.564670E+02 2.163078E+03 9.673536E+02 -2.458358E+03 -2.663023E+03 1.991160E+03 2.918299E+03 -2.824925E+03 -6.875876E+02 + 3.049580E+03 -3.411181E+03 1.333194E+03 3.513914E+03 -8.093936E+03 7.142278E+03 2.666417E+03 -1.618140E+04 1.791497E+04 5.873938E+03 + -4.266034E+04 3.693344E+04 6.265727E+04 -2.135770E+05 2.803278E+05 -2.016661E+05 6.065413E+04 3.316609E+04 -4.046849E+04 3.367338E+03 + 2.007325E+04 -1.340654E+04 -3.692210E+03 1.131285E+04 -5.687855E+03 -3.823476E+03 7.262093E+03 -3.983737E+03 -1.113626E+03 3.206843E+03 + -1.186847E+03 -1.428342E+03 1.760418E+03 -2.304234E+02 -1.213969E+03 1.536049E+03 -8.415618E+02 9.964006E+01 4.405703E+01 2.825706E+01 + 7.388917E+01 -1.909990E+02 1.878781E+02 -9.416094E+01 -3.088632E+02 4.952962E+02 -1.108527E+02 -3.323768E+02 8.523176E+01 -3.219652E+01 + 1.783004E+02 5.507287E+02 6.707586E+01 -2.246257E+02 6.029855E+02 6.576328E+02 -2.761072E+02 -6.023638E+02 -3.424891E+02 -1.228500E+02 + -6.233144E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 26 + -2.326556E+03 2.845493E+02 2.149608E+03 1.856436E+03 -5.970859E+02 -2.381862E+03 -3.977436E+02 2.613949E+03 -3.020348E+02 -3.035694E+03 + 2.917800E+03 1.586896E+03 -6.178194E+03 5.529869E+03 9.087941E+02 -8.353136E+03 1.026948E+04 -2.330662E+03 -1.301080E+04 2.073054E+04 + -5.827273E+02 -3.816071E+04 3.781206E+04 5.631389E+04 -2.016661E+05 2.784539E+05 -2.149950E+05 6.554051E+04 4.101859E+04 -4.684938E+04 + 2.315138E+03 2.539945E+04 -1.726184E+04 -5.099490E+03 1.576157E+04 -8.491056E+03 -5.172962E+03 1.293030E+04 -1.048868E+04 2.857654E+03 + 1.764739E+03 -1.508514E+03 -5.511264E+02 1.930975E+03 -1.953958E+03 1.434930E+03 -9.072092E+02 6.046225E+02 -4.805654E+02 1.635023E+02 + 1.563816E+01 -4.565334E+01 1.696542E+02 -2.944704E+02 1.512512E+02 3.414546E+02 -9.819092E+02 4.298767E+02 6.895607E+02 -6.433962E+02 + -6.925147E+01 1.717466E+02 -5.757958E+02 -3.827828E+01 5.085134E+02 3.250162E+01 -3.136661E+02 -2.026794E+02 -9.587305E+01 3.020696E+00 + 5.556053E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 27 + 3.080928E+03 -6.149256E+02 -3.512527E+03 -2.696350E+03 2.275234E+03 4.475975E+03 -9.753498E+02 -4.882138E+03 2.190174E+03 3.607273E+03 + -4.678542E+03 8.455511E+02 4.042856E+03 -6.594205E+03 4.779480E+03 1.293013E+03 -9.078757E+03 1.201084E+04 -2.292275E+03 -1.695564E+04 + 2.433862E+04 2.149315E+03 -4.608016E+04 4.187292E+04 6.065413E+04 -2.149950E+05 2.910581E+05 -2.100734E+05 5.336344E+04 4.274099E+04 + -4.032148E+04 -3.156464E+03 2.439109E+04 -1.238384E+04 -8.031726E+03 1.422288E+04 -4.665798E+03 -7.710803E+03 1.148448E+04 -6.108175E+03 + -7.663931E+02 3.325930E+03 -1.467817E+03 -1.786462E+03 3.472116E+03 -3.291918E+03 1.911768E+03 -7.583479E+02 4.726432E+02 -1.902740E+02 + -9.658644E+01 2.237345E+02 -3.206486E+02 2.701076E+02 2.038219E+02 -6.758306E+02 7.144956E+02 -1.285148E+01 -5.004550E+02 4.489205E+02 + 1.104849E+01 -4.772436E+02 1.969072E+02 1.167021E+02 -7.558642E+02 -5.836969E+02 3.329155E+02 5.916004E+02 3.736568E+02 1.728098E+02 + 9.073860E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 28 + -4.400904E+02 3.037360E+02 9.541723E+02 4.779007E+02 -1.303661E+03 -1.409990E+03 1.051913E+03 1.486753E+03 -1.287691E+03 -4.126932E+02 + 1.172025E+03 -1.773808E+03 1.886527E+03 4.513054E+02 -4.119724E+03 5.209906E+03 -8.769124E+02 -6.695672E+03 1.011534E+04 -2.126730E+03 + -1.430840E+04 1.934986E+04 5.816237E+03 -4.316957E+04 3.316609E+04 6.554051E+04 -2.100734E+05 2.809200E+05 -2.071908E+05 5.857517E+04 + 3.870019E+04 -4.151902E+04 2.540518E+03 2.099722E+04 -1.465547E+04 -2.961011E+03 1.160119E+04 -8.467030E+03 1.014562E+03 3.329947E+03 + -2.340150E+03 -5.307955E+02 1.883972E+03 -1.097093E+03 -2.583331E+02 7.173565E+02 -3.163206E+02 7.212235E+01 -1.926548E+02 2.704522E+02 + -2.578547E+01 -2.812764E+02 3.972338E+02 -1.549532E+02 -2.543198E+02 2.660386E+02 3.945515E+02 -3.842819E+02 -3.779554E+02 2.304848E+02 + -4.738738E+01 3.066251E+01 6.536206E+02 4.029095E+02 -6.221326E+01 -2.596301E+01 -7.764826E+01 -2.293618E+02 -1.544233E+02 -8.780686E+01 + -8.030786E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 29 + -2.187145E+03 2.918184E+01 2.081994E+03 2.026938E+03 -7.639962E+02 -2.584970E+03 1.693117E+02 2.945248E+03 -1.122567E+03 -2.839560E+03 + 3.293141E+03 7.525974E+02 -4.973191E+03 4.977509E+03 -5.869012E+02 -4.990883E+03 7.755184E+03 -4.234164E+03 -5.598424E+03 1.344746E+04 + -6.887646E+03 -1.306415E+04 2.325307E+04 -4.160255E+02 -4.046849E+04 4.101859E+04 5.336344E+04 -2.071908E+05 2.895217E+05 -2.150098E+05 + 5.730484E+04 4.441384E+04 -4.595536E+04 1.345428E+03 2.602884E+04 -1.838268E+04 -3.964585E+03 1.815833E+04 -1.569137E+04 4.124921E+03 + 3.600460E+03 -3.819385E+03 -4.024570E+01 3.470088E+03 -4.120363E+03 3.359278E+03 -2.008465E+03 7.424788E+02 -2.172582E+02 -1.853262E+02 + 2.061683E+02 2.387416E+01 -1.038926E+02 -3.078258E+01 -2.224495E+01 3.692722E+02 -9.287329E+02 3.766474E+02 7.006434E+02 -5.479531E+02 + -3.204208E+01 4.467847E+02 -5.779678E+02 -8.237354E+02 1.888312E+02 7.019592E+02 3.094476E+02 -3.364283E+01 -1.264178E+02 -1.095483E+02 + -7.244638E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 30 + 2.271790E+03 -3.607873E+02 -2.485136E+03 -1.976725E+03 1.661965E+03 2.946196E+03 -1.045404E+03 -3.184100E+03 1.987188E+03 2.429275E+03 + -3.506122E+03 9.035941E+02 2.031848E+03 -3.829379E+03 3.672316E+03 -5.304617E+02 -4.960850E+03 8.043001E+03 -3.288666E+03 -7.686745E+03 + 1.381093E+04 -3.935268E+03 -1.698697E+04 2.418178E+04 3.367338E+03 -4.684938E+04 4.274099E+04 5.857517E+04 -2.150098E+05 2.941054E+05 + -2.118095E+05 5.323216E+04 4.245776E+04 -4.053820E+04 -1.557292E+03 2.356660E+04 -1.352614E+04 -6.523949E+03 1.483506E+04 -8.873794E+03 + -1.217568E+02 3.815671E+03 -1.964258E+03 -1.869302E+03 3.829524E+03 -3.645328E+03 2.123542E+03 -8.555220E+02 5.842992E+02 -3.549818E+02 + 4.332227E+01 2.206424E+02 -3.784724E+02 3.168226E+02 1.155531E+02 -5.318122E+02 4.586093E+02 3.973649E+01 -3.938854E+02 3.254567E+02 + 2.631060E+02 -6.138214E+02 -3.417970E+02 4.812474E+02 3.979519E+02 -2.472248E+02 -3.168277E+02 4.244658E+01 1.280932E+02 6.591929E+01 + 3.379556E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 31 + -1.009467E+02 4.126932E+02 3.759335E+02 -1.548308E+02 -7.359089E+02 -3.304388E+02 5.460701E+02 2.793544E+02 -3.896512E+02 2.030479E+02 + -1.254031E+02 -1.140643E+03 2.472806E+03 -1.145032E+03 -2.249626E+03 4.309259E+03 -2.552856E+03 -2.182892E+03 6.246136E+03 -4.600693E+03 + -3.948919E+03 1.083168E+04 -4.349560E+03 -1.317596E+04 2.007325E+04 2.315138E+03 -4.032148E+04 3.870019E+04 5.730484E+04 -2.118095E+05 + 2.861185E+05 -2.074516E+05 5.983817E+04 3.586249E+04 -4.251977E+04 6.614626E+03 1.882279E+04 -1.916588E+04 6.624607E+03 3.414173E+03 + -4.608244E+03 1.135206E+03 2.114070E+03 -2.913428E+03 1.867592E+03 -1.039821E+03 7.141129E+02 -2.717683E+02 -1.332376E+02 4.776839E+02 + -3.209695E+02 -1.587037E+02 4.028648E+02 -2.265253E+02 -1.616850E+02 1.348493E+02 4.259027E+02 -5.397565E+02 3.480511E+01 1.153993E+02 + -2.170667E+02 4.900939E+02 6.140743E+02 8.074636E+01 -2.292576E+02 -4.707331E+02 -5.011196E+02 -2.446622E+02 -1.829666E+00 5.440664E+01 + 3.392034E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 32 + -2.415270E+03 1.720966E+02 2.181412E+03 2.037878E+03 -9.569780E+02 -2.230427E+03 5.294041E+02 2.229196E+03 -1.556568E+03 -1.994723E+03 + 3.242807E+03 -1.858722E+02 -3.422045E+03 4.051099E+03 -1.493194E+03 -2.503314E+03 5.794813E+03 -4.972195E+03 -1.793441E+03 9.075541E+03 + -7.479886E+03 -4.454872E+03 1.431416E+04 -7.552279E+03 -1.340654E+04 2.539945E+04 -3.156464E+03 -4.151902E+04 4.441384E+04 5.323216E+04 + -2.074516E+05 2.873390E+05 -2.153890E+05 6.066734E+04 4.304219E+04 -4.839182E+04 4.673777E+03 2.815501E+04 -2.805440E+04 9.027788E+03 + 4.863728E+03 -5.935955E+03 8.288669E+01 5.308871E+03 -6.495120E+03 5.447486E+03 -3.348839E+03 1.350298E+03 -5.869769E+02 3.957584E+01 + 7.449689E+01 1.877478E+02 -3.265764E+02 1.108177E+02 6.320573E+01 1.963849E+02 -4.714504E+02 1.285338E+02 4.565443E+02 -3.721493E+02 + -4.357494E+02 2.783965E+02 -7.998869E+01 -6.646888E+02 -2.551363E+02 5.185369E+02 5.655400E+02 1.387992E+02 -2.626050E+01 -4.010964E+00 + 2.582561E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 33 + 2.222546E+03 -4.751226E+02 -2.302039E+03 -1.629872E+03 1.402766E+03 2.335122E+03 -8.216858E+02 -2.390603E+03 1.618496E+03 1.832330E+03 + -2.925248E+03 1.094181E+03 1.089181E+03 -2.594935E+03 3.008492E+03 -1.356737E+03 -2.511820E+03 5.270027E+03 -3.124380E+03 -3.256531E+03 + 8.028271E+03 -4.153903E+03 -7.200170E+03 1.371272E+04 -3.692210E+03 -1.726184E+04 2.439109E+04 2.540518E+03 -4.595536E+04 4.245776E+04 + 5.983817E+04 -2.153890E+05 2.896010E+05 -2.070916E+05 5.520128E+04 3.722801E+04 -3.973529E+04 3.520097E+03 1.872990E+04 -1.468907E+04 + 1.423337E+03 4.928018E+03 -3.474397E+03 -7.937478E+02 3.319140E+03 -3.485162E+03 2.050704E+03 -6.791393E+02 3.021599E+02 -7.428196E+01 + -1.195047E+02 7.224241E+01 1.171253E+02 -2.654836E+02 4.950350E+02 -4.341144E+02 -2.080240E+02 7.672704E+02 -8.111860E+02 2.232196E+02 + 8.747291E+02 -1.045586E+03 -3.582562E+02 1.178045E+03 3.819118E+02 -3.429584E+02 -8.003263E+00 1.351393E+02 -9.452540E+00 -2.823300E+01 + 4.084762E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 34 + 8.286865E+02 -5.583527E+01 -4.995758E+02 -6.353633E+02 5.396624E+01 2.788705E+02 8.982532E+01 -1.554263E+02 3.875838E+01 3.971708E+02 + -4.604130E+02 -6.429200E+02 1.837904E+03 -1.397588E+03 -7.471780E+02 2.792688E+03 -2.762220E+03 2.511179E+02 3.535501E+03 -4.654552E+03 + -7.550327E+01 6.259816E+03 -5.233924E+03 -4.198397E+03 1.131285E+04 -5.099490E+03 -1.238384E+04 2.099722E+04 1.345428E+03 -4.053820E+04 + 3.586249E+04 6.066734E+04 -2.070916E+05 2.788867E+05 -2.089932E+05 6.761596E+04 3.184705E+04 -5.057974E+04 2.393813E+04 2.786814E+03 + -9.086474E+03 2.863318E+03 4.076752E+03 -6.551981E+03 5.238926E+03 -3.437472E+03 1.998388E+03 -7.756626E+02 2.323618E+02 5.626375E-01 + 1.880724E+02 -5.282232E+02 6.294372E+02 -4.018514E+02 -1.707001E+02 3.406716E+02 7.425193E+01 -2.439437E+02 6.406552E+01 2.220816E+01 + 2.480557E+02 2.807696E+02 -1.041977E+02 -1.813978E+02 -1.776983E+02 -2.803456E+02 -1.177479E+02 9.499478E+01 4.711298E+01 -1.711055E+01 + -4.142519E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 35 + -2.635207E+03 3.281069E+02 2.612304E+03 2.103925E+03 -1.370198E+03 -2.706845E+03 6.885530E+02 2.810375E+03 -1.524620E+03 -2.496164E+03 + 3.396614E+03 -3.811830E+02 -2.804327E+03 3.678794E+03 -1.976993E+03 -1.379422E+03 4.517400E+03 -4.430294E+03 -5.866781E+02 6.427660E+03 + -6.117929E+03 -1.866198E+03 9.731027E+03 -7.198979E+03 -5.687855E+03 1.576157E+04 -8.031726E+03 -1.465547E+04 2.602884E+04 -1.557292E+03 + -4.251977E+04 4.304219E+04 5.520128E+04 -2.089932E+05 2.871911E+05 -2.142564E+05 6.280318E+04 4.126334E+04 -5.484946E+04 1.924352E+04 + 8.847901E+03 -1.138147E+04 1.330230E+03 7.527749E+03 -9.480486E+03 7.455727E+03 -4.030442E+03 1.061577E+03 -1.097646E+01 -3.677730E+02 + 1.441452E+02 5.669708E+02 -1.118985E+03 1.142268E+03 -6.824527E+02 8.550646E+01 5.674596E+02 -1.013044E+03 9.330368E+02 -7.195608E+01 + -1.553540E+03 1.081480E+03 1.012743E+03 -1.408439E+03 -7.429043E+02 5.659386E+02 3.838888E+02 7.287906E+01 8.889191E+01 4.938449E+01 + 8.273771E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 36 + 1.319142E+03 -5.602440E+01 -1.557571E+03 -1.140283E+03 8.505709E+02 1.836504E+03 -6.046638E+02 -2.043002E+03 1.417003E+03 1.518984E+03 + -2.625786E+03 1.077543E+03 8.604719E+02 -2.029532E+03 2.186110E+03 -9.827606E+02 -1.491730E+03 3.393752E+03 -2.451204E+03 -1.471886E+03 + 5.186605E+03 -3.568630E+03 -3.864398E+03 9.074832E+03 -3.823476E+03 -8.491056E+03 1.422288E+04 -2.961011E+03 -1.838268E+04 2.356660E+04 + 6.614626E+03 -4.839182E+04 3.722801E+04 6.761596E+04 -2.142564E+05 2.801029E+05 -2.024758E+05 6.662890E+04 1.789375E+04 -2.815176E+04 + 5.151343E+03 8.951047E+03 -8.424899E+03 1.910394E+03 2.127022E+03 -2.333881E+03 8.313473E+02 3.135060E+02 -5.092942E+02 4.355465E+02 + -2.604185E+02 -5.144863E+01 1.342786E+02 1.944522E+01 1.637036E+02 -3.688936E+02 2.351548E-01 4.468589E+02 -5.306869E+02 3.420590E+02 + 4.722982E+02 -8.015659E+02 -2.872891E+02 9.094235E+02 7.546327E+02 -5.953639E+01 -4.879574E+02 -3.972605E+02 -9.170197E+01 3.119438E+01 + 5.035124E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 37 + 1.082717E+03 -2.043244E+02 -1.226027E+03 -8.912525E+02 9.250748E+02 1.231303E+03 -4.286970E+02 -1.258219E+03 3.158346E+02 1.325269E+03 + -1.113349E+03 -4.056594E+02 1.712882E+03 -1.760059E+03 3.616141E+01 2.232973E+03 -2.845415E+03 9.320369E+02 2.670010E+03 -4.253911E+03 + 7.827479E+02 4.506091E+03 -4.796759E+03 -1.434509E+03 7.262093E+03 -5.172962E+03 -4.665798E+03 1.160119E+04 -3.964585E+03 -1.352614E+04 + 1.882279E+04 4.673777E+03 -3.973529E+04 3.184705E+04 6.280318E+04 -2.024758E+05 2.662430E+05 -2.083405E+05 8.986032E+04 1.736555E+03 + -2.469149E+04 1.055465E+04 6.294424E+03 -1.312820E+04 1.172837E+04 -8.627070E+03 5.341517E+03 -2.185247E+03 6.993512E+02 5.848601E+01 + -6.904944E+01 -5.523162E+02 1.332950E+03 -1.732609E+03 9.798199E+02 2.097347E+02 -1.055583E+03 1.173369E+03 -5.889965E+02 -6.323257E+02 + 1.442792E+03 -5.594334E+02 -1.533998E+03 6.463948E+02 6.977416E+02 -2.624105E+02 -1.292683E+02 1.146490E+02 1.184760E+01 -2.625832E+01 + -1.876564E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 38 + -2.210694E+03 1.850381E+02 2.565785E+03 1.827941E+03 -1.529088E+03 -2.863802E+03 1.005792E+03 3.260723E+03 -2.027362E+03 -2.586035E+03 + 3.981458E+03 -1.306325E+03 -2.316125E+03 4.341320E+03 -2.846789E+03 -1.206703E+03 4.604143E+03 -4.776946E+03 1.478555E+02 5.976014E+03 + -6.414943E+03 -1.033158E+03 8.971418E+03 -7.333202E+03 -3.983737E+03 1.293030E+04 -7.710803E+03 -8.467030E+03 1.815833E+04 -6.523949E+03 + -1.916588E+04 2.815501E+04 3.520097E+03 -5.057974E+04 4.126334E+04 6.662890E+04 -2.083405E+05 2.759358E+05 -2.095055E+05 6.815298E+04 + 2.108504E+04 -3.135895E+04 9.351611E+03 1.003289E+04 -1.485371E+04 1.152096E+04 -5.883871E+03 9.968396E+02 8.406935E+02 -1.401064E+03 + 1.007060E+03 4.343322E+02 -1.572827E+03 2.007907E+03 -1.360367E+03 2.792312E+02 1.097715E+03 -1.666430E+03 1.137918E+03 2.683134E+02 + -1.699984E+03 1.363849E+03 1.804469E+03 -1.342426E+03 -1.708057E+03 1.770610E+02 1.042791E+03 7.302878E+02 2.032218E+02 -1.272421E+01 + -4.500869E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 39 + 1.536458E+03 -1.928686E+01 -1.246418E+03 -1.222640E+03 1.437662E+02 1.495494E+03 -2.721060E+02 -1.727975E+03 1.698840E+03 9.074425E+02 + -2.766532E+03 1.863511E+03 7.817868E+02 -3.173048E+03 3.415325E+03 -1.061103E+03 -2.463134E+03 4.874521E+03 -2.987396E+03 -3.226403E+03 + 7.317396E+03 -2.905052E+03 -6.658634E+03 9.833333E+03 -1.113626E+03 -1.048868E+04 1.148448E+04 1.014562E+03 -1.569137E+04 1.483506E+04 + 6.624607E+03 -2.805440E+04 1.872990E+04 2.393813E+04 -5.484946E+04 1.789375E+04 8.986032E+04 -2.095055E+05 2.428787E+05 -1.514777E+05 + 4.093342E+04 9.614610E+03 -1.863108E+04 1.190281E+04 -7.046938E+03 7.587650E+03 -7.939404E+03 6.480936E+03 -4.693608E+03 2.199082E+03 + -2.926219E+02 -1.393859E+03 1.498297E+03 -5.089709E+02 -7.371198E+02 1.165211E+03 -1.268650E+03 2.001161E+02 4.328263E+02 -6.064292E+02 + 6.521522E+02 6.796972E+01 -1.407763E+03 8.526876E+02 2.272121E+03 3.125804E+01 -2.242249E+03 -2.201773E+03 -1.011928E+03 -2.848772E+02 + -8.917584E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 40 + -5.588026E+01 -1.181381E+02 -8.789123E+02 -3.316622E+01 1.380118E+03 1.093254E+03 -6.570037E+02 -1.635515E+03 1.875266E+01 1.847873E+03 + -6.464285E+02 -1.184033E+03 1.292742E+03 1.012999E+01 -1.722452E+03 2.194825E+03 -7.615294E+02 -1.837572E+03 3.313657E+03 -6.506802E+02 + -3.968802E+03 4.014007E+03 1.531514E+03 -5.721206E+03 3.206843E+03 2.857654E+03 -6.108175E+03 3.329947E+03 4.124921E+03 -8.873794E+03 + 3.414173E+03 9.027788E+03 -1.468907E+04 2.786814E+03 1.924352E+04 -2.815176E+04 1.736555E+03 6.815298E+04 -1.514777E+05 1.746428E+05 + -1.353848E+05 7.524336E+04 -1.268953E+04 -2.638292E+04 3.839303E+04 -3.843814E+04 2.899487E+04 -1.571724E+04 7.299013E+03 4.504916E+02 + -4.513104E+03 5.186135E+03 -2.488278E+03 -2.063333E+03 5.311697E+03 -5.184246E+03 2.336132E+03 2.299069E+03 -4.048286E+03 2.481758E+03 + 5.066252E+02 -3.402017E+03 2.246576E+03 4.943974E+02 -3.391502E+03 -4.404096E+02 3.378589E+03 3.471574E+03 1.797944E+03 6.308783E+02 + 2.582262E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 41 + -5.707036E+02 3.154145E+02 1.407845E+03 5.559246E+02 -1.586113E+03 -2.244365E+03 7.201448E+02 3.247126E+03 -7.562344E+02 -2.878684E+03 + 1.991879E+03 7.617136E+02 -1.901842E+03 1.263613E+03 2.667625E+02 -1.433606E+03 1.724783E+03 -6.430057E+02 -1.627282E+03 1.906352E+03 + 4.744715E+02 -1.984820E+03 1.115056E+03 3.237977E+02 -1.186847E+03 1.764739E+03 -7.663931E+02 -2.340150E+03 3.600460E+03 -1.217568E+02 + -4.608244E+03 4.863728E+03 1.423337E+03 -9.086474E+03 8.847901E+03 5.151343E+03 -2.469149E+04 2.108504E+04 4.093342E+04 -1.353848E+05 + 2.031355E+05 -1.923008E+05 1.003568E+05 -2.693489E+03 -4.745041E+04 5.951473E+04 -4.521215E+04 2.218290E+04 -7.396799E+03 -5.682214E+03 + 1.166197E+04 -1.068517E+04 5.026445E+03 3.388421E+03 -9.281928E+03 9.429334E+03 -4.212716E+03 -3.557080E+03 7.068212E+03 -4.772630E+03 + -7.518850E+02 6.121156E+03 -4.006619E+03 -1.753807E+03 4.915986E+03 1.171505E+03 -4.031668E+03 -4.399049E+03 -2.435899E+03 -8.933144E+02 + -3.730139E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 42 + 5.181592E+01 -3.548773E+02 -3.576121E+02 -8.886180E+01 6.680452E+02 1.201842E+03 -1.310124E+02 -1.779334E+03 -2.321956E+01 1.608433E+03 + -7.014959E+02 -6.923828E+02 8.547571E+02 -3.482832E+02 7.074223E+01 1.145665E+02 -5.936828E+02 8.080781E+02 3.545149E+01 -7.172333E+02 + 6.810541E+02 -4.282762E+02 -5.276903E+02 1.854062E+03 -1.428342E+03 -1.508514E+03 3.325930E+03 -5.307955E+02 -3.819385E+03 3.815671E+03 + 1.135206E+03 -5.935955E+03 4.928018E+03 2.863318E+03 -1.138147E+04 8.951047E+03 1.055465E+04 -3.135895E+04 9.614610E+03 7.524336E+04 + -1.923008E+05 2.578381E+05 -2.069366E+05 8.962348E+04 4.493592E+03 -4.682454E+04 4.393181E+04 -2.381197E+04 8.168790E+03 6.815602E+03 + -1.416524E+04 1.367033E+04 -8.177866E+03 -8.981680E+02 8.262125E+03 -1.009498E+04 5.848277E+03 1.935064E+03 -6.676556E+03 5.535602E+03 + -2.660405E+02 -5.940271E+03 4.801760E+03 1.715332E+03 -5.107652E+03 -1.289362E+03 3.703351E+03 4.183909E+03 2.333453E+03 8.171218E+02 + 3.180577E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 43 + 1.911940E+02 9.903656E+01 -4.778858E+02 -1.452192E+02 3.593281E+02 3.769159E+02 -3.794716E+02 -6.727020E+02 8.204024E+02 4.160669E+02 + -1.027550E+03 6.107972E+02 2.267933E+02 -7.070146E+02 1.919148E+02 4.396169E+02 -5.575644E+02 1.656412E+02 4.233889E+02 -6.635968E+02 + -1.159281E+02 1.299210E+03 -9.518772E+02 -8.517587E+02 1.760418E+03 -5.511264E+02 -1.467817E+03 1.883972E+03 -4.024570E+01 -1.964258E+03 + 2.114070E+03 8.288669E+01 -3.474397E+03 4.076752E+03 1.330230E+03 -8.424899E+03 6.294424E+03 9.351611E+03 -1.863108E+04 -1.268953E+04 + 1.003568E+05 -2.069366E+05 2.506186E+05 -1.950644E+05 9.293993E+04 -1.648667E+04 -1.312754E+04 1.722334E+04 -1.122518E+04 1.565388E+03 + 5.570935E+03 -9.219917E+03 8.900281E+03 -4.678066E+03 -9.045626E+02 4.846357E+03 -5.122304E+03 1.720788E+03 2.151443E+03 -3.330450E+03 + 1.575427E+03 2.073324E+03 -2.783624E+03 -3.325277E+02 2.197954E+03 2.056208E+02 -1.592135E+03 -1.383590E+03 -5.123261E+02 -7.495600E+01 + 3.611137E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 44 + 8.446044E+01 2.275933E+02 4.173228E+02 -6.718734E+00 -7.957866E+02 -1.041051E+03 5.000720E+02 1.616755E+03 -6.041818E+02 -1.475546E+03 + 1.384265E+03 -1.064718E+02 -7.565380E+02 1.014070E+03 -3.651697E+02 -4.060160E+02 8.669632E+02 -8.954959E+02 -1.129870E+02 1.233010E+03 + -9.648445E+02 -5.453242E+02 1.575409E+03 -1.169036E+03 -2.304234E+02 1.930975E+03 -1.786462E+03 -1.097093E+03 3.470088E+03 -1.869302E+03 + -2.913428E+03 5.308871E+03 -7.937478E+02 -6.551981E+03 7.527749E+03 1.910394E+03 -1.312820E+04 1.003289E+04 1.190281E+04 -2.638292E+04 + -2.693489E+03 8.962348E+04 -1.950644E+05 2.380966E+05 -1.867558E+05 1.058134E+05 -4.133165E+04 8.264597E+02 1.130695E+04 -1.433836E+04 + 1.086585E+04 -2.954194E+03 -3.868129E+03 8.038859E+03 -7.674448E+03 3.593754E+03 1.327280E+03 -4.316470E+03 3.472931E+03 -7.896628E+02 + -1.902700E+03 2.744126E+03 -1.006349E+03 -9.888410E+02 2.332541E+03 9.915276E+02 -1.592604E+03 -2.234177E+03 -1.419746E+03 -5.396131E+02 + -2.108483E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 45 + -1.257590E+02 -4.640156E+02 -1.631531E+02 5.479250E+01 7.125498E+02 9.966101E+02 -2.138214E+02 -1.351508E+03 -1.636700E+02 1.369794E+03 + -4.323270E+02 -8.060197E+02 1.077032E+03 -7.541964E+02 1.056854E+00 5.839170E+02 -7.554769E+02 8.341775E+02 -8.246382E+01 -1.011291E+03 + 1.313142E+03 -5.655821E+02 -9.301072E+02 2.074319E+03 -1.213969E+03 -1.953958E+03 3.472116E+03 -2.583331E+02 -4.120363E+03 3.829524E+03 + 1.867592E+03 -6.495120E+03 3.319140E+03 5.238926E+03 -9.480486E+03 2.127022E+03 1.172837E+04 -1.485371E+04 -7.046938E+03 3.839303E+04 + -4.745041E+04 4.493592E+03 9.293993E+04 -1.867558E+05 2.057460E+05 -1.673650E+05 9.807302E+04 -3.295484E+04 2.846362E+03 1.696458E+04 + -2.262031E+04 1.687814E+04 -7.126399E+03 -3.939794E+03 1.049533E+04 -1.007280E+04 4.350721E+03 3.052808E+03 -6.332544E+03 4.815209E+03 + 2.442509E+02 -5.088490E+03 4.280594E+03 9.402796E+02 -5.224381E+03 -9.724981E+02 4.093919E+03 4.228439E+03 2.086086E+03 6.118986E+02 + 1.901878E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 46 + -1.893059E+02 5.402209E+02 3.174864E+02 9.789500E+01 -6.125462E+02 -9.311712E+02 -2.002274E+02 9.797008E+02 8.028352E+02 -7.226645E+02 + -6.468067E+02 1.184511E+03 -8.948117E+02 2.561284E+02 4.408474E+02 -7.087482E+02 2.690681E+02 -1.117365E+02 6.860767E+01 2.542220E+02 + -7.146773E+02 9.664208E+02 1.480360E+00 -1.669210E+03 1.536049E+03 1.434930E+03 -3.291918E+03 7.173565E+02 3.359278E+03 -3.645328E+03 + -1.039821E+03 5.447486E+03 -3.485162E+03 -3.437472E+03 7.455727E+03 -2.333881E+03 -8.627070E+03 1.152096E+04 7.587650E+03 -3.843814E+04 + 5.951473E+04 -4.682454E+04 -1.648667E+04 1.058134E+05 -1.673650E+05 1.911289E+05 -1.507586E+05 8.376098E+04 -4.150586E+04 3.383329E+03 + 1.813054E+04 -2.557045E+04 2.135040E+04 -8.734283E+03 -4.392736E+03 1.143527E+04 -1.003888E+04 2.448372E+03 4.442884E+03 -7.131442E+03 + 3.225157E+03 3.725087E+03 -5.958761E+03 9.341782E+02 5.474704E+03 -6.148172E+02 -5.021172E+03 -4.013854E+03 -1.684322E+03 -4.679525E+02 + -1.728436E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 47 + 1.412876E+02 -3.053520E+02 -3.201877E+02 -1.341349E+02 4.534478E+02 6.150696E+02 3.107704E+02 -4.092662E+02 -8.239633E+02 -1.960576E+01 + 7.429124E+02 -3.933476E+02 2.679703E+02 -1.810477E+02 -1.646449E+02 3.994010E+02 8.576045E+01 -2.971842E+02 -7.791521E+01 1.583515E+02 + 2.473322E+02 -6.090701E+02 7.261002E+01 9.163709E+02 -8.415618E+02 -9.072092E+02 1.911768E+03 -3.163206E+02 -2.008465E+03 2.123542E+03 + 7.141129E+02 -3.348839E+03 2.050704E+03 1.998388E+03 -4.030442E+03 8.313473E+02 5.341517E+03 -5.883871E+03 -7.939404E+03 2.899487E+04 + -4.521215E+04 4.393181E+04 -1.312754E+04 -4.133165E+04 9.807302E+04 -1.507586E+05 1.507409E+05 -1.153111E+05 8.249632E+04 -4.175592E+04 + 9.023443E+03 1.618286E+04 -2.505319E+04 2.030254E+04 -7.498843E+03 -4.955797E+03 1.044207E+04 -8.151875E+03 1.501406E+03 5.241649E+03 + -5.747365E+03 4.788040E+02 4.466509E+03 -2.791543E+03 -2.806438E+03 2.014157E+03 3.182492E+03 1.584763E+03 4.098878E+02 1.237661E+02 + 1.141214E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 48 + 2.746734E+02 7.384358E+01 -4.823024E+00 -1.908151E+02 -2.403460E+02 -3.943660E+01 -1.058421E+02 -1.593292E+02 3.145625E+02 5.927827E+02 + -7.703605E+01 -9.266528E+02 3.900030E+02 5.482874E+02 -6.846734E+02 1.118986E+02 -3.370348E+01 1.839522E+02 7.739537E+01 -2.251703E+01 + -3.224082E+02 1.529046E+02 4.376270E+02 -5.473583E+02 9.964006E+01 6.046225E+02 -7.583479E+02 7.212235E+01 7.424788E+02 -8.555220E+02 + -2.717683E+02 1.350298E+03 -6.791393E+02 -7.756626E+02 1.061577E+03 3.135060E+02 -2.185247E+03 9.968396E+02 6.480936E+03 -1.571724E+04 + 2.218290E+04 -2.381197E+04 1.722334E+04 8.264597E+02 -3.295484E+04 8.376098E+04 -1.153111E+05 1.293058E+05 -1.264903E+05 1.008414E+05 + -6.287462E+04 1.571345E+04 1.374108E+04 -2.680324E+04 2.229378E+04 -7.897331E+03 -5.531825E+03 1.253773E+04 -9.612541E+03 -2.737792E+02 + 6.880118E+03 -5.912983E+03 -8.467853E+02 3.869518E+03 -1.236984E+03 -2.350973E+03 2.626871E+02 1.459547E+03 9.761500E+02 3.330461E+02 + 8.775911E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 49 + -3.985927E+02 7.017424E+01 1.715780E+02 2.968303E+02 8.232834E+00 -1.989666E+02 -7.996314E+01 3.774077E+02 1.860373E+02 -8.402548E+02 + -2.851726E+02 1.417729E+03 -6.878274E+02 -5.778606E+02 9.323676E+02 -3.125598E+02 -1.936921E+01 -2.825948E+01 -8.461286E+01 -1.502468E+02 + 4.780037E+02 -3.164962E+01 -6.805856E+02 5.602717E+02 4.405703E+01 -4.805654E+02 4.726432E+02 -1.926548E+02 -2.172582E+02 5.842992E+02 + -1.332376E+02 -5.869769E+02 3.021599E+02 2.323618E+02 -1.097646E+01 -5.092942E+02 6.993512E+02 8.406935E+02 -4.693608E+03 7.299013E+03 + -7.396799E+03 8.168790E+03 -1.122518E+04 1.130695E+04 2.846362E+03 -4.150586E+04 8.249632E+04 -1.264903E+05 1.527323E+05 -1.521818E+05 + 1.192465E+05 -5.788385E+04 1.078504E+04 2.196280E+04 -3.072415E+04 1.997135E+04 -2.375148E+03 -1.252477E+04 1.485658E+04 -4.975354E+03 + -6.013344E+03 9.430272E+03 -2.763314E+03 -4.048170E+03 4.385616E+03 2.305697E+03 -2.747664E+03 -3.533044E+03 -1.946992E+03 -6.823158E+02 + -2.739527E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 50 + 2.895858E+02 -1.941749E+02 -2.744306E+02 -1.520575E+02 3.226830E+02 2.847715E+02 1.613701E+01 -4.511228E+02 -3.411104E+02 7.861205E+02 + 2.144473E+02 -1.089081E+03 6.472534E+02 3.153652E+02 -6.879562E+02 3.237511E+02 1.023792E+02 -3.865583E+02 3.487432E+02 2.518376E+02 + -6.521950E+02 1.222194E+02 5.771980E+02 -4.709922E+02 2.825706E+01 1.635023E+02 -1.902740E+02 2.704522E+02 -1.853262E+02 -3.549818E+02 + 4.776839E+02 3.957584E+01 -7.428196E+01 5.626376E-01 -3.677730E+02 4.355465E+02 5.848601E+01 -1.401064E+03 2.199082E+03 4.504916E+02 + -5.682214E+03 6.815602E+03 1.565388E+03 -1.433836E+04 1.696458E+04 3.383329E+03 -4.175592E+04 1.008414E+05 -1.521818E+05 1.884583E+05 + -1.771969E+05 1.161157E+05 -5.722194E+04 1.869305E+03 2.874983E+04 -3.091240E+04 1.496351E+04 6.132775E+03 -1.638234E+04 1.098054E+04 + 2.166659E+03 -1.090202E+04 6.743132E+03 3.327005E+03 -7.006690E+03 -1.901503E+03 4.872626E+03 5.229934E+03 2.772500E+03 9.648701E+02 + 4.174375E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 51 + 7.316291E+01 1.787215E+02 1.529122E+02 -1.406309E+02 -4.769502E+02 -1.732489E+02 2.189924E+02 3.000757E+02 1.229393E+02 -4.203222E+02 + 6.986558E+01 3.326483E+02 -3.427102E+02 -2.315522E+01 3.017098E+02 -2.917801E+02 -7.342350E+01 6.760476E+02 -6.298529E+02 -2.042472E+02 + 6.166434E+02 -1.013905E+02 -3.545099E+02 1.549714E+02 7.388917E+01 1.563816E+01 -9.658644E+01 -2.578547E+01 2.061683E+02 4.332227E+01 + -3.209695E+02 7.449689E+01 -1.195047E+02 1.880724E+02 1.441452E+02 -2.604185E+02 -6.904944E+01 1.007060E+03 -2.926219E+02 -4.513104E+03 + 1.166197E+04 -1.416524E+04 5.570935E+03 1.086585E+04 -2.262031E+04 1.813054E+04 9.023443E+03 -6.287462E+04 1.192465E+05 -1.771969E+05 + 1.926761E+05 -1.540104E+05 1.030517E+05 -3.885823E+04 -1.050353E+04 3.076938E+04 -2.438518E+04 4.551190E+03 1.128741E+04 -1.355365E+04 + 3.065028E+03 8.244079E+03 -8.536627E+03 -1.314270E+03 7.391906E+03 8.324428E+02 -5.646622E+03 -5.267478E+03 -2.541790E+03 -8.507942E+02 + -3.864268E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 52 + -6.115992E+02 -3.519725E+01 2.706263E+02 4.304804E+02 3.074604E+02 -1.322513E+02 -2.876959E+02 8.157139E+01 1.066562E+02 -3.309248E+02 + -3.883396E+01 5.444882E+02 -3.538027E+02 -6.329654E+01 2.141704E+02 -4.894492E+01 1.042985E+02 -5.376386E+02 4.134659E+02 2.195104E+02 + -2.204922E+02 -2.009566E+02 5.941344E+01 2.589277E+02 -1.909990E+02 -4.565334E+01 2.237345E+02 -2.812764E+02 2.387416E+01 2.206424E+02 + -1.587037E+02 1.877478E+02 7.224241E+01 -5.282232E+02 5.669708E+02 -5.144863E+01 -5.523162E+02 4.343322E+02 -1.393859E+03 5.186135E+03 + -1.068517E+04 1.367033E+04 -9.219917E+03 -2.954194E+03 1.687814E+04 -2.557045E+04 1.618286E+04 1.571345E+04 -5.788385E+04 1.161157E+05 + -1.540104E+05 1.593280E+05 -1.442664E+05 9.506310E+04 -3.404316E+04 -1.096002E+04 2.589424E+04 -1.888429E+04 2.674921E+03 1.017102E+04 + -9.515501E+03 -2.357010E+02 6.695280E+03 -2.698750E+03 -4.391894E+03 1.415827E+03 4.459878E+03 3.263917E+03 1.281197E+03 3.529412E+02 + 1.512268E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 53 + 5.008016E+02 -1.447181E+02 -3.396040E+02 -2.492464E+02 1.260773E+02 3.362046E+02 -6.653014E+01 -5.669192E+02 1.987499E+01 9.894223E+02 + -2.339194E+02 -1.041534E+03 8.228053E+02 1.952648E+02 -7.259474E+02 4.219584E+02 -9.626980E+01 3.760631E+01 2.093770E+02 -2.492236E+02 + -2.754349E+02 4.084717E+02 2.649908E+02 -5.711405E+02 1.878781E+02 1.696542E+02 -3.206486E+02 3.972338E+02 -1.038926E+02 -3.784724E+02 + 4.028648E+02 -3.265764E+02 1.171253E+02 6.294372E+02 -1.118985E+03 1.342786E+02 1.332950E+03 -1.572827E+03 1.498297E+03 -2.488278E+03 + 5.026445E+03 -8.177866E+03 8.900281E+03 -3.868129E+03 -7.126399E+03 2.135040E+04 -2.505319E+04 1.374108E+04 1.078504E+04 -5.722194E+04 + 1.030517E+05 -1.442664E+05 1.759205E+05 -1.632962E+05 1.034436E+05 -3.303872E+04 -1.202988E+04 2.946728E+04 -2.101560E+04 -2.199714E+02 + 1.345229E+04 -1.043628E+04 -1.355312E+03 6.845778E+03 -1.402360E+03 -4.004675E+03 -9.971291E+02 9.316837E+02 1.000626E+03 4.671134E+02 + 2.301602E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 54 + 1.835648E+02 2.539069E+02 -2.138758E-01 -2.525287E+02 -6.506912E+02 -1.332076E+02 5.791678E+02 5.574422E+02 -2.172296E+02 -9.092774E+02 + 2.739700E+02 8.474141E+02 -6.542195E+02 -2.222762E+02 7.147561E+02 -4.430387E+02 -3.182576E+01 4.672014E+02 -6.377236E+02 4.324386E+01 + 5.976511E+02 -2.957568E+02 -5.049864E+02 6.251659E+02 -9.416094E+01 -2.944704E+02 2.701076E+02 -1.549532E+02 -3.078258E+01 3.168226E+02 + -2.265253E+02 1.108177E+02 -2.654836E+02 -4.018514E+02 1.142268E+03 1.944522E+01 -1.732609E+03 2.007907E+03 -5.089709E+02 -2.063333E+03 + 3.388421E+03 -8.981680E+02 -4.678066E+03 8.038859E+03 -3.939794E+03 -8.734283E+03 2.030254E+04 -2.680324E+04 2.196280E+04 1.869305E+03 + -3.885823E+04 9.506310E+04 -1.632962E+05 2.068013E+05 -1.808098E+05 1.062913E+05 -3.225655E+04 -2.132572E+04 3.570280E+04 -1.758134E+04 + -8.783789E+03 1.928155E+04 -8.183744E+03 -8.315056E+03 8.987144E+03 5.162737E+03 -4.573322E+03 -6.208782E+03 -3.402720E+03 -1.197652E+03 + -5.405252E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 55 + -5.187432E+02 -1.588338E+02 1.376053E+02 3.407005E+02 7.708631E+02 8.208362E+01 -5.408707E+02 -4.168895E+02 3.218791E+01 3.766868E+02 + 1.462570E+02 -3.979956E+02 7.321921E+01 2.708149E+02 -2.808835E+02 -4.870195E+01 4.336901E+02 -7.603964E+02 4.663964E+02 4.608926E+02 + -6.435554E+02 -1.422080E+02 5.116120E+02 -6.159936E+01 -3.088632E+02 1.512512E+02 2.038219E+02 -2.543198E+02 -2.224495E+01 1.155531E+02 + -1.616850E+02 6.320573E+01 4.950350E+02 -1.707001E+02 -6.824527E+02 1.637036E+02 9.798199E+02 -1.360367E+03 -7.371198E+02 5.311697E+03 + -9.281928E+03 8.262125E+03 -9.045626E+02 -7.674448E+03 1.049533E+04 -4.392736E+03 -7.498843E+03 2.229378E+04 -3.072415E+04 2.874983E+04 + -1.050353E+04 -3.404316E+04 1.034436E+05 -1.808098E+05 2.100131E+05 -1.735842E+05 9.973691E+04 -1.869577E+04 -2.856620E+04 3.312122E+04 + -7.469846E+03 -1.743508E+04 1.680460E+04 3.069730E+03 -1.422077E+04 -2.886670E+03 9.484588E+03 9.426497E+03 4.534859E+03 1.443025E+03 + 5.884856E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 56 + 1.578406E+02 -2.081624E+01 1.044367E+02 -1.390388E+02 -1.811333E+02 1.545113E+01 -1.884378E+02 -9.079405E+01 5.251978E+02 2.873498E+02 + -6.156582E+02 -1.805982E+01 3.335936E+02 -2.324666E+02 -1.289935E+02 4.524224E+02 -5.150740E+02 3.526937E+02 1.095143E+02 -5.026626E+02 + 7.468124E+01 4.639968E+02 2.292664E+01 -7.501234E+02 4.952962E+02 3.414546E+02 -6.758306E+02 2.660386E+02 3.692722E+02 -5.318122E+02 + 1.348493E+02 1.963849E+02 -4.341144E+02 3.406716E+02 8.550646E+01 -3.688936E+02 2.097347E+02 2.792312E+02 1.165211E+03 -5.184246E+03 + 9.429334E+03 -1.009498E+04 4.846357E+03 3.593754E+03 -1.007280E+04 1.143527E+04 -4.955797E+03 -7.897331E+03 1.997135E+04 -3.091240E+04 + 3.076938E+04 -1.096002E+04 -3.303872E+04 1.062913E+05 -1.735842E+05 2.033795E+05 -1.728319E+05 9.420289E+04 -1.464644E+04 -3.275605E+04 + 2.925752E+04 8.819898E+02 -1.790339E+04 7.253332E+03 1.158889E+04 -3.062557E+03 -1.009939E+04 -7.104196E+03 -2.614048E+03 -6.108607E+02 + -1.709744E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 57 + 5.187007E+01 1.377419E+02 -1.335428E+01 -1.322444E+02 -6.131383E+02 6.440291E+01 7.279435E+02 4.855991E+02 -5.745812E+02 -7.550924E+02 + 4.162378E+02 5.756522E+02 -5.052344E+02 6.532598E+01 2.474657E+02 -2.791255E+02 4.625632E+01 2.677273E+02 -4.546178E+02 1.654759E+02 + 4.494995E+02 -3.857191E+02 -5.855478E+02 1.024688E+03 -1.108527E+02 -9.819092E+02 7.144956E+02 3.945515E+02 -9.287329E+02 4.586093E+02 + 4.259027E+02 -4.714504E+02 -2.080240E+02 7.425193E+01 5.674596E+02 2.351549E-01 -1.055583E+03 1.097715E+03 -1.268650E+03 2.336132E+03 + -4.212716E+03 5.848277E+03 -5.122304E+03 1.327280E+03 4.350721E+03 -1.003888E+04 1.044207E+04 -5.531825E+03 -2.375148E+03 1.496351E+04 + -2.438518E+04 2.589424E+04 -1.202988E+04 -3.225655E+04 9.973691E+04 -1.728319E+05 2.114274E+05 -1.807626E+05 9.422029E+04 -1.017790E+03 + -3.952091E+04 2.455967E+04 6.551002E+03 -1.685748E+04 -8.100908E+02 9.123617E+03 5.513364E+03 1.208520E+03 -2.016054E+02 -1.903926E+02 + -1.019353E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 58 + -4.797283E+00 -1.357054E+02 -2.589112E+02 1.822033E+02 6.973186E+02 8.447350E+01 -4.806623E+02 -4.203758E+02 -3.408731E+01 4.383002E+02 + 3.824184E+02 -6.122891E+02 -5.359784E+01 4.199814E+02 -1.008484E+02 -4.380493E+02 6.064994E+02 -3.944750E+02 -9.889580E+00 4.409340E+02 + -4.448030E+02 -1.139047E+02 4.818589E+02 -1.628674E+02 -3.323768E+02 4.298767E+02 -1.285148E+01 -3.842819E+02 3.766474E+02 3.973649E+01 + -5.397565E+02 1.285338E+02 7.672704E+02 -2.439437E+02 -1.013044E+03 4.468589E+02 1.173369E+03 -1.666430E+03 2.001161E+02 2.299069E+03 + -3.557080E+03 1.935064E+03 1.720788E+03 -4.316470E+03 3.052808E+03 2.448372E+03 -8.151875E+03 1.253773E+04 -1.252477E+04 6.132775E+03 + 4.551190E+03 -1.888429E+04 2.946728E+04 -2.132572E+04 -1.869577E+04 9.420289E+04 -1.807626E+05 2.356765E+05 -1.957657E+05 8.479945E+04 + 1.201399E+04 -4.528676E+04 1.813936E+04 1.681115E+04 -1.552602E+04 -1.058618E+04 5.270550E+03 8.361084E+03 4.400234E+03 1.435289E+03 + 5.824559E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 59 + 1.578641E+02 7.017451E+01 6.124970E+01 -1.634129E+02 -2.589181E+02 -9.411511E+01 2.404795E+01 -3.200165E+01 4.775443E+02 2.032319E+02 + -7.107952E+02 8.966096E+01 5.793866E+02 -7.509257E+02 5.328636E+01 8.492558E+02 -7.669501E+02 9.529512E+01 3.943020E+02 -5.245067E+02 + 1.541485E+02 1.518398E+02 2.375208E+02 -6.230641E+02 8.523176E+01 6.895607E+02 -5.004550E+02 -3.779554E+02 7.006434E+02 -3.938854E+02 + 3.480511E+01 4.565443E+02 -8.111860E+02 6.406552E+01 9.330368E+02 -5.306869E+02 -5.889965E+02 1.137918E+03 4.328263E+02 -4.048286E+03 + 7.068212E+03 -6.676556E+03 2.151443E+03 3.472931E+03 -6.332544E+03 4.442884E+03 1.501406E+03 -9.612541E+03 1.485658E+04 -1.638234E+04 + 1.128741E+04 2.674921E+03 -2.101560E+04 3.570280E+04 -2.856620E+04 -1.464644E+04 9.422029E+04 -1.957657E+05 2.474668E+05 -1.931807E+05 + 7.350497E+04 2.705114E+04 -4.301938E+04 1.704264E+03 2.441356E+04 1.693031E+03 -1.486633E+04 -1.281311E+04 -5.550979E+03 -1.640534E+03 + -6.699974E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 60 + -3.618314E+02 1.147905E+02 3.796565E+02 1.523960E+02 -6.551771E+02 -2.101754E+02 9.057554E+02 5.119939E+02 -1.214588E+03 -1.781747E+02 + 9.426883E+02 -4.352462E+02 -1.994578E+02 8.617918E+02 -9.491417E+02 1.301752E+02 3.852582E+02 -2.794277E+02 1.343696E+02 -2.893952E+01 + 6.504836E+01 -5.781680E+01 -3.077329E+02 5.486200E+02 -3.219652E+01 -6.433962E+02 4.489205E+02 2.304848E+02 -5.479531E+02 3.254567E+02 + 1.153993E+02 -3.721493E+02 2.232196E+02 2.220816E+01 -7.195608E+01 3.420590E+02 -6.323257E+02 2.683134E+02 -6.064292E+02 2.481758E+03 + -4.772630E+03 5.535602E+03 -3.330450E+03 -7.896628E+02 4.815209E+03 -7.131442E+03 5.241649E+03 -2.737792E+02 -4.975354E+03 1.098054E+04 + -1.355365E+04 1.017102E+04 -2.199714E+02 -1.758134E+04 3.312122E+04 -3.275605E+04 -1.017790E+03 8.479945E+04 -1.931807E+05 2.666044E+05 + -2.070599E+05 6.137582E+04 3.341704E+04 -3.452590E+04 -1.348870E+04 1.394858E+04 1.496874E+04 7.258860E+03 2.252938E+03 5.184324E+02 + 1.608038E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 61 + -4.617368E+02 -2.610235E+02 1.845409E+02 8.877884E+02 7.838502E+02 -6.705053E+02 -1.158694E+03 4.486350E+02 5.027108E+02 -3.366467E+02 + 4.321136E+01 2.432833E+02 -4.013998E+02 3.225042E+02 -4.916642E+01 -3.511892E+02 5.258554E+02 -2.746483E+02 -4.571290E+01 1.823389E+02 + -3.679584E+02 4.113800E+02 -1.583465E+02 -1.125142E+02 1.783004E+02 -6.925147E+01 1.104849E+01 -4.738739E+01 -3.204208E+01 2.631060E+02 + -2.170667E+02 -4.357494E+02 8.747291E+02 2.480557E+02 -1.553540E+03 4.722982E+02 1.442792E+03 -1.699984E+03 6.521522E+02 5.066252E+02 + -7.518850E+02 -2.660405E+02 1.575427E+03 -1.902700E+03 2.442509E+02 3.225157E+03 -5.747365E+03 6.880118E+03 -6.013344E+03 2.166659E+03 + 3.065028E+03 -9.515501E+03 1.345229E+04 -8.783789E+03 -7.469846E+03 2.925752E+04 -3.952091E+04 1.201399E+04 7.350497E+04 -2.070599E+05 + 2.874730E+05 -2.038760E+05 5.038312E+04 4.100336E+04 -2.533879E+04 -2.080572E+04 3.230348E+03 9.328571E+03 5.124695E+03 1.718886E+03 + 7.759141E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 62 + 4.602168E+02 -1.396634E+02 -4.398188E+02 -3.142424E+02 6.051266E+02 4.266724E+02 -9.443174E+02 -5.636608E+02 1.524900E+03 3.098322E+02 + -1.813607E+03 8.249576E+02 6.453893E+02 -1.354958E+03 1.010357E+03 1.781551E+02 -8.796696E+02 9.328236E+02 -3.728794E+02 -7.280188E+02 + 8.944190E+02 1.139570E+02 -4.502767E+02 -3.585083E+02 5.507287E+02 1.717466E+02 -4.772436E+02 3.066251E+01 4.467847E+02 -6.138214E+02 + 4.900939E+02 2.783965E+02 -1.045586E+03 2.807696E+02 1.081480E+03 -8.015659E+02 -5.594334E+02 1.363849E+03 6.796972E+01 -3.402017E+03 + 6.121156E+03 -5.940271E+03 2.073324E+03 2.744126E+03 -5.088490E+03 3.725087E+03 4.788040E+02 -5.912983E+03 9.430272E+03 -1.090202E+04 + 8.244079E+03 -2.357010E+02 -1.043628E+04 1.928155E+04 -1.743508E+04 8.819898E+02 2.455967E+04 -4.528676E+04 2.705114E+04 6.137582E+04 + -2.038760E+05 3.020604E+05 -2.063414E+05 2.465243E+04 5.345495E+04 -9.939424E+02 -2.672520E+04 -2.050158E+04 -8.531015E+03 -2.488007E+03 + -1.003623E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 63 + -1.204998E+02 2.466428E+02 2.443276E+02 -6.939821E+02 -6.178579E+02 7.187396E+02 1.419388E+03 -6.776783E+02 -1.653359E+03 8.913410E+02 + 8.993760E+02 -8.660368E+02 1.668547E+02 1.581258E+02 -1.577878E+02 5.336528E+01 -5.538371E+02 8.477364E+02 7.135108E+01 -9.163272E+02 + 8.248985E+02 -3.379433E+02 -1.185570E+02 3.275250E+02 6.707586E+01 -5.757958E+02 1.969072E+02 6.536206E+02 -5.779678E+02 -3.417970E+02 + 6.140743E+02 -7.998869E+01 -3.582562E+02 -1.041977E+02 1.012743E+03 -2.872891E+02 -1.533998E+03 1.804469E+03 -1.407763E+03 2.246576E+03 + -4.006619E+03 4.801760E+03 -2.783624E+03 -1.006349E+03 4.280594E+03 -5.958761E+03 4.466509E+03 -8.467853E+02 -2.763314E+03 6.743132E+03 + -8.536627E+03 6.695280E+03 -1.355312E+03 -8.183744E+03 1.680460E+04 -1.790339E+04 6.551002E+03 1.813936E+04 -4.301938E+04 3.341704E+04 + 5.038312E+04 -2.063414E+05 3.309540E+05 -2.023922E+05 -8.276678E+03 4.120116E+04 1.948753E+04 2.141664E+03 -1.407413E+03 -9.226015E+02 + -6.148708E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 64 + -1.080640E+03 -3.857980E+01 1.069717E+03 9.043022E+02 -1.445489E+02 -1.270789E+03 2.758576E+02 1.122306E+03 -1.151685E+03 -5.416091E+02 + 1.523101E+03 -6.755738E+02 -1.089473E+02 5.849044E+02 -9.873124E+02 5.285522E+02 3.002062E+02 -1.030824E+03 9.357845E+02 2.411676E+02 + -8.772888E+02 1.401668E+02 5.850112E+02 -1.807132E+02 -2.246257E+02 -3.827828E+01 1.167021E+02 4.029095E+02 -8.237354E+02 4.812474E+02 + 8.074636E+01 -6.646888E+02 1.178045E+03 -1.813978E+02 -1.408439E+03 9.094235E+02 6.463948E+02 -1.342426E+03 8.526876E+02 4.943974E+02 + -1.753807E+03 1.715332E+03 -3.325277E+02 -9.888410E+02 9.402796E+02 9.341782E+02 -2.791543E+03 3.869518E+03 -4.048170E+03 3.327005E+03 + -1.314270E+03 -2.698750E+03 6.845778E+03 -8.315056E+03 3.069730E+03 7.253332E+03 -1.685748E+04 1.681115E+04 1.704264E+03 -3.452590E+04 + 4.100336E+04 2.465243E+04 -2.023922E+05 3.867089E+05 -2.003183E+05 -4.642028E+04 2.091898E+04 2.254171E+04 9.210322E+03 2.372304E+03 + 9.868466E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 65 + 7.087309E+02 -6.819067E+02 -8.155964E+02 2.231723E+02 1.099618E+03 -2.449422E+02 -1.139488E+03 4.981636E+02 1.331460E+03 -8.121072E+02 + -6.187602E+02 2.632861E+02 3.182195E+02 1.432765E+02 -2.248378E+02 -1.542295E+02 4.721767E+02 -6.680245E+02 1.679002E+02 3.370530E+02 + -4.748742E+02 6.133615E+02 -3.330973E+02 -5.684761E+02 6.029855E+02 5.085134E+02 -7.558642E+02 -6.221326E+01 1.888312E+02 3.979519E+02 + -2.292576E+02 -2.551363E+02 3.819118E+02 -1.776983E+02 -7.429043E+02 7.546327E+02 6.977416E+02 -1.708057E+03 2.272121E+03 -3.391502E+03 + 4.915986E+03 -5.107652E+03 2.197954E+03 2.332541E+03 -5.224381E+03 5.474704E+03 -2.806438E+03 -1.236984E+03 4.385616E+03 -7.006690E+03 + 7.391906E+03 -4.391894E+03 -1.402360E+03 8.987144E+03 -1.422077E+04 1.158889E+04 -8.100908E+02 -1.552602E+04 2.441356E+04 -1.348870E+04 + -2.533879E+04 5.345495E+04 -8.276678E+03 -2.003183E+05 4.400933E+05 -1.742224E+05 -7.396590E+04 -2.372578E+04 -7.490273E+03 -2.175421E+03 + -3.142655E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 66 + 2.939702E+03 -8.879407E+02 -2.960137E+03 -1.685345E+03 1.499556E+03 2.427293E+03 -7.796483E+02 -1.700074E+03 1.821766E+03 8.232148E+01 + -1.625020E+03 8.948504E+02 1.048050E+02 -5.230156E+02 1.220924E+03 -9.950596E+02 -3.049051E+02 1.061312E+03 -5.262697E+02 -4.984348E+02 + 5.334000E+02 4.456981E+02 -9.717697E+02 4.701060E+01 6.576328E+02 3.250162E+01 -5.836969E+02 -2.596301E+01 7.019592E+02 -2.472248E+02 + -4.707331E+02 5.185369E+02 -3.429584E+02 -2.803456E+02 5.659386E+02 -5.953639E+01 -2.624105E+02 1.770610E+02 3.125804E+01 -4.404096E+02 + 1.171505E+03 -1.289362E+03 2.056208E+02 9.915276E+02 -9.724981E+02 -6.148172E+02 2.014157E+03 -2.350973E+03 2.305697E+03 -1.901503E+03 + 8.324428E+02 1.415827E+03 -4.004675E+03 5.162737E+03 -2.886670E+03 -3.062557E+03 9.123617E+03 -1.058618E+04 1.693031E+03 1.394858E+04 + -2.080572E+04 -9.939424E+02 4.120116E+04 -4.642028E+04 -1.742224E+05 4.957278E+05 -1.472119E+05 -7.529543E+04 -2.905471E+04 -8.473296E+03 + -2.650027E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 67 + 3.319769E+03 -4.847204E+02 -2.990083E+03 -2.322916E+03 7.932439E+02 2.857950E+03 -4.045898E+01 -2.119844E+03 1.145458E+03 3.414098E+02 + -1.322321E+03 1.216842E+03 -1.398005E+02 -1.194115E+03 1.621300E+03 -5.293674E+02 -8.939550E+02 1.296658E+03 -2.401627E+02 -6.444252E+02 + 4.025416E+02 5.244984E+01 -3.051648E+02 5.150383E+02 -2.761072E+02 -3.136661E+02 3.329155E+02 -7.764826E+01 3.094476E+02 -3.168277E+02 + -5.011196E+02 5.655400E+02 -8.003263E+00 -1.177479E+02 3.838888E+02 -4.879574E+02 -1.292683E+02 1.042791E+03 -2.242249E+03 3.378589E+03 + -4.031668E+03 3.703351E+03 -1.592135E+03 -1.592604E+03 4.093919E+03 -5.021172E+03 3.182492E+03 2.626871E+02 -2.747664E+03 4.872626E+03 + -5.646622E+03 4.459878E+03 -9.971291E+02 -4.573322E+03 9.484588E+03 -1.009939E+04 5.513364E+03 5.270550E+03 -1.486633E+04 1.496874E+04 + 3.230348E+03 -2.672520E+04 1.948753E+04 2.091898E+04 -7.396590E+04 -1.472119E+05 5.698876E+05 -7.569197E+04 -3.042479E+04 -8.961856E+03 + -3.031127E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 68 + 2.300556E+03 -8.270830E+01 -1.830480E+03 -1.714532E+03 8.561668E+01 1.698515E+03 2.058629E+02 -1.276855E+03 5.268259E+02 2.639525E+02 + -6.314204E+02 8.562205E+02 -1.761964E+02 -9.375172E+02 1.063701E+03 -1.348758E+02 -7.013254E+02 7.532803E+02 -8.950692E+00 -3.849158E+02 + 8.513971E+01 -7.760893E+01 1.745900E+02 4.253987E+02 -6.023638E+02 -2.026794E+02 5.916004E+02 -2.293618E+02 -3.364283E+01 4.244658E+01 + -2.446622E+02 1.387992E+02 1.351393E+02 9.499478E+01 7.287906E+01 -3.972605E+02 1.146490E+02 7.302878E+02 -2.201773E+03 3.471574E+03 + -4.399049E+03 4.183909E+03 -1.383590E+03 -2.234177E+03 4.228439E+03 -4.013854E+03 1.584763E+03 1.459547E+03 -3.533044E+03 5.229934E+03 + -5.267478E+03 3.263917E+03 9.316837E+02 -6.208782E+03 9.426497E+03 -7.104196E+03 1.208520E+03 8.361084E+03 -1.281311E+04 7.258860E+03 + 9.328571E+03 -2.050158E+04 2.141664E+03 2.254171E+04 -2.372578E+04 -7.529543E+04 -7.569197E+04 6.560268E+05 -1.868854E+04 -5.539038E+03 + -1.926389E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 69 + 1.156645E+03 7.351870E+01 -8.090984E+02 -9.203634E+02 -2.067510E+02 6.574920E+02 2.695362E+02 -4.116613E+02 8.637178E+01 7.706790E+01 + -2.585498E+01 3.513750E+02 -2.953501E+02 -2.759095E+02 5.086637E+02 -2.041762E+02 -2.149750E+02 3.543349E+02 -1.427068E+02 -9.959844E+01 + 1.209497E+02 -1.310418E+02 8.661791E+01 2.489598E+02 -3.424891E+02 -9.587305E+01 3.736568E+02 -1.544233E+02 -1.264178E+02 1.280932E+02 + -1.829666E+00 -2.626050E+01 -9.452540E+00 4.711298E+01 8.889191E+01 -9.170197E+01 1.184760E+01 2.032218E+02 -1.011928E+03 1.797944E+03 + -2.435899E+03 2.333453E+03 -5.123261E+02 -1.419746E+03 2.086086E+03 -1.684322E+03 4.098878E+02 9.761500E+02 -1.946992E+03 2.772500E+03 + -2.541790E+03 1.281197E+03 1.000626E+03 -3.402720E+03 4.534859E+03 -2.614048E+03 -2.016054E+02 4.400234E+03 -5.550979E+03 2.252938E+03 + 5.124695E+03 -8.531015E+03 -1.407413E+03 9.210322E+03 -7.490273E+03 -2.905471E+04 -3.042479E+04 -1.868854E+04 6.939938E+05 -2.293019E+03 + -8.054502E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 70 + 5.226168E+02 9.029161E+01 -3.192425E+02 -4.651970E+02 -2.322491E+02 2.119234E+02 2.758013E+02 -1.501302E+01 -1.079091E+02 -4.148624E+01 + 1.693066E+02 7.416137E+01 -2.867639E+02 6.561980E+01 2.338403E+02 -2.412395E+02 -4.278936E+00 1.989733E+02 -1.561116E+02 -2.664689E+01 + 1.217372E+02 -6.715684E+01 -1.168987E+01 7.433609E+01 -1.228500E+02 3.020696E+00 1.728098E+02 -8.780686E+01 -1.095483E+02 6.591929E+01 + 5.440664E+01 -4.010964E+00 -2.823300E+01 -1.711055E+01 4.938449E+01 3.119438E+01 -2.625832E+01 -1.272421E+01 -2.848772E+02 6.308783E+02 + -8.933144E+02 8.171218E+02 -7.495600E+01 -5.396131E+02 6.118986E+02 -4.679525E+02 1.237661E+02 3.330461E+02 -6.823158E+02 9.648701E+02 + -8.507942E+02 3.529412E+02 4.671134E+02 -1.197652E+03 1.443025E+03 -6.108607E+02 -1.903926E+02 1.435289E+03 -1.640534E+03 5.184324E+02 + 1.718886E+03 -2.488007E+03 -9.226015E+02 2.372304E+03 -2.175421E+03 -8.473296E+03 -8.961856E+03 -5.539038E+03 -2.293019E+03 7.010148E+05 + -2.417876E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 71 + 3.101771E+02 7.570924E+01 -1.737529E+02 -2.978274E+02 -1.875107E+02 1.028806E+02 2.288636E+02 5.130991E+01 -1.207789E+02 -4.739513E+01 + 1.409848E+02 -3.189026E+00 -1.912595E+02 9.990696E+01 1.258634E+02 -1.656348E+02 6.686010E+00 1.186130E+02 -7.751671E+01 -2.173268E+01 + 6.002557E+01 -1.208098E+01 -5.716555E+00 -1.053061E+01 -6.233144E+01 5.556053E+01 9.073860E+01 -8.030786E+01 -7.244638E+01 3.379556E+01 + 3.392034E+01 2.582561E+01 4.084762E+00 -4.142519E+01 8.273771E+00 5.035124E+01 -1.876564E+01 -4.500869E+01 -8.917584E+01 2.582262E+02 + -3.730139E+02 3.180577E+02 3.611138E+00 -2.108483E+02 1.901878E+02 -1.728436E+02 1.141214E+02 8.775911E+01 -2.739527E+02 4.174375E+02 + -3.864268E+02 1.512268E+02 2.301602E+02 -5.405252E+02 5.884856E+02 -1.709744E+02 -1.019353E+02 5.824559E+02 -6.699974E+02 1.608038E+02 + 7.759141E+02 -1.003623E+03 -6.148708E+02 9.868466E+02 -3.142655E+02 -2.650027E+03 -3.031127E+03 -1.926389E+03 -8.054502E+02 -2.417876E+02 + 7.016128E+05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 72 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.485020E+02 -4.035220E+02 2.097896E+02 -5.924096E+01 1.068897E+01 -8.777408E-02 5.572225E-01 -5.783417E-01 -1.158360E-01 + 4.101757E-01 -9.600154E-03 1.423664E-01 -4.515992E-01 2.174251E-01 3.020442E-01 -2.514572E-03 -4.575704E-01 5.420844E-01 -2.081840E-01 + 8.191596E-01 -1.545628E+00 1.172697E+00 -5.126003E-01 -4.517256E-01 6.186414E-01 5.304535E-01 -1.785282E+00 2.198096E+00 -1.764287E+00 + 1.638166E-01 1.867552E+00 -4.507936E+00 4.803091E+00 -1.158498E+00 -3.719260E+00 4.696320E+00 -2.467816E+00 9.223340E-01 -2.311532E+00 + 5.575165E+00 4.018481E+00 -3.563396E+01 3.864798E+01 -4.319911E+01 1.953487E+02 -6.764146E+01 7.329343E+01 -1.089858E+02 -3.298428E+02 + -1.409130E+02 -1.700609E+02 5.262416E+01 2.153100E+03 -2.213227E+04 -1.554892E+04 9.573317E+02 1.189372E+03 -8.712167E+02 -7.660822E+02 + -9.765402E+03 -8.118300E+03 -2.163725E+03 1.828084E+03 2.777000E+04 -4.793893E+03 1.579441E+04 3.301750E+03 -1.912447E+03 2.147520E+03 + -5.469084E+09 + 73 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.035220E+02 8.365129E+02 -6.324041E+02 2.495938E+02 -6.411487E+01 1.260660E+01 -4.918376E+00 1.841742E+00 3.599989E-01 + -9.170825E-01 5.995586E-02 -1.950088E-01 5.594527E-01 -3.216750E-01 -3.842754E-01 -5.288502E-01 7.943638E-01 -2.490359E-01 -7.973705E-01 + 2.573994E-01 1.039592E+00 -1.186340E+00 5.382902E-01 9.841532E-01 -1.551539E+00 2.798382E-02 2.217989E+00 -2.508073E+00 2.330464E+00 + -1.048619E+00 -2.300375E+00 6.295168E+00 -6.951397E+00 2.692349E+00 5.041774E+00 -8.117617E+00 4.687721E+00 -3.137602E+00 9.800755E+00 + -1.326859E+01 -3.687444E+00 5.923276E+01 -7.573926E+01 6.189991E+01 -3.837509E+02 1.464725E+02 -1.441555E+02 9.275701E+01 4.570410E+02 + -4.448723E+01 9.522332E+00 4.054288E+02 -6.165877E+03 3.601651E+04 1.457948E+03 -4.750482E+03 1.171306E+03 -1.506251E+02 8.277848E+03 + -1.997214E+03 7.291421E+03 2.088390E+03 -1.752824E+03 -7.654128E+04 -2.371510E+04 -1.970351E+04 -8.816662E+03 1.097358E+03 -2.190839E+03 + -2.918500E+09 + 74 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.097896E+02 -6.324041E+02 7.487032E+02 -4.523981E+02 1.582168E+02 -3.882254E+01 9.149620E+00 -1.796521E+00 -5.712416E-01 + 6.974545E-01 -5.869247E-02 3.480091E-02 -1.554264E-01 1.312218E-01 2.555532E-01 5.045449E-01 -3.390978E-01 -5.501352E-01 1.125031E+00 + -1.120812E+00 6.405065E-01 5.563876E-02 6.842861E-03 -8.142127E-01 1.728100E+00 -1.902788E+00 4.800262E-01 -5.742418E-01 4.383514E-01 + 1.795746E+00 -1.884802E+00 4.886940E-01 8.303855E-01 -1.747073E+00 -2.497051E+00 5.956466E+00 -8.757518E+00 1.234930E+01 -1.687744E+01 + 1.038094E+01 5.296798E+00 -5.146505E+01 8.265881E+01 -7.694702E+01 3.307381E+02 -2.853115E+02 2.826089E+02 1.890004E+02 -4.684142E+02 + 2.886497E+02 6.388416E+02 -1.111026E+03 4.785743E+03 -2.195689E+04 2.424706E+04 4.061512E+03 -1.761763E+03 8.419772E+02 -4.721168E+03 + 1.507618E+04 -4.136021E+03 7.833287E+03 -2.169028E+01 7.474604E+04 1.441199E+04 1.021418E+04 8.506810E+03 3.219388E+02 -9.945472E+02 + 1.244447E+10 + 75 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -5.924096E+01 2.495938E+02 -4.523981E+02 4.356815E+02 -2.309974E+02 7.178540E+01 -1.725166E+01 3.027248E+00 6.071966E-01 + -6.288144E-01 3.628307E-01 -6.154006E-01 6.293677E-01 -4.142458E-01 -2.485789E-01 2.449138E-01 -1.844940E-01 1.457983E-01 1.097282E-01 + -2.593321E-01 3.574240E-01 -9.169274E-01 7.588158E-01 -3.892889E-01 -6.543964E-01 2.010386E+00 -1.782436E+00 2.181822E+00 -1.818654E+00 + -1.675927E+00 4.019750E+00 -3.903637E+00 1.880387E+00 1.269890E+00 -1.099383E+00 -1.852828E+00 7.670999E+00 -1.565168E+01 1.319549E+01 + -3.708923E+00 3.013549E+00 3.098878E+01 -6.311706E+01 8.133630E+01 -1.400424E+02 2.677458E+02 -3.713521E+02 -2.014000E+02 6.575748E+02 + 3.506702E+02 -5.128418E+02 1.663535E+03 -1.890353E+03 5.643919E+03 2.392908E+03 1.683080E+02 -1.710041E+03 -2.874214E+03 -7.754560E+02 + -9.499864E+03 1.402169E+04 -1.418209E+04 3.501055E+03 -3.283018E+04 -9.288895E+03 -1.520963E+04 2.270845E+03 1.675848E+03 2.866393E+03 + -1.013585E+10 + 76 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.068897E+01 -6.411487E+01 1.582168E+02 -2.309974E+02 1.992892E+02 -9.774726E+01 3.212287E+01 -8.836922E+00 1.220717E+00 + 1.604688E-01 -6.612794E-01 1.039116E+00 -1.190151E+00 7.260947E-01 -7.758688E-02 -2.820421E-01 1.818769E-01 1.889849E-01 -4.098608E-01 + 4.077353E-01 -6.304697E-01 1.062007E+00 -1.121179E+00 8.988528E-01 -3.226213E-02 -1.336886E+00 1.671125E+00 -2.596062E+00 1.454405E+00 + 9.613087E-01 -2.744957E+00 2.459656E+00 -1.522084E+00 8.751764E-01 2.691481E-01 9.736054E-01 -3.680623E+00 1.071183E+01 -7.446599E+00 + 4.153528E+00 -1.189375E+01 -1.119639E+00 5.920908E+00 -1.086794E+01 -3.916487E+01 -2.537140E+01 3.701359E+02 -1.573526E+02 -5.575862E+02 + -9.734717E+02 3.226986E+02 -1.327244E+03 1.762486E+03 2.707720E+03 -2.064878E+04 -9.426048E+02 -1.011692E+02 3.018876E+03 -5.346066E+03 + 2.553126E+04 -1.107336E+04 8.653400E+03 -3.803394E+03 7.783656E+03 1.847448E+04 1.170647E+04 -1.103997E+04 -3.283474E+03 -2.700588E+03 + 3.900138E+09 + 77 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -8.777408E-02 1.260660E+01 -3.882254E+01 7.178540E+01 -9.774726E+01 8.604414E+01 -4.593754E+01 1.680589E+01 -4.776971E+00 + 1.138606E+00 1.496881E-01 -7.052556E-01 9.223904E-01 -5.047070E-01 8.485133E-02 1.827566E-01 -2.397742E-01 6.977749E-02 -1.941280E-02 + 1.757058E-01 -2.004188E-01 -1.421789E-01 6.157793E-01 -7.082812E-01 1.286093E-01 8.820151E-01 -1.111590E+00 1.802941E+00 -1.360326E+00 + 1.619011E-01 1.312938E+00 -1.118730E+00 1.022239E+00 -2.403169E+00 1.874854E+00 -8.257231E-01 2.597406E+00 -6.799704E+00 6.426980E+00 + -5.522074E+00 6.052578E+00 -1.021334E+01 2.970395E+01 -3.909984E+01 8.639504E+01 -7.130093E+01 -3.047936E+02 2.169287E+02 2.994823E+02 + 8.898436E+02 -2.000227E+01 5.155148E+02 -1.524157E+03 -1.158463E+03 7.802534E+03 2.016701E+02 2.485618E+03 -9.186808E+02 5.751716E+03 + -3.299666E+04 5.554294E+03 -4.084201E+03 8.727254E+02 9.985343E+02 -1.906055E+04 -7.308238E+03 6.530834E+03 2.113492E+03 1.529808E+03 + 1.783393E+09 + 78 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.572225E-01 -4.918376E+00 9.149620E+00 -1.725166E+01 3.212287E+01 -4.593754E+01 4.506496E+01 -2.532758E+01 8.947754E+00 + -2.797016E+00 6.656434E-01 5.318957E-02 -4.203242E-01 1.155484E-01 2.904046E-01 -4.912693E-01 5.758078E-01 -3.961907E-01 2.995188E-01 + -4.283488E-01 5.634041E-01 -2.715284E-01 -1.800528E-01 3.149741E-01 7.286990E-02 -6.291352E-01 5.509476E-01 -5.243663E-01 6.488431E-01 + -4.003710E-01 -6.330326E-01 2.282857E-01 4.421989E-01 7.281966E-01 -1.179384E+00 -2.584628E-01 -8.280810E-01 3.372922E+00 -5.125171E+00 + 4.553941E+00 -5.009252E+00 1.066847E+01 -2.538895E+01 2.930020E+01 -6.860431E+01 5.746992E+01 1.674917E+02 5.894084E+01 -1.230664E+02 + -2.891840E+02 6.989965E+01 8.954947E+00 9.052660E+02 1.042045E+03 4.474175E+02 -7.553202E+02 -5.161752E+02 6.337633E+02 -5.943940E+03 + 1.920848E+04 -3.282980E+03 9.941911E+02 -1.151060E+03 1.973765E+03 2.182537E+04 2.611124E+02 -6.385652E+02 -1.045372E+03 -8.122112E+02 + -7.861651E+08 + 79 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -5.783417E-01 1.841742E+00 -1.796521E+00 3.027248E+00 -8.836922E+00 1.680589E+01 -2.532758E+01 2.644063E+01 -1.549721E+01 + 5.824855E+00 -1.955833E+00 5.558824E-01 -8.881992E-02 1.964080E-01 -4.449929E-01 5.318107E-01 -4.744700E-01 2.852237E-01 -2.080644E-01 + 3.244813E-01 -3.684306E-01 1.974391E-01 9.785863E-02 -5.586838E-02 -1.595353E-01 2.301604E-01 2.984284E-02 -1.884712E-01 1.272548E-01 + -1.556946E-01 6.680041E-01 -1.862802E-01 -3.754633E-01 2.587292E-01 1.935226E-01 2.631526E-01 3.589294E-01 -1.639650E+00 1.031423E+00 + -2.604313E+00 5.969934E+00 -1.110118E+01 1.454988E+01 -8.615069E+00 3.246452E+01 -1.906816E+01 1.018031E+01 -1.122690E+02 5.400637E+01 + 5.236282E+01 -2.566296E+02 -2.370842E+02 -3.699089E+02 1.454058E+03 2.443024E+03 9.875654E+02 -1.485751E+03 8.833084E+01 4.629764E+03 + -1.143155E+04 1.351186E+03 9.682145E+02 5.902367E+02 -7.016591E+03 -8.432482E+03 -1.044226E+03 8.138512E+02 2.845357E+02 7.242430E+02 + 3.121085E+09 + 80 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.158360E-01 3.599989E-01 -5.712416E-01 6.071966E-01 1.220717E+00 -4.776971E+00 8.947754E+00 -1.549721E+01 1.836086E+01 + -1.157192E+01 4.644522E+00 -1.500565E+00 4.843100E-01 -2.950078E-01 3.193684E-01 -3.033090E-01 2.331403E-01 -1.159477E-01 2.726479E-02 + -1.082864E-01 1.872695E-01 -7.893810E-02 -1.619861E-01 5.481299E-02 5.709136E-02 1.987738E-01 -4.448231E-01 4.938553E-01 -3.690545E-01 + 7.048331E-01 -9.323048E-01 4.132769E-01 -1.481284E-01 -1.925861E-01 5.264539E-01 -6.334128E-01 4.060393E-02 9.226098E-01 1.276525E+00 + 4.191558E-01 -3.237202E+00 6.821518E+00 -6.727756E+00 8.394308E+00 -1.079018E+01 -6.726499E+00 -3.158621E+01 -1.397158E+01 -5.448398E+01 + 7.812653E+01 -1.779053E+01 2.702827E+02 2.150299E+01 -3.041540E+03 -2.500255E+03 1.558418E+03 1.063198E+03 3.178253E+02 -2.411156E+03 + 2.296543E+04 -3.908165E+03 -6.033816E+02 -7.662216E+02 1.479406E+04 -6.831816E+02 5.049613E+03 3.668407E+02 4.766369E+02 -3.437821E+01 + -2.748881E+09 + 81 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.101757E-01 -9.170825E-01 6.974545E-01 -6.288144E-01 1.604688E-01 1.138606E+00 -2.797016E+00 5.824855E+00 -1.157192E+01 + 1.541044E+01 -1.063937E+01 4.371512E+00 -1.593421E+00 6.313513E-01 -3.775693E-01 1.801871E-01 -4.454219E-02 -5.975386E-02 7.327010E-02 + 2.987216E-02 -4.620026E-02 6.958676E-02 2.015927E-02 2.175106E-02 2.884687E-02 -5.227568E-01 7.916753E-01 -5.153076E-01 1.604750E-01 + -6.657491E-01 9.439116E-01 -5.783618E-01 9.733657E-01 -8.322120E-01 2.243760E-01 -5.025014E-04 3.222352E-01 -1.203085E+00 3.476584E-01 + -9.280356E-01 6.564209E-01 -8.433008E-01 -3.277129E-01 1.962935E+00 -5.588408E+00 -7.089022E+00 -1.517394E+01 -4.873032E+00 6.905270E+01 + -1.692235E+02 4.817682E+02 -3.550362E+02 -3.265362E-01 1.485182E+03 -1.070639E+03 -1.607101E+03 -5.035818E+02 -1.336637E+03 1.448359E+02 + -1.799140E+04 1.219429E+03 7.401241E+02 1.917641E+02 -5.254685E+03 -2.548910E+03 2.337674E+02 -7.503683E+01 -4.339882E+02 -1.534597E+02 + 2.715342E+09 + 82 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -9.600154E-03 5.995586E-02 -5.869247E-02 3.628307E-01 -6.612794E-01 1.496881E-01 6.656434E-01 -1.955833E+00 4.644522E+00 + -1.063937E+01 1.486764E+01 -1.045609E+01 4.551149E+00 -1.721813E+00 6.180972E-01 -2.354314E-01 -1.667814E-02 4.024207E-02 1.193096E-01 + -2.089915E-01 1.216535E-01 -7.337981E-02 9.003379E-02 -1.617064E-01 9.118807E-02 4.401830E-01 -7.168957E-01 4.590152E-01 -1.447717E-01 + 3.677664E-01 -3.889637E-01 7.357189E-02 -6.536456E-01 7.693502E-01 -8.624462E-01 1.794288E+00 -1.388939E+00 1.597240E+00 -2.223074E+00 + 3.062446E+00 -5.828227E-01 1.588943E+00 -3.173461E+00 -3.670704E+00 9.497304E+00 -2.313535E+01 1.252404E+02 -1.013865E+02 -2.565810E+01 + 4.001833E+00 -4.556536E+02 3.863975E+02 -1.784622E+02 3.639374E+03 -3.065140E+03 3.378122E+01 7.242895E+02 1.074386E+03 1.649741E+03 + 4.934956E+03 1.265472E+03 -7.042855E+02 1.958785E+02 3.369476E+03 7.101137E+03 -3.135384E+03 -1.608282E+03 6.358583E+02 -4.147986E+01 + -2.439106E+09 + 83 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.423664E-01 -1.950088E-01 3.480091E-02 -6.154006E-01 1.039116E+00 -7.052556E-01 5.318957E-02 5.558824E-01 -1.500565E+00 + 4.371512E+00 -1.045609E+01 1.423914E+01 -9.871544E+00 4.235270E+00 -1.467005E+00 3.623741E-01 4.346988E-02 -6.949757E-02 -1.065786E-01 + 2.550695E-01 -4.963234E-02 -1.024320E-01 -1.611308E-01 3.096493E-01 -1.472762E-01 -8.228939E-02 1.654535E-01 -1.115709E-01 1.677227E-01 + -3.503304E-01 2.746776E-01 -4.607803E-02 3.845932E-01 2.053897E-02 1.140738E-02 -7.716628E-01 4.597168E-01 -1.105878E+00 2.290890E+00 + -9.386156E-01 -2.840390E+00 -2.263538E+00 7.019890E+00 -1.465355E+00 -5.817779E+00 3.093390E+01 -1.641632E+02 1.824880E+02 -5.515830E+00 + 9.639202E+01 2.347120E+02 -3.069883E+02 4.741874E+02 -4.525616E+03 6.490474E+03 -1.426164E+03 -9.430108E+02 -9.323448E+02 -3.628529E+03 + 7.779569E+03 -4.237826E+03 5.097127E+02 -6.903124E+02 -7.042715E+03 -2.603992E+03 -2.116211E+03 3.582901E+03 -1.304182E+03 1.295177E+02 + 2.653981E+09 + 84 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.515992E-01 5.594527E-01 -1.554264E-01 6.293677E-01 -1.190151E+00 9.223904E-01 -4.203242E-01 -8.881992E-02 4.843100E-01 + -1.593421E+00 4.551149E+00 -9.871544E+00 1.289758E+01 -8.693170E+00 3.303906E+00 -7.564999E-01 -7.057904E-02 8.345928E-02 1.152780E-01 + -2.638697E-01 -2.776266E-02 1.929336E-01 1.268548E-01 -2.791121E-01 1.432348E-01 -9.870070E-02 9.938230E-02 -1.465782E-01 -2.373044E-01 + 4.249370E-01 -2.278015E-01 2.207358E-01 -3.518328E-01 9.219198E-02 -4.397856E-01 1.152023E+00 -1.101567E+00 2.714438E+00 -3.698995E+00 + 2.755295E+00 1.898602E+00 -1.657075E+00 -2.021804E+00 -2.779292E+00 5.837521E+00 -5.381365E+01 1.347887E+02 -1.531901E+02 -5.959087E+01 + -1.328956E+02 -9.962346E+01 1.247366E+02 -3.141494E+02 3.274040E+03 2.827006E+02 2.444150E+03 -7.385657E+02 1.751536E+03 2.122956E+03 + -1.262971E+03 3.626240E+03 1.015890E+03 9.861610E+02 4.856648E+03 -8.254664E+03 4.795798E+03 3.416226E+03 4.934524E+02 -1.917802E+02 + -9.326944E+08 + 85 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.174251E-01 -3.216750E-01 1.312218E-01 -4.142458E-01 7.260947E-01 -5.047070E-01 1.155484E-01 1.964080E-01 -2.950078E-01 + 6.313513E-01 -1.721813E+00 4.235270E+00 -8.693170E+00 1.099936E+01 -7.057080E+00 2.531777E+00 -5.348558E-01 1.070628E-02 -1.367430E-01 + 2.808642E-01 -1.021643E-01 -7.023139E-02 -6.566592E-02 2.109968E-01 -1.694201E-01 1.081764E-01 -1.265478E-01 -3.319476E-02 4.183316E-01 + -4.314072E-01 1.392278E-01 -2.801454E-02 1.454605E-01 -3.023873E-01 8.602165E-01 -1.639333E+00 1.303301E+00 -2.077844E+00 2.687906E+00 + -4.576388E+00 1.891296E+00 5.111496E-01 2.377520E+00 6.346914E+00 -7.437546E+00 5.214671E+01 -1.103387E+02 1.278889E+02 5.437423E+01 + 1.810666E+02 9.012563E+01 -2.961526E+02 3.002374E+02 -2.409488E+03 -1.118800E+03 -1.108999E+03 7.992313E+02 -1.267542E+03 -5.041942E+02 + 6.318892E+03 -2.982545E+03 -7.540519E+02 -1.772150E+03 -1.082573E+03 5.512860E+03 -4.870750E+02 -2.828498E+03 -3.941718E+02 2.674596E+02 + 3.155416E+08 + 86 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 3.020442E-01 -3.842754E-01 2.555532E-01 -2.485789E-01 -7.758688E-02 8.485133E-02 2.904046E-01 -4.449929E-01 3.193684E-01 + -3.775693E-01 6.180972E-01 -1.467005E+00 3.303906E+00 -7.057080E+00 9.700132E+00 -6.744083E+00 2.495816E+00 -4.389427E-01 1.456985E-02 + -2.323386E-01 2.671848E-01 -9.600672E-02 6.092444E-02 -8.453038E-02 5.277859E-03 1.032862E-01 -3.411706E-02 1.040688E-01 -3.212453E-01 + 1.839313E-01 -1.692312E-01 5.281664E-04 1.826165E-03 1.320031E-01 -6.952528E-01 1.217074E+00 -1.289494E+00 6.190525E-01 -4.220285E-01 + 2.862090E+00 -1.093757E+00 -3.999510E+00 5.807392E-01 -1.344671E+01 7.633512E+00 -2.617400E+01 8.693785E+01 -1.153673E+02 2.894191E+00 + -1.599173E+02 -1.711757E+02 -4.059654E+00 3.203225E+02 8.245068E+02 2.293698E+03 -3.263376E+02 -4.538153E+00 6.740405E+02 2.211307E+02 + -5.811400E+03 2.436355E+03 -1.615692E+03 1.180033E+03 8.900351E+03 4.634022E+03 2.172032E+03 -1.883360E+03 9.249022E+02 -9.692214E+01 + -1.168488E+09 + 87 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.514572E-03 -5.288502E-01 5.045449E-01 2.449138E-01 -2.820421E-01 1.827566E-01 -4.912693E-01 5.318107E-01 -3.033090E-01 + 1.801871E-01 -2.354314E-01 3.623741E-01 -7.564999E-01 2.531777E+00 -6.744083E+00 1.012906E+01 -7.574792E+00 3.095855E+00 -7.790173E-01 + 3.777049E-01 -4.998234E-01 3.085068E-01 -1.324672E-01 3.871471E-02 6.862271E-02 -1.676556E-01 3.433409E-02 7.337890E-02 -1.349053E-03 + 2.223877E-01 1.053215E-01 3.068270E-02 -3.396732E-01 -1.408410E-02 7.000020E-01 -1.672530E+00 2.476130E+00 -6.149510E-01 -5.730012E-01 + 9.268895E-02 -1.542570E+00 5.348164E+00 -5.697560E-01 1.273844E+01 -1.744909E+01 2.626948E+01 -9.551065E+01 9.951058E+01 -2.450507E+01 + -2.793284E+01 2.061917E+02 4.745066E+02 -5.658806E+02 -1.789264E+02 -5.825125E+03 1.447025E+03 9.552731E+01 -5.652172E+02 7.049458E+02 + -4.919414E+03 -2.341486E+03 2.073698E+03 -2.896082E+02 -2.590631E+04 1.712459E+03 -3.173878E+03 4.054008E+03 -8.474201E+02 -2.688101E+02 + 2.537083E+08 + 88 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.575704E-01 7.943638E-01 -3.390978E-01 -1.844940E-01 1.818769E-01 -2.397742E-01 5.758078E-01 -4.744700E-01 2.331403E-01 + -4.454219E-02 -1.667814E-02 4.346988E-02 -7.057904E-02 -5.348558E-01 2.495816E+00 -7.574792E+00 1.226372E+01 -9.886176E+00 4.556592E+00 + -1.645505E+00 8.434960E-01 -4.861187E-01 5.255532E-03 1.498656E-01 -3.494786E-02 3.110070E-02 -2.550275E-02 -8.061662E-02 1.363537E-01 + -2.551420E-01 -1.271933E-01 1.570555E-01 5.410835E-01 -3.562861E-01 -1.686149E-01 1.067093E+00 -1.798788E+00 1.885583E-01 4.493443E-01 + -2.564839E+00 5.043071E+00 -5.092688E+00 3.569731E+00 -7.134752E+00 2.617526E+01 -1.413346E+01 4.234223E+01 -6.451691E+01 3.586859E+01 + 5.701558E+01 -3.206052E+02 3.140635E+01 1.621750E+02 -7.428247E+02 8.198053E+03 -3.604408E+02 2.318178E+02 3.989380E+02 7.825253E+01 + -2.248188E+03 3.933292E+03 -5.794170E+02 2.946342E+02 3.106568E+04 -4.775075E+03 4.447777E+03 -1.484144E+03 1.930642E+02 4.127839E+02 + 1.043230E+08 + 89 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.420844E-01 -2.490359E-01 -5.501352E-01 1.457983E-01 1.889849E-01 6.977749E-02 -3.961907E-01 2.852237E-01 -1.159477E-01 + -5.975386E-02 4.024207E-02 -6.949757E-02 8.345928E-02 1.070628E-02 -4.389427E-01 3.095855E+00 -9.886176E+00 1.558884E+01 -1.277284E+01 + 6.509908E+00 -2.693376E+00 1.123904E+00 -2.370930E-01 -2.198579E-01 1.950697E-01 -4.646332E-02 -1.217474E-01 3.503294E-01 -3.300769E-01 + 1.585144E-01 2.436029E-01 -4.036602E-01 -5.912670E-01 1.026649E+00 -2.718480E-01 -6.425719E-01 -1.425838E-01 1.222873E+00 -5.712646E-01 + 2.746375E+00 -7.557992E+00 8.289946E+00 -8.012794E+00 -4.470779E-01 -2.710654E+01 -1.461739E+00 7.732726E+01 -3.633901E+01 -2.624641E+01 + -1.089824E+02 2.347660E+02 -6.213880E+02 2.376835E+02 2.849904E+03 -6.952866E+03 -1.307995E+02 -3.724142E+02 1.451592E+02 -4.229096E+02 + 5.034648E+03 -2.028590E+03 -1.681838E+03 5.310931E+02 -2.414651E+04 5.059404E+03 -2.449805E+03 -4.413870E+03 3.238957E+02 -4.315714E+02 + 5.991911E+07 + 90 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.081840E-01 -7.973705E-01 1.125031E+00 1.097282E-01 -4.098608E-01 -1.941280E-02 2.995188E-01 -2.080644E-01 2.726479E-02 + 7.327010E-02 1.193096E-01 -1.065786E-01 1.152780E-01 -1.367430E-01 1.456985E-02 -7.790173E-01 4.556592E+00 -1.277284E+01 1.961486E+01 + -1.647458E+01 8.612462E+00 -3.370586E+00 1.199269E+00 -4.370273E-01 -2.803802E-02 5.758310E-02 4.154725E-01 -8.506600E-01 7.853630E-01 + -4.702506E-01 -2.916326E-01 7.680402E-01 5.222242E-02 -3.438444E-01 -7.899078E-01 9.516846E-01 1.535148E+00 -3.011118E+00 1.494978E+00 + -1.913803E+00 7.360487E+00 -7.539733E+00 5.880013E+00 1.932199E+00 1.674474E+01 -5.405389E+00 -7.380394E+01 8.090222E+01 6.253500E+01 + 1.064025E+02 -3.096056E+02 7.120524E+02 -3.003320E+02 -2.073391E+03 -7.349808E+01 -9.847842E+02 1.002324E+03 -7.798490E+02 1.427833E+03 + -2.401698E+03 6.764857E+02 4.409707E+03 -2.413142E+03 6.254495E+03 -4.900962E+03 1.359436E+03 4.993888E+03 4.790111E+02 1.827916E+02 + -1.280396E+09 + 91 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 8.191596E-01 2.573994E-01 -1.120812E+00 -2.593321E-01 4.077353E-01 1.757058E-01 -4.283488E-01 3.244813E-01 -1.082864E-01 + 2.987216E-02 -2.089915E-01 2.550695E-01 -2.638697E-01 2.808642E-01 -2.323386E-01 3.777049E-01 -1.645505E+00 6.509908E+00 -1.647458E+01 + 2.405506E+01 -1.946699E+01 9.680982E+00 -3.747368E+00 1.570364E+00 -5.087657E-01 4.343999E-02 -6.516011E-01 1.280626E+00 -1.119336E+00 + 6.969812E-01 1.372537E-01 -7.812858E-01 3.541382E-01 -3.420474E-01 1.314401E+00 -1.366207E+00 1.433215E-01 1.934462E+00 -1.482235E+00 + 2.111953E+00 -4.428012E+00 -1.157668E+00 7.430380E+00 -6.856296E+00 -9.184831E+00 1.840319E+01 7.392864E+00 4.723687E-01 -3.292927E+01 + -2.360820E+02 2.970673E+02 -2.428327E+02 4.648691E+02 -2.830070E+02 4.703476E+03 1.145172E+03 -1.440127E+03 1.206010E+03 -1.965382E+03 + 4.559159E+03 -4.648933E+02 -4.734152E+03 2.684029E+03 5.403578E+03 8.595982E+03 -4.846896E+03 -7.227568E+02 -1.279508E+03 1.134212E+02 + 3.443897E+09 + 92 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.545628E+00 1.039592E+00 6.405065E-01 3.574240E-01 -6.304697E-01 -2.004188E-01 5.634041E-01 -3.684306E-01 1.872695E-01 + -4.620026E-02 1.216535E-01 -4.963234E-02 -2.776266E-02 -1.021643E-01 2.671848E-01 -4.998234E-01 8.434960E-01 -2.693376E+00 8.612462E+00 + -1.946699E+01 2.675606E+01 -2.064460E+01 9.838915E+00 -3.619406E+00 1.166637E+00 -5.018218E-01 7.076899E-01 -1.271069E+00 1.457246E+00 + -1.083820E+00 2.784242E-01 3.033884E-01 1.472068E-01 -5.913130E-02 -1.094548E+00 2.059206E+00 -2.472605E+00 -5.830958E-01 9.602150E-01 + -1.931250E+00 9.537632E-01 6.331727E+00 -8.686308E+00 4.836358E-01 6.511578E+00 -2.191883E+01 2.426206E+01 -5.555972E+01 -1.816458E+01 + 1.724435E+02 -1.414508E+00 -5.840310E+02 1.429169E+01 -2.123088E+03 -2.218691E+03 1.565922E+03 1.037331E+03 -5.789623E+02 5.218849E+02 + -6.085204E+03 -1.188162E+03 4.555856E+03 -3.059434E+03 -1.535447E+04 -1.888438E+03 6.004372E+03 -5.433455E+03 1.680996E+03 3.006346E+02 + -3.572911E+09 + 93 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.172697E+00 -1.186340E+00 5.563876E-02 -9.169274E-01 1.062007E+00 -1.421789E-01 -2.715284E-01 1.974391E-01 -7.893810E-02 + 6.958676E-02 -7.337981E-02 -1.024320E-01 1.929336E-01 -7.023139E-02 -9.600672E-02 3.085068E-01 -4.861187E-01 1.123904E+00 -3.370586E+00 + 9.680982E+00 -2.064460E+01 2.783131E+01 -2.108677E+01 9.992034E+00 -3.559205E+00 1.480874E+00 -8.499521E-01 8.717556E-01 -1.289428E+00 + 1.023333E+00 -2.326373E-01 -3.968606E-01 -5.782163E-02 -1.236784E-01 1.129422E+00 -2.620506E+00 2.038594E+00 2.312212E+00 -2.235546E+00 + -9.149744E-02 3.254731E+00 -1.064334E+01 1.214117E+01 -6.533785E+00 1.656464E+01 -1.421287E+01 -4.587457E+01 1.081883E+02 -6.105664E+01 + -2.163547E+02 -2.649966E+02 7.911494E+02 -2.676290E+02 4.256822E+03 4.548245E+03 -3.263311E+03 -1.008085E+03 -6.705359E+02 -2.961920E+02 + 1.775221E+04 2.866279E+02 -1.447660E+03 2.904881E+03 4.997177E+03 -1.056362E+03 -8.024311E+03 7.099859E+03 -1.551209E+03 -1.670741E+02 + 3.313321E+09 + 94 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -5.126003E-01 5.382902E-01 6.842861E-03 7.588158E-01 -1.121179E+00 6.157793E-01 -1.800528E-01 9.785863E-02 -1.619861E-01 + 2.015927E-02 9.003379E-02 -1.611308E-01 1.268548E-01 -6.566592E-02 6.092444E-02 -1.324672E-01 5.255532E-03 -2.370930E-01 1.199269E+00 + -3.747368E+00 9.838915E+00 -2.108677E+01 2.990741E+01 -2.433170E+01 1.192823E+01 -5.015794E+00 2.482522E+00 -1.390180E+00 1.001329E+00 + -7.499942E-01 3.844442E-01 7.061873E-01 -9.357264E-01 7.364364E-01 -1.032957E+00 2.441797E+00 -1.778560E+00 -1.914593E+00 2.848061E+00 + 4.553030E-01 -2.417804E+00 1.231518E+01 -2.050013E+01 9.405506E+00 -1.726072E+01 9.352343E+00 7.829644E+01 -2.011476E+02 1.320619E+02 + 6.785872E+01 4.129700E+02 -8.341918E+02 4.460969E+02 -5.388239E+03 -1.231520E+04 1.109170E+03 1.230962E+03 -1.050849E+03 -1.196687E+02 + -1.514813E+04 3.560120E+03 -8.645687E+02 -2.812700E+03 6.990040E+03 1.076053E+04 3.521477E+03 -7.314407E+02 5.292745E+02 1.734665E+02 + -2.949793E+09 + 95 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.517256E-01 9.841532E-01 -8.142127E-01 -3.892889E-01 8.988528E-01 -7.082812E-01 3.149741E-01 -5.586838E-02 5.481299E-02 + 2.175106E-02 -1.617064E-01 3.096493E-01 -2.791121E-01 2.109968E-01 -8.453038E-02 3.871471E-02 1.498656E-01 -2.198579E-01 -4.370273E-01 + 1.570364E+00 -3.619406E+00 9.992034E+00 -2.433170E+01 3.652094E+01 -2.919241E+01 1.603394E+01 -8.374256E+00 4.229827E+00 -2.387063E+00 + 1.027005E+00 -6.567895E-01 -6.846935E-01 1.711247E+00 -1.385424E+00 1.445444E+00 -2.992366E+00 3.161531E+00 -8.522329E-01 -2.775239E+00 + 2.923115E+00 -1.465394E-01 -8.750801E+00 2.063600E+01 -2.466034E+00 1.408757E+01 6.883397E+01 -2.871674E+02 2.175546E+02 5.501070E+01 + -5.425894E+01 -6.594325E+02 8.277689E+02 -1.427218E+01 3.614034E+03 1.266757E+04 3.035862E+03 4.468193E+02 2.025430E+03 1.666949E+03 + 3.083123E+03 -4.361760E+03 1.784182E+02 2.765747E+03 -6.205999E+03 -4.992703E+03 9.703152E+03 -6.820630E+03 2.378166E+02 -1.161565E+02 + 2.072976E+09 + 96 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 6.186414E-01 -1.551539E+00 1.728100E+00 -6.543964E-01 -3.226213E-02 1.286093E-01 7.286990E-02 -1.595353E-01 5.709136E-02 + 2.884687E-02 9.118807E-02 -1.472762E-01 1.432348E-01 -1.694201E-01 5.277859E-03 6.862271E-02 -3.494786E-02 1.950697E-01 -2.803802E-02 + -5.087657E-01 1.166637E+00 -3.559205E+00 1.192823E+01 -2.919241E+01 4.107870E+01 -3.565872E+01 2.139172E+01 -1.022778E+01 5.582770E+00 + -1.969830E+00 1.409782E-01 1.094589E+00 -1.441850E+00 5.555130E-01 -6.874117E-01 1.328345E+00 -2.080517E+00 1.467656E+00 1.794336E+00 + -4.894301E+00 1.863667E+00 2.180408E+00 -1.302109E+01 -5.539112E+00 -1.849151E+01 -6.071920E+01 2.784013E+02 -1.466222E+02 -2.535786E+02 + 1.026202E+01 3.414469E+02 -4.032834E+02 -4.732760E+02 -8.169415E+02 -4.203118E+03 -2.618320E+03 -1.770721E+03 -9.940092E+02 -8.473933E+02 + 1.739332E+03 5.437522E+03 1.112768E+03 3.915955E+02 1.230328E+04 -7.220233E+03 -4.432033E+03 1.635430E+03 1.186401E+03 1.670672E+02 + -1.385513E+09 + 97 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.304535E-01 2.798382E-02 -1.902788E+00 2.010386E+00 -1.336886E+00 8.820151E-01 -6.291352E-01 2.301604E-01 1.987738E-01 + -5.227568E-01 4.401830E-01 -8.228939E-02 -9.870070E-02 1.081764E-01 1.032862E-01 -1.676556E-01 3.110070E-02 -4.646332E-02 5.758310E-02 + 4.343999E-02 -5.018218E-01 1.480874E+00 -5.015794E+00 1.603394E+01 -3.565872E+01 5.528711E+01 -4.834409E+01 2.743816E+01 -1.456429E+01 + 5.870465E+00 -1.404998E+00 -6.857555E-01 -1.606970E-01 1.081767E+00 -6.889570E-01 6.738743E-01 1.716766E+00 -2.345131E+00 7.994833E-01 + 2.560793E+00 2.079430E+00 -3.522059E+00 1.493644E+01 6.992074E+00 1.646333E+01 2.033236E+01 -2.019498E+02 1.460177E+02 9.330565E+01 + 5.069322E+02 -1.608325E+02 -6.052722E+01 5.511715E+02 9.025624E+02 3.570947E+03 2.643967E+03 1.590408E+03 1.230490E+03 2.582988E+02 + 6.105832E+02 -5.975017E+03 -8.064337E+02 -2.461420E+03 -2.458765E+03 7.316489E+03 -4.152683E+03 6.368682E+03 1.360745E+01 1.103600E+02 + 2.249460E+09 + 98 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.785282E+00 2.217989E+00 4.800262E-01 -1.782436E+00 1.671125E+00 -1.111590E+00 5.509476E-01 2.984284E-02 -4.448231E-01 + 7.916753E-01 -7.168957E-01 1.654535E-01 9.938230E-02 -1.265478E-01 -3.411706E-02 3.433409E-02 -2.550275E-02 -1.217474E-01 4.154725E-01 + -6.516011E-01 7.076899E-01 -8.499521E-01 2.482522E+00 -8.374256E+00 2.139172E+01 -4.834409E+01 6.957187E+01 -5.932686E+01 3.510026E+01 + -1.586442E+01 6.519268E+00 -1.458455E+00 1.137008E+00 -1.546896E+00 1.415059E+00 -1.702589E+00 -1.415136E+00 3.264708E+00 -2.081029E+00 + 5.177449E+00 -1.996055E+01 2.802552E+01 -2.456644E+01 -3.299045E+01 2.635849E+01 -2.534598E+01 1.601011E+02 -1.612438E+02 2.165063E+02 + -1.092604E+03 4.401234E+02 2.185007E+02 -5.850158E+02 -4.118975E+03 -6.624439E+03 -1.106459E+03 -7.321441E+02 -2.768752E+03 -1.428660E+03 + -2.253268E+02 2.990348E+03 -5.768534E+02 2.695687E+03 -1.925434E+04 -7.143454E+03 -9.516290E+03 -2.681281E+03 -1.205308E+03 -1.488886E+02 + 2.090568E+08 + 99 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.198096E+00 -2.508073E+00 -5.742418E-01 2.181822E+00 -2.596062E+00 1.802941E+00 -5.243663E-01 -1.884712E-01 4.938553E-01 + -5.153076E-01 4.590152E-01 -1.115709E-01 -1.465782E-01 -3.319476E-02 1.040688E-01 7.337890E-02 -8.061662E-02 3.503294E-01 -8.506600E-01 + 1.280626E+00 -1.271069E+00 8.717556E-01 -1.390180E+00 4.229827E+00 -1.022778E+01 2.743816E+01 -5.932686E+01 8.688014E+01 -7.272264E+01 + 3.851813E+01 -1.694664E+01 5.290019E+00 -1.253410E+00 6.925241E-01 -1.707308E+00 9.502871E-01 1.754354E+00 -6.687268E+00 5.306989E+00 + -1.690751E+01 3.850597E+01 -4.951267E+01 3.270256E+01 3.127636E+01 -2.371511E+01 2.943716E+01 -2.365140E+02 6.803310E+01 -4.207344E+02 + 1.608920E+03 -9.570320E+02 -1.895687E+02 1.108796E+03 3.352673E+03 2.866477E+03 -1.349011E+03 3.721504E+02 3.708611E+03 1.937996E+03 + 3.466078E+03 2.055558E+03 9.848585E+02 -4.909992E+03 2.876266E+04 -8.189314E+03 1.836673E+04 -4.949958E+03 1.907345E+03 -1.357866E+02 + -3.539363E+09 + 100 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.764287E+00 2.330464E+00 4.383514E-01 -1.818654E+00 1.454405E+00 -1.360326E+00 6.488431E-01 1.272548E-01 -3.690545E-01 + 1.604750E-01 -1.447717E-01 1.677227E-01 -2.373044E-01 4.183316E-01 -3.212453E-01 -1.349053E-03 1.363537E-01 -3.300769E-01 7.853630E-01 + -1.119336E+00 1.457246E+00 -1.289428E+00 1.001329E+00 -2.387063E+00 5.582770E+00 -1.456429E+01 3.510026E+01 -7.272264E+01 1.019176E+02 + -8.329426E+01 4.493744E+01 -1.981592E+01 8.415755E+00 -3.982000E+00 2.279240E+00 -1.215863E+00 1.308698E+00 6.389663E+00 -1.158448E+01 + 2.415266E+01 -4.024642E+01 4.086455E+01 -2.563247E+01 -7.489086E+00 -1.925891E+01 -6.130068E+01 2.970164E+02 1.292000E+02 2.683898E+02 + -1.775734E+03 9.457326E+02 -3.066125E+02 -4.323714E+02 -4.320952E+03 -3.838543E+02 2.876978E+03 4.551602E+02 -1.779520E+03 1.481290E+03 + 6.171192E+03 -4.915462E+03 2.768813E+02 3.817544E+03 -3.309541E+04 1.896882E+04 -1.193401E+04 3.348668E+03 -2.577769E+03 -4.887326E+02 + 1.405264E+09 + 101 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.638166E-01 -1.048619E+00 1.795746E+00 -1.675927E+00 9.613087E-01 1.619011E-01 -4.003710E-01 -1.556946E-01 7.048331E-01 + -6.657491E-01 3.677664E-01 -3.503304E-01 4.249370E-01 -4.314072E-01 1.839313E-01 2.223877E-01 -2.551420E-01 1.585144E-01 -4.702506E-01 + 6.969812E-01 -1.083820E+00 1.023333E+00 -7.499942E-01 1.027005E+00 -1.969830E+00 5.870465E+00 -1.586442E+01 3.851813E+01 -8.329426E+01 + 1.222492E+02 -9.975932E+01 5.470612E+01 -2.756227E+01 1.250494E+01 -5.155412E+00 2.556790E+00 -3.422812E+00 -4.288516E+00 1.482978E+01 + -2.325572E+01 2.606437E+01 -2.832103E+01 4.334782E+01 -1.556322E+01 5.632102E+01 4.345093E+01 -2.034055E+02 -3.019795E+02 -1.054637E+02 + 1.638992E+03 -1.196667E+03 2.251030E+03 -2.090389E+03 1.150314E+04 5.457444E+02 -3.467974E+03 -1.169824E+03 -4.374881E+02 -5.721554E+03 + -3.561953E+04 6.395640E+03 -1.579568E+03 -1.887175E+03 2.381183E+04 -2.191112E+04 -6.139980E+03 7.993012E+03 1.129955E+02 2.121830E+02 + -5.363410E+09 + 102 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.867552E+00 -2.300375E+00 -1.884802E+00 4.019750E+00 -2.744957E+00 1.312938E+00 -6.330326E-01 6.680041E-01 -9.323048E-01 + 9.439116E-01 -3.889637E-01 2.746776E-01 -2.278015E-01 1.392278E-01 -1.692312E-01 1.053215E-01 -1.271933E-01 2.436029E-01 -2.916326E-01 + 1.372537E-01 2.784242E-01 -2.326373E-01 3.844442E-01 -6.567895E-01 1.409782E-01 -1.404998E+00 6.519268E+00 -1.694664E+01 4.493744E+01 + -9.975932E+01 1.428827E+02 -1.198911E+02 7.103046E+01 -3.791202E+01 1.799668E+01 -1.048994E+01 8.094254E+00 4.324256E-01 -9.895028E+00 + 1.641787E+01 -1.062042E+01 2.754349E+01 -6.538249E+01 4.014913E+01 -4.808485E+01 -4.976131E+01 2.355071E+02 2.863276E+02 2.744548E+01 + -1.069798E+03 8.100384E+02 -3.215869E+03 2.722876E+03 -1.291555E+04 -5.833399E+03 -2.479368E+02 2.651724E+03 1.067008E+01 8.238348E+03 + 3.737425E+04 -5.074259E+03 -6.533855E+02 3.828352E+03 -4.126025E+04 2.681434E+04 3.004720E+04 -1.469126E+04 1.349390E+03 -5.906897E+02 + 7.116247E+09 + 103 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.507936E+00 6.295168E+00 4.886940E-01 -3.903637E+00 2.459656E+00 -1.118730E+00 2.282857E-01 -1.862802E-01 4.132769E-01 + -5.783618E-01 7.357189E-02 -4.607803E-02 2.207358E-01 -2.801454E-02 5.281664E-04 3.068270E-02 1.570555E-01 -4.036602E-01 7.680402E-01 + -7.812858E-01 3.033884E-01 -3.968606E-01 7.061873E-01 -6.846935E-01 1.094589E+00 -6.857555E-01 -1.458455E+00 5.290019E+00 -1.981592E+01 + 5.470612E+01 -1.198911E+02 1.812134E+02 -1.599146E+02 9.747083E+01 -5.298602E+01 2.781727E+01 -2.152722E+01 1.327448E+01 -4.812379E+00 + -5.694023E+00 -1.065010E+00 -1.143342E+01 6.751392E+01 -5.169786E+01 2.601244E+01 -6.782267E+00 -2.127958E+02 -6.734779E+01 -2.773307E+02 + 4.255980E+02 -5.180202E+02 2.855563E+03 -1.631866E+03 6.078899E+03 8.627189E+03 3.645262E+03 -1.400082E+03 1.321920E+03 -3.678338E+02 + -2.793704E+04 -4.676678E+03 1.056376E+04 -5.667037E+03 4.211532E+04 -1.470481E+04 -1.731653E+04 8.210362E+03 -1.265646E+02 2.726742E+02 + -5.231428E+09 + 104 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.803091E+00 -6.951397E+00 8.303855E-01 1.880387E+00 -1.522084E+00 1.022239E+00 4.421989E-01 -3.754633E-01 -1.481284E-01 + 9.733657E-01 -6.536456E-01 3.845932E-01 -3.518328E-01 1.454605E-01 1.826165E-03 -3.396732E-01 5.410835E-01 -5.912670E-01 5.222242E-02 + 3.541382E-01 1.472068E-01 -5.782163E-02 -9.357264E-01 1.711247E+00 -1.441850E+00 -1.606970E-01 1.137008E+00 -1.253410E+00 8.415755E+00 + -2.756227E+01 7.103046E+01 -1.599146E+02 2.519759E+02 -2.283162E+02 1.436225E+02 -8.467255E+01 4.771350E+01 -2.692963E+01 1.398100E+01 + 3.054263E-01 -8.398712E-01 2.809690E+00 -6.808204E+01 7.447390E+01 -7.051468E+01 1.762711E+02 4.122570E+01 -2.506394E+02 3.394092E+02 + 2.806414E+02 2.539351E+02 -3.719587E+02 1.387631E+03 2.823480E+03 1.567154E+04 -8.779968E+03 2.164800E+03 -4.182182E+03 -5.241214E+03 + -2.067211E+03 1.321033E+04 -1.231068E+04 7.683234E+03 -4.088412E+03 -8.747014E+03 2.934569E+04 -1.338740E+03 6.874452E+02 1.468214E+03 + 2.156686E+08 + 105 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.158498E+00 2.692349E+00 -1.747073E+00 1.269890E+00 8.751764E-01 -2.403169E+00 7.281966E-01 2.587292E-01 -1.925861E-01 + -8.322120E-01 7.693502E-01 2.053897E-02 9.219198E-02 -3.023873E-01 1.320031E-01 -1.408410E-02 -3.562861E-01 1.026649E+00 -3.438444E-01 + -3.420474E-01 -5.913130E-02 -1.236784E-01 7.364364E-01 -1.385424E+00 5.555130E-01 1.081767E+00 -1.546896E+00 6.925241E-01 -3.982000E+00 + 1.250494E+01 -3.791202E+01 9.747083E+01 -2.283162E+02 3.793554E+02 -3.456044E+02 2.220799E+02 -1.246082E+02 5.662336E+01 -3.533554E+01 + 9.796792E+00 -5.328886E-01 -4.998802E+00 7.731131E+01 -1.027058E+02 1.045403E+02 -2.032111E+02 2.984734E+01 3.643820E+02 -5.789165E+01 + -3.330446E+02 5.203354E+02 -8.339989E+02 -1.197641E+03 6.615803E+03 -3.533747E+04 9.394272E+03 2.449474E+02 3.221233E+03 8.143493E+03 + 4.732252E+04 -1.791828E+04 8.032595E+03 4.118426E+02 -1.451816E+04 1.367494E+04 -4.098473E+04 -9.730895E+03 -2.536891E+03 -2.934116E+03 + 6.919028E+09 + 106 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -3.719260E+00 5.041774E+00 -2.497051E+00 -1.099383E+00 2.691481E-01 1.874854E+00 -1.179384E+00 1.935226E-01 5.264539E-01 + 2.243760E-01 -8.624462E-01 1.140738E-02 -4.397856E-01 8.602165E-01 -6.952528E-01 7.000020E-01 -1.686149E-01 -2.718480E-01 -7.899078E-01 + 1.314401E+00 -1.094548E+00 1.129422E+00 -1.032957E+00 1.445444E+00 -6.874117E-01 -6.889570E-01 1.415059E+00 -1.707308E+00 2.279240E+00 + -5.155412E+00 1.799668E+01 -5.298602E+01 1.436225E+02 -3.456044E+02 5.646402E+02 -5.232920E+02 3.302790E+02 -1.743169E+02 8.537354E+01 + -4.156625E+01 3.458605E+00 8.864274E+00 -8.934458E+01 1.266444E+02 -1.457737E+02 2.093994E+02 -5.941340E+02 -5.419619E+01 -5.250196E+02 + -3.718396E+02 -1.266329E+03 1.240115E+03 6.766637E+02 -2.309122E+03 6.160276E+04 -2.057204E+03 -8.326992E+03 -5.900245E+03 -1.158054E+04 + -8.889071E+04 1.928812E+04 4.093086E+03 -2.535151E+03 6.928838E+04 -1.908270E+04 1.896061E+04 3.296892E+04 -1.420844E+03 4.522866E+03 + -8.980145E+09 + 107 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.696320E+00 -8.117617E+00 5.956466E+00 -1.852828E+00 9.736054E-01 -8.257231E-01 -2.584628E-01 2.631526E-01 -6.334128E-01 + -5.025014E-04 1.794288E+00 -7.716628E-01 1.152023E+00 -1.639333E+00 1.217074E+00 -1.672530E+00 1.067093E+00 -6.425719E-01 9.516846E-01 + -1.366207E+00 2.059206E+00 -2.620506E+00 2.441797E+00 -2.992366E+00 1.328345E+00 6.738743E-01 -1.702589E+00 9.502871E-01 -1.215863E+00 + 2.556790E+00 -1.048994E+01 2.781727E+01 -8.467255E+01 2.220799E+02 -5.232920E+02 8.898394E+02 -8.090100E+02 5.002158E+02 -2.557268E+02 + 1.031425E+02 -5.517811E+00 -5.967190E+01 1.293938E+02 -1.353316E+02 2.167522E+02 -4.891306E+02 6.250182E+02 4.042292E+02 4.550738E+02 + 4.201697E+02 -8.566128E+00 1.118408E+03 6.235630E+01 -8.286886E+03 -4.254284E+04 -4.039804E+02 6.075077E+03 1.037318E+04 8.031514E+03 + 1.442948E+04 9.684384E+03 -2.583097E+04 6.370360E+03 -9.413046E+04 2.733461E+04 -3.208919E+04 -4.146008E+04 -4.763963E+02 -3.638916E+03 + 4.997677E+08 + 108 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.467816E+00 4.687721E+00 -8.757518E+00 7.670999E+00 -3.680623E+00 2.597406E+00 -8.280810E-01 3.589294E-01 4.060393E-02 + 3.222352E-01 -1.388939E+00 4.597168E-01 -1.101567E+00 1.303301E+00 -1.289494E+00 2.476130E+00 -1.798788E+00 -1.425838E-01 1.535148E+00 + 1.433215E-01 -2.472605E+00 2.038594E+00 -1.778560E+00 3.161531E+00 -2.080517E+00 1.716766E+00 -1.415136E+00 1.754354E+00 1.308698E+00 + -3.422812E+00 8.094254E+00 -2.152722E+01 4.771350E+01 -1.246082E+02 3.302790E+02 -8.090100E+02 1.351955E+03 -1.243234E+03 7.460485E+02 + -3.843313E+02 1.427630E+02 2.909197E+01 -1.536486E+02 1.349719E+02 -2.408600E+02 5.752271E+02 -1.611022E+02 -3.484416E+02 -2.250914E+02 + -4.630811E+02 -6.351323E+02 2.271890E+03 1.288490E+03 -1.297422E+04 3.259204E+04 7.368277E+03 -5.842398E+03 -2.236031E+03 -5.330394E+03 + 6.379610E+04 -3.375712E+04 3.171262E+04 -8.696681E+03 2.051941E+05 -4.898021E+04 7.202582E+03 3.959542E+04 1.749607E+03 1.895446E+03 + -3.910471E+09 + 109 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 9.223340E-01 -3.137602E+00 1.234930E+01 -1.565168E+01 1.071183E+01 -6.799704E+00 3.372922E+00 -1.639650E+00 9.226098E-01 + -1.203085E+00 1.597240E+00 -1.105878E+00 2.714438E+00 -2.077844E+00 6.190525E-01 -6.149510E-01 1.885583E-01 1.222873E+00 -3.011118E+00 + 1.934462E+00 -5.830958E-01 2.312212E+00 -1.914593E+00 -8.522329E-01 1.467656E+00 -2.345131E+00 3.264708E+00 -6.687268E+00 6.389663E+00 + -4.288516E+00 4.324256E-01 1.327448E+01 -2.692963E+01 5.662336E+01 -1.743169E+02 5.002158E+02 -1.243234E+03 2.183574E+03 -1.923067E+03 + 1.116652E+03 -5.741306E+02 1.526450E+02 6.234661E+01 -1.845010E+02 3.735270E+02 -6.721554E+02 -8.920958E+00 -4.106216E+02 1.115906E+03 + -2.433848E+03 4.472062E+03 -3.642058E+03 -3.765582E+03 3.058646E+04 -5.161856E+04 -2.871192E+04 1.132800E+04 2.886508E+03 -1.860103E+04 + 1.079415E+05 2.357735E+04 -2.567656E+04 6.026099E+03 -1.855309E+05 5.395456E+04 1.057115E+04 -3.946747E+04 2.298846E+03 7.627224E+03 + 8.109293E+09 + 110 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.311532E+00 9.800755E+00 -1.687744E+01 1.319549E+01 -7.446599E+00 6.426980E+00 -5.125171E+00 1.031423E+00 1.276525E+00 + 3.476584E-01 -2.223074E+00 2.290890E+00 -3.698995E+00 2.687906E+00 -4.220285E-01 -5.730012E-01 4.493443E-01 -5.712646E-01 1.494978E+00 + -1.482235E+00 9.602150E-01 -2.235546E+00 2.848061E+00 -2.775239E+00 1.794336E+00 7.994833E-01 -2.081029E+00 5.306989E+00 -1.158448E+01 + 1.482978E+01 -9.895028E+00 -4.812379E+00 1.398100E+01 -3.533554E+01 8.537354E+01 -2.557268E+02 7.460485E+02 -1.923067E+03 3.264990E+03 + -2.821813E+03 1.651858E+03 -7.011428E+02 9.568943E+01 3.506460E+02 -6.728218E+02 8.021146E+02 1.341672E+02 7.703210E+02 -2.237795E+03 + 4.440859E+03 -4.556014E+03 -3.633211E+03 9.700242E+03 -4.091090E+04 6.639641E+04 2.438626E+04 -1.311148E+04 -8.095645E+03 7.254571E+03 + -2.578390E+05 -2.885759E+04 -2.347562E+03 -1.854010E+04 -8.843509E+03 -3.417524E+04 -1.975477E+04 1.429238E+04 -3.412750E+03 4.983774E+03 + -5.927140E+09 + 111 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.575165E+00 -1.326859E+01 1.038094E+01 -3.708923E+00 4.153528E+00 -5.522074E+00 4.553941E+00 -2.604313E+00 4.191558E-01 + -9.280356E-01 3.062446E+00 -9.386156E-01 2.755295E+00 -4.576388E+00 2.862090E+00 9.268895E-02 -2.564839E+00 2.746375E+00 -1.913803E+00 + 2.111953E+00 -1.931250E+00 -9.149744E-02 4.553030E-01 2.923115E+00 -4.894301E+00 2.560793E+00 5.177449E+00 -1.690751E+01 2.415266E+01 + -2.325572E+01 1.641787E+01 -5.694023E+00 3.054263E-01 9.796792E+00 -4.156625E+01 1.031425E+02 -3.843313E+02 1.116652E+03 -2.821813E+03 + 5.434558E+03 -5.331254E+03 2.948083E+03 -1.272186E+03 1.165545E+02 5.005210E+02 -4.858240E+02 3.731438E+02 8.546214E+02 3.596113E+03 + -9.673792E+02 1.601650E+03 -1.481250E+03 -5.868498E+02 2.194333E+04 1.625896E+04 9.561287E+03 -2.927318E+03 1.149302E+04 1.169542E+04 + -7.773478E+03 4.903258E+04 -1.068924E+04 3.759467E+04 1.000482E+05 1.274052E+05 1.158974E+05 6.627484E+04 9.564160E+03 -1.446382E+04 + 1.115186E+10 + 112 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.018481E+00 -3.687444E+00 5.296798E+00 3.013549E+00 -1.189375E+01 6.052578E+00 -5.009252E+00 5.969934E+00 -3.237202E+00 + 6.564209E-01 -5.828227E-01 -2.840390E+00 1.898602E+00 1.891296E+00 -1.093757E+00 -1.542570E+00 5.043071E+00 -7.557992E+00 7.360487E+00 + -4.428012E+00 9.537632E-01 3.254731E+00 -2.417804E+00 -1.465394E-01 1.863667E+00 2.079430E+00 -1.996055E+01 3.850597E+01 -4.024642E+01 + 2.606437E+01 -1.062042E+01 -1.065010E+00 -8.398712E-01 -5.328886E-01 3.458605E+00 -5.517811E+00 1.427630E+02 -5.741306E+02 1.651858E+03 + -5.331254E+03 1.226570E+04 -1.146598E+04 5.828912E+03 -2.421817E+03 5.702946E+02 -6.344936E+02 1.086186E+03 -1.615205E+03 -1.223305E+03 + 1.059284E+04 -1.297596E+04 -1.179823E+04 8.522804E+02 -1.859347E+05 2.108400E+04 2.675293E+04 8.500471E+03 5.403872E+04 8.225994E+03 + -8.332188E+04 3.923552E+04 -1.260811E+04 -4.151881E+04 -1.426264E+05 -1.585812E+05 -1.352358E+05 3.096202E+03 -2.693326E+04 1.479337E+04 + -2.866100E+10 + 113 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -3.563396E+01 5.923276E+01 -5.146505E+01 3.098878E+01 -1.119639E+00 -1.021334E+01 1.066847E+01 -1.110118E+01 6.821518E+00 + -8.433008E-01 1.588943E+00 -2.263538E+00 -1.657075E+00 5.111496E-01 -3.999510E+00 5.348164E+00 -5.092688E+00 8.289946E+00 -7.539733E+00 + -1.157668E+00 6.331727E+00 -1.064334E+01 1.231518E+01 -8.750801E+00 2.180408E+00 -3.522059E+00 2.802552E+01 -4.951267E+01 4.086455E+01 + -2.832103E+01 2.754349E+01 -1.143342E+01 2.809690E+00 -4.998802E+00 8.864274E+00 -5.967190E+01 2.909197E+01 1.526450E+02 -7.011428E+02 + 2.948083E+03 -1.146598E+04 2.498606E+04 -2.178988E+04 1.221601E+04 -6.800483E+03 5.317613E+03 -1.381660E+03 1.996308E+03 1.196609E+03 + -7.770480E+03 1.429938E+03 3.554821E+04 -1.107346E+04 1.842492E+05 -1.369832E+05 -6.892252E+04 3.590248E+04 -9.554707E+04 3.966594E+04 + -5.462478E+05 -2.767102E+04 -3.905236E+04 1.077753E+04 2.297489E+05 9.116797E+05 -7.684267E+04 -1.849187E+04 1.619603E+04 -2.009370E+04 + 3.284684E+10 + 114 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 3.864798E+01 -7.573926E+01 8.265881E+01 -6.311706E+01 5.920908E+00 2.970395E+01 -2.538895E+01 1.454988E+01 -6.727756E+00 + -3.277129E-01 -3.173461E+00 7.019890E+00 -2.021804E+00 2.377520E+00 5.807392E-01 -5.697560E-01 3.569731E+00 -8.012794E+00 5.880013E+00 + 7.430380E+00 -8.686308E+00 1.214117E+01 -2.050013E+01 2.063600E+01 -1.302109E+01 1.493644E+01 -2.456644E+01 3.270256E+01 -2.563247E+01 + 4.334782E+01 -6.538249E+01 6.751392E+01 -6.808204E+01 7.731131E+01 -8.934458E+01 1.293938E+02 -1.536486E+02 6.234661E+01 9.568943E+01 + -1.272186E+03 5.828912E+03 -2.178988E+04 4.539892E+04 -4.379707E+04 2.858226E+04 -1.943544E+04 6.580757E+03 -5.994911E+03 2.719139E+03 + -1.569750E+03 1.851884E+04 -3.079885E+04 2.854519E+04 -2.061710E+05 1.280538E+05 6.964439E+04 -5.324831E+04 1.093461E+05 1.350121E+04 + 5.722753E+05 7.697190E+04 5.195084E+04 7.515832E+04 -5.300560E+05 -1.076477E+06 4.177656E+05 5.957958E+04 -1.511137E+04 -3.799967E+04 + 1.351102E+10 + 115 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.319911E+01 6.189991E+01 -7.694702E+01 8.133630E+01 -1.086794E+01 -3.909984E+01 2.930020E+01 -8.615069E+00 8.394308E+00 + 1.962935E+00 -3.670704E+00 -1.465355E+00 -2.779292E+00 6.346914E+00 -1.344671E+01 1.273844E+01 -7.134752E+00 -4.470779E-01 1.932199E+00 + -6.856296E+00 4.836358E-01 -6.533785E+00 9.405506E+00 -2.466034E+00 -5.539112E+00 6.992074E+00 -3.299045E+01 3.127636E+01 -7.489086E+00 + -1.556322E+01 4.014913E+01 -5.169786E+01 7.447390E+01 -1.027058E+02 1.266444E+02 -1.353316E+02 1.349719E+02 -1.845010E+02 3.506460E+02 + 1.165545E+02 -2.421817E+03 1.221601E+04 -4.379707E+04 1.149580E+05 -1.217078E+05 7.473014E+04 -4.023400E+04 2.835295E+04 -2.534875E+04 + -1.498028E+04 -1.186708E+04 3.525736E+03 4.028767E+04 1.869716E+05 -2.479481E+05 -4.097944E+04 3.259342E+04 -6.689179E+04 -5.305986E+04 + -1.149048E+06 6.237365E+04 9.038461E+04 -1.549830E+05 1.053700E+06 6.170460E+05 -3.035939E+05 -1.142406E+05 -5.596704E+04 3.175520E+04 + -7.593121E+10 + 116 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.953487E+02 -3.837509E+02 3.307381E+02 -1.400424E+02 -3.916487E+01 8.639504E+01 -6.860431E+01 3.246452E+01 -1.079018E+01 + -5.588408E+00 9.497304E+00 -5.817779E+00 5.837521E+00 -7.437546E+00 7.633512E+00 -1.744909E+01 2.617526E+01 -2.710654E+01 1.674474E+01 + -9.184831E+00 6.511578E+00 1.656464E+01 -1.726072E+01 1.408757E+01 -1.849151E+01 1.646333E+01 2.635849E+01 -2.371511E+01 -1.925891E+01 + 5.632102E+01 -4.808485E+01 2.601244E+01 -7.051468E+01 1.045403E+02 -1.457737E+02 2.167522E+02 -2.408600E+02 3.735270E+02 -6.728218E+02 + 5.005210E+02 5.702946E+02 -6.800483E+03 2.858226E+04 -1.217078E+05 3.378804E+05 -3.247520E+05 2.019100E+05 -8.280247E+04 9.380808E+04 + -2.991649E+04 -4.580579E+04 -3.730279E+04 -6.095832E+02 -5.902768E+05 5.847443E+05 -2.028160E+04 1.397296E+05 3.150208E+04 -1.102154E+05 + 2.500128E+06 -3.989950E+05 -1.214798E+05 2.627864E+05 -7.863052E+05 -1.807709E+05 1.071747E+06 -3.488791E+05 5.109354E+04 -3.358973E+04 + -2.489680E+11 + 117 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -6.764146E+01 1.464725E+02 -2.853115E+02 2.677458E+02 -2.537140E+01 -7.130093E+01 5.746992E+01 -1.906816E+01 -6.726499E+00 + -7.089022E+00 -2.313535E+01 3.093390E+01 -5.381365E+01 5.214671E+01 -2.617400E+01 2.626948E+01 -1.413346E+01 -1.461739E+00 -5.405389E+00 + 1.840319E+01 -2.191883E+01 -1.421287E+01 9.352343E+00 6.883397E+01 -6.071920E+01 2.033236E+01 -2.534598E+01 2.943716E+01 -6.130068E+01 + 4.345093E+01 -4.976131E+01 -6.782267E+00 1.762711E+02 -2.032111E+02 2.093994E+02 -4.891306E+02 5.752271E+02 -6.721554E+02 8.021146E+02 + -4.858240E+02 -6.344936E+02 5.317613E+03 -1.943544E+04 7.473014E+04 -3.247520E+05 1.113492E+06 -1.132582E+06 4.727430E+05 -2.226598E+05 + 1.025866E+05 2.277648E+05 1.520371E+05 4.096530E+05 1.722604E+05 8.890679E+05 -3.295933E+04 -1.165933E+05 9.280246E+04 1.421903E+05 + -2.201278E+06 8.039928E+05 3.161465E+05 1.781294E+05 -1.899550E+06 8.867509E+05 6.589586E+05 2.031797E+06 -8.510018E+04 -4.594036E+04 + 2.586144E+10 + 118 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 7.329343E+01 -1.441555E+02 2.826089E+02 -3.713521E+02 3.701359E+02 -3.047936E+02 1.674917E+02 1.018031E+01 -3.158621E+01 + -1.517394E+01 1.252404E+02 -1.641632E+02 1.347887E+02 -1.103387E+02 8.693785E+01 -9.551065E+01 4.234223E+01 7.732726E+01 -7.380394E+01 + 7.392864E+00 2.426206E+01 -4.587457E+01 7.829644E+01 -2.871674E+02 2.784013E+02 -2.019498E+02 1.601011E+02 -2.365140E+02 2.970164E+02 + -2.034055E+02 2.355071E+02 -2.127958E+02 4.122570E+01 2.984734E+01 -5.941340E+02 6.250182E+02 -1.611022E+02 -8.920958E+00 1.341672E+02 + 3.731438E+02 1.086186E+03 -1.381660E+03 6.580757E+03 -4.023400E+04 2.019100E+05 -1.132582E+06 4.518499E+06 -2.790683E+06 6.250711E+05 + -6.827779E+05 -5.741473E+05 -7.576778E+05 2.002787E+05 -3.973350E+05 -3.798694E+06 5.481468E+05 4.603685E+05 -4.829630E+03 7.861116E+05 + 2.542471E+06 -2.161801E+05 -1.475492E+06 8.937138E+05 4.240114E+06 1.277614E+06 -3.281788E+06 -7.203688E+05 -1.600369E+05 3.017818E+05 + 6.573551E+11 + 119 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.089858E+02 9.275701E+01 1.890004E+02 -2.014000E+02 -1.573526E+02 2.169287E+02 5.894084E+01 -1.122690E+02 -1.397158E+01 + -4.873032E+00 -1.013865E+02 1.824880E+02 -1.531901E+02 1.278889E+02 -1.153673E+02 9.951058E+01 -6.451691E+01 -3.633901E+01 8.090222E+01 + 4.723687E-01 -5.555972E+01 1.081883E+02 -2.011476E+02 2.175546E+02 -1.466222E+02 1.460177E+02 -1.612438E+02 6.803310E+01 1.292000E+02 + -3.019795E+02 2.863276E+02 -6.734779E+01 -2.506394E+02 3.643820E+02 -5.419619E+01 4.042292E+02 -3.484416E+02 -4.106216E+02 7.703210E+02 + 8.546214E+02 -1.615205E+03 1.996308E+03 -5.994911E+03 2.835295E+04 -8.280247E+04 4.727430E+05 -2.790683E+06 1.148862E+07 -2.535840E+06 + 7.546866E+05 -2.346896E+05 -2.492129E+05 -1.022937E+06 -7.703353E+05 1.991969E+06 1.130820E+06 4.274780E+05 4.051066E+05 2.439018E+05 + 1.176882E+07 2.336269E+06 1.815270E+05 1.858774E+06 -3.950408E+06 -1.444769E+07 3.663684E+06 8.533900E+05 1.895894E+05 1.909990E+05 + -1.944804E+11 + 120 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -3.298428E+02 4.570410E+02 -4.684142E+02 6.575748E+02 -5.575862E+02 2.994823E+02 -1.230664E+02 5.400637E+01 -5.448398E+01 + 6.905270E+01 -2.565810E+01 -5.515830E+00 -5.959087E+01 5.437423E+01 2.894191E+00 -2.450507E+01 3.586859E+01 -2.624641E+01 6.253500E+01 + -3.292927E+01 -1.816458E+01 -6.105664E+01 1.320619E+02 5.501070E+01 -2.535786E+02 9.330565E+01 2.165063E+02 -4.207344E+02 2.683898E+02 + -1.054637E+02 2.744548E+01 -2.773307E+02 3.394092E+02 -5.789165E+01 -5.250196E+02 4.550738E+02 -2.250914E+02 1.115906E+03 -2.237795E+03 + 3.596113E+03 -1.223305E+03 1.196609E+03 2.719139E+03 -2.534875E+04 9.380808E+04 -2.226598E+05 6.250711E+05 -2.535840E+06 1.199879E+07 + -3.482297E+06 2.922209E+06 -1.467166E+06 2.687599E+06 7.784182E+05 1.480343E+06 -3.870535E+05 2.356909E+05 -1.792003E+05 -1.424430E+06 + -1.708015E+07 2.946700E+06 1.028099E+06 -7.613389E+05 5.394774E+06 2.445972E+06 8.484790E+05 9.536788E+05 5.857458E+05 -2.701172E+05 + 1.067786E+12 + 121 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.409130E+02 -4.448723E+01 2.886497E+02 3.506702E+02 -9.734717E+02 8.898436E+02 -2.891840E+02 5.236282E+01 7.812653E+01 + -1.692235E+02 4.001833E+00 9.639202E+01 -1.328956E+02 1.810666E+02 -1.599173E+02 -2.793284E+01 5.701558E+01 -1.089824E+02 1.064025E+02 + -2.360820E+02 1.724435E+02 -2.163547E+02 6.785872E+01 -5.425894E+01 1.026202E+01 5.069322E+02 -1.092604E+03 1.608920E+03 -1.775734E+03 + 1.638992E+03 -1.069798E+03 4.255980E+02 2.806414E+02 -3.330446E+02 -3.718396E+02 4.201697E+02 -4.630811E+02 -2.433848E+03 4.440859E+03 + -9.673792E+02 1.059284E+04 -7.770480E+03 -1.569750E+03 -1.498028E+04 -2.991649E+04 1.025866E+05 -6.827779E+05 7.546866E+05 -3.482297E+06 + 4.024710E+07 -2.346510E+07 1.382939E+07 -3.640669E+06 -2.984868E+06 -1.259864E+06 -1.692200E+06 -1.361119E+06 -3.152538E+06 -1.260127E+06 + 1.257953E+07 -1.047136E+07 5.835425E+05 -7.092607E+05 7.966261E+06 1.480594E+07 -2.351779E+06 -1.549886E+06 -1.254239E+05 -1.177409E+06 + -5.598214E+12 + 122 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.700609E+02 9.522332E+00 6.388416E+02 -5.128418E+02 3.226986E+02 -2.000227E+01 6.989965E+01 -2.566296E+02 -1.779053E+01 + 4.817682E+02 -4.556536E+02 2.347120E+02 -9.962346E+01 9.012563E+01 -1.711757E+02 2.061917E+02 -3.206052E+02 2.347660E+02 -3.096056E+02 + 2.970673E+02 -1.414508E+00 -2.649966E+02 4.129700E+02 -6.594325E+02 3.414469E+02 -1.608325E+02 4.401234E+02 -9.570320E+02 9.457326E+02 + -1.196667E+03 8.100384E+02 -5.180202E+02 2.539351E+02 5.203354E+02 -1.266329E+03 -8.566128E+00 -6.351323E+02 4.472062E+03 -4.556014E+03 + 1.601650E+03 -1.297596E+04 1.429938E+03 1.851884E+04 -1.186708E+04 -4.580579E+04 2.277648E+05 -5.741473E+05 -2.346896E+05 2.922209E+06 + -2.346510E+07 9.776366E+07 -6.824868E+07 2.254582E+07 1.879391E+07 -1.111896E+07 4.201202E+05 3.194800E+06 1.757923E+06 -1.023201E+06 + 7.579482E+06 -1.591308E+06 3.641147E+06 -1.570984E+06 -3.323366E+07 2.219568E+07 -4.442350E+07 3.148967E+05 -3.367645E+06 9.736775E+05 + 8.752808E+12 + 123 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.262416E+01 4.054288E+02 -1.111026E+03 1.663535E+03 -1.327244E+03 5.155148E+02 8.954947E+00 -2.370842E+02 2.702827E+02 + -3.550362E+02 3.863975E+02 -3.069883E+02 1.247366E+02 -2.961526E+02 -4.059654E+00 4.745066E+02 3.140635E+01 -6.213880E+02 7.120524E+02 + -2.428327E+02 -5.840310E+02 7.911494E+02 -8.341918E+02 8.277689E+02 -4.032834E+02 -6.052722E+01 2.185007E+02 -1.895687E+02 -3.066125E+02 + 2.251030E+03 -3.215869E+03 2.855563E+03 -3.719587E+02 -8.339989E+02 1.240115E+03 1.118408E+03 2.271890E+03 -3.642058E+03 -3.633211E+03 + -1.481250E+03 -1.179823E+04 3.554821E+04 -3.079885E+04 3.525736E+03 -3.730279E+04 1.520371E+05 -7.576778E+05 -2.492129E+05 -1.467166E+06 + 1.382939E+07 -6.824868E+07 2.085136E+08 -1.424400E+08 1.574832E+08 -8.176910E+06 -3.660468E+07 -7.605323E+06 -9.342959E+06 -1.364161E+07 + 1.044224E+06 -8.151248E+05 -2.352368E+06 3.261016E+06 1.822367E+07 2.883979E+07 -2.114653E+07 2.116166E+07 -3.093062E+06 1.211333E+06 + -1.266012E+13 + 124 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.153100E+03 -6.165877E+03 4.785743E+03 -1.890353E+03 1.762486E+03 -1.524157E+03 9.052660E+02 -3.699089E+02 2.150299E+01 + -3.265362E-01 -1.784622E+02 4.741874E+02 -3.141494E+02 3.002374E+02 3.203225E+02 -5.658806E+02 1.621750E+02 2.376835E+02 -3.003320E+02 + 4.648691E+02 1.429169E+01 -2.676290E+02 4.460969E+02 -1.427218E+01 -4.732760E+02 5.511715E+02 -5.850158E+02 1.108796E+03 -4.323714E+02 + -2.090389E+03 2.722876E+03 -1.631866E+03 1.387631E+03 -1.197641E+03 6.766637E+02 6.235630E+01 1.288490E+03 -3.765582E+03 9.700242E+03 + -5.868498E+02 8.522804E+02 -1.107346E+04 2.854519E+04 4.028767E+04 -6.095832E+02 4.096530E+05 2.002787E+05 -1.022937E+06 2.687599E+06 + -3.640669E+06 2.254582E+07 -1.424400E+08 2.693203E+08 -3.384506E+08 3.393313E+08 4.558686E+07 1.128771E+07 9.372306E+06 9.971227E+06 + -4.379155E+07 3.771830E+06 5.510248E+06 -5.133487E+06 2.429491E+06 3.544244E+06 1.690700E+07 -1.418150E+07 2.694424E+06 -2.203820E+06 + 7.315382E+12 + 125 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.213227E+04 3.601651E+04 -2.195689E+04 5.643919E+03 2.707720E+03 -1.158463E+03 1.042045E+03 1.454058E+03 -3.041540E+03 + 1.485182E+03 3.639374E+03 -4.525616E+03 3.274040E+03 -2.409488E+03 8.245068E+02 -1.789264E+02 -7.428247E+02 2.849904E+03 -2.073391E+03 + -2.830070E+02 -2.123088E+03 4.256822E+03 -5.388239E+03 3.614034E+03 -8.169415E+02 9.025624E+02 -4.118975E+03 3.352673E+03 -4.320952E+03 + 1.150314E+04 -1.291555E+04 6.078899E+03 2.823480E+03 6.615803E+03 -2.309122E+03 -8.286886E+03 -1.297422E+04 3.058646E+04 -4.091090E+04 + 2.194333E+04 -1.859347E+05 1.842492E+05 -2.061710E+05 1.869716E+05 -5.902768E+05 1.722604E+05 -3.973350E+05 -7.703353E+05 7.784182E+05 + -2.984868E+06 1.879391E+07 1.574832E+08 -3.384506E+08 5.665349E+09 -2.926334E+09 -1.147293E+09 -3.544247E+08 -2.796112E+08 -5.035830E+08 + 9.465725E+08 -1.748442E+08 6.774240E+07 -3.390700E+07 -2.455649E+08 -1.828782E+08 -3.299930E+08 -1.014627E+08 7.160923E+06 9.867461E+05 + -2.629126E+12 + 126 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.554892E+04 1.457948E+03 2.424706E+04 2.392908E+03 -2.064878E+04 7.802534E+03 4.474175E+02 2.443024E+03 -2.500255E+03 + -1.070639E+03 -3.065140E+03 6.490474E+03 2.827006E+02 -1.118800E+03 2.293698E+03 -5.825125E+03 8.198053E+03 -6.952866E+03 -7.349808E+01 + 4.703476E+03 -2.218691E+03 4.548245E+03 -1.231520E+04 1.266757E+04 -4.203118E+03 3.570947E+03 -6.624439E+03 2.866477E+03 -3.838543E+02 + 5.457444E+02 -5.833399E+03 8.627189E+03 1.567154E+04 -3.533747E+04 6.160276E+04 -4.254284E+04 3.259204E+04 -5.161856E+04 6.639641E+04 + 1.625896E+04 2.108400E+04 -1.369832E+05 1.280538E+05 -2.479481E+05 5.847443E+05 8.890679E+05 -3.798694E+06 1.991969E+06 1.480343E+06 + -1.259864E+06 -1.111896E+07 -8.176910E+06 3.393313E+08 -2.926334E+09 2.433440E+10 4.938418E+07 -5.976624E+08 -1.835903E+08 -7.186568E+08 + 1.156237E+09 -4.706924E+08 1.550969E+07 -7.486943E+07 2.912131E+08 -1.134430E+09 1.594944E+08 1.041971E+08 -7.618526E+06 1.702957E+07 + 1.488450E+13 + 127 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 9.573317E+02 -4.750482E+03 4.061512E+03 1.683080E+02 -9.426048E+02 2.016701E+02 -7.553202E+02 9.875654E+02 1.558418E+03 + -1.607101E+03 3.378122E+01 -1.426164E+03 2.444150E+03 -1.108999E+03 -3.263376E+02 1.447025E+03 -3.604408E+02 -1.307995E+02 -9.847842E+02 + 1.145172E+03 1.565922E+03 -3.263311E+03 1.109170E+03 3.035862E+03 -2.618320E+03 2.643967E+03 -1.106459E+03 -1.349011E+03 2.876978E+03 + -3.467974E+03 -2.479368E+02 3.645262E+03 -8.779968E+03 9.394272E+03 -2.057204E+03 -4.039804E+02 7.368277E+03 -2.871192E+04 2.438626E+04 + 9.561287E+03 2.675293E+04 -6.892252E+04 6.964439E+04 -4.097944E+04 -2.028160E+04 -3.295933E+04 5.481468E+05 1.130820E+06 -3.870535E+05 + -1.692200E+06 4.201202E+05 -3.660468E+07 4.558686E+07 -1.147293E+09 4.938418E+07 2.757532E+09 -1.386769E+08 6.570181E+08 5.636702E+08 + -1.238058E+09 2.353078E+08 -3.137540E+07 9.095042E+07 1.896212E+08 -1.417674E+07 -1.656228E+07 6.476228E+07 -2.090016E+06 4.144784E+05 + 1.748647E+13 + 128 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.189372E+03 1.171306E+03 -1.761763E+03 -1.710041E+03 -1.011692E+02 2.485618E+03 -5.161752E+02 -1.485751E+03 1.063198E+03 + -5.035818E+02 7.242895E+02 -9.430108E+02 -7.385657E+02 7.992313E+02 -4.538153E+00 9.552731E+01 2.318178E+02 -3.724142E+02 1.002324E+03 + -1.440127E+03 1.037331E+03 -1.008085E+03 1.230962E+03 4.468193E+02 -1.770721E+03 1.590408E+03 -7.321441E+02 3.721504E+02 4.551602E+02 + -1.169824E+03 2.651724E+03 -1.400082E+03 2.164800E+03 2.449474E+02 -8.326992E+03 6.075077E+03 -5.842398E+03 1.132800E+04 -1.311148E+04 + -2.927318E+03 8.500471E+03 3.590248E+04 -5.324831E+04 3.259342E+04 1.397296E+05 -1.165933E+05 4.603685E+05 4.274780E+05 2.356909E+05 + -1.361119E+06 3.194800E+06 -7.605323E+06 1.128771E+07 -3.544247E+08 -5.976624E+08 -1.386769E+08 1.075115E+09 -8.302346E+07 7.461572E+08 + -1.107242E+09 2.657275E+08 -4.596166E+07 3.493541E+07 1.498884E+07 2.454500E+08 -1.017590E+08 3.558671E+07 4.090952E+05 1.110471E+07 + 2.914020E+12 + 129 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -8.712167E+02 -1.506251E+02 8.419772E+02 -2.874214E+03 3.018876E+03 -9.186808E+02 6.337633E+02 8.833084E+01 3.178253E+02 + -1.336637E+03 1.074386E+03 -9.323448E+02 1.751536E+03 -1.267542E+03 6.740405E+02 -5.652172E+02 3.989380E+02 1.451592E+02 -7.798490E+02 + 1.206010E+03 -5.789623E+02 -6.705359E+02 -1.050849E+03 2.025430E+03 -9.940092E+02 1.230490E+03 -2.768752E+03 3.708611E+03 -1.779520E+03 + -4.374881E+02 1.067008E+01 1.321920E+03 -4.182182E+03 3.221233E+03 -5.900245E+03 1.037318E+04 -2.236031E+03 2.886508E+03 -8.095645E+03 + 1.149302E+04 5.403872E+04 -9.554707E+04 1.093461E+05 -6.689179E+04 3.150208E+04 9.280246E+04 -4.829630E+03 4.051066E+05 -1.792003E+05 + -3.152538E+06 1.757923E+06 -9.342959E+06 9.372306E+06 -2.796112E+08 -1.835903E+08 6.570181E+08 -8.302346E+07 8.477404E+08 8.735006E+07 + -5.658481E+08 2.886790E+08 -1.166508E+07 1.327346E+07 -4.196659E+07 -2.590506E+07 -6.898422E+07 5.562413E+07 -6.974848E+06 7.185640E+06 + -1.585592E+12 + 130 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -7.660822E+02 8.277848E+03 -4.721168E+03 -7.754560E+02 -5.346066E+03 5.751716E+03 -5.943940E+03 4.629764E+03 -2.411156E+03 + 1.448359E+02 1.649741E+03 -3.628529E+03 2.122956E+03 -5.041942E+02 2.211307E+02 7.049458E+02 7.825253E+01 -4.229096E+02 1.427833E+03 + -1.965382E+03 5.218849E+02 -2.961920E+02 -1.196687E+02 1.666949E+03 -8.473933E+02 2.582988E+02 -1.428660E+03 1.937996E+03 1.481290E+03 + -5.721554E+03 8.238348E+03 -3.678338E+02 -5.241214E+03 8.143493E+03 -1.158054E+04 8.031514E+03 -5.330394E+03 -1.860103E+04 7.254571E+03 + 1.169542E+04 8.225994E+03 3.966594E+04 1.350121E+04 -5.305986E+04 -1.102154E+05 1.421903E+05 7.861116E+05 2.439018E+05 -1.424430E+06 + -1.260127E+06 -1.023201E+06 -1.364161E+07 9.971227E+06 -5.035830E+08 -7.186568E+08 5.636702E+08 7.461572E+08 8.735006E+07 4.035810E+09 + -6.502469E+09 2.136802E+09 -5.548532E+08 2.862016E+08 8.991046E+08 3.085258E+08 2.479676E+08 2.144075E+08 -2.985958E+07 -2.570585E+05 + -7.366624E+12 + 131 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -9.765402E+03 -1.997214E+03 1.507618E+04 -9.499864E+03 2.553126E+04 -3.299666E+04 1.920848E+04 -1.143155E+04 2.296543E+04 + -1.799140E+04 4.934956E+03 7.779569E+03 -1.262971E+03 6.318892E+03 -5.811400E+03 -4.919414E+03 -2.248188E+03 5.034648E+03 -2.401698E+03 + 4.559159E+03 -6.085204E+03 1.775221E+04 -1.514813E+04 3.083123E+03 1.739332E+03 6.105832E+02 -2.253268E+02 3.466078E+03 6.171192E+03 + -3.561953E+04 3.737425E+04 -2.793704E+04 -2.067211E+03 4.732252E+04 -8.889071E+04 1.442948E+04 6.379610E+04 1.079415E+05 -2.578390E+05 + -7.773478E+03 -8.332188E+04 -5.462478E+05 5.722753E+05 -1.149048E+06 2.500128E+06 -2.201278E+06 2.542471E+06 1.176882E+07 -1.708015E+07 + 1.257953E+07 7.579482E+06 1.044224E+06 -4.379155E+07 9.465725E+08 1.156237E+09 -1.238058E+09 -1.107242E+09 -5.658481E+08 -6.502469E+09 + 1.224610E+11 -4.755008E+09 3.224030E+09 -1.108713E+09 -1.062696E+09 2.166672E+08 -3.331615E+08 -9.126785E+08 -3.416202E+06 2.392586E+07 + -1.402988E+13 + 132 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -8.118300E+03 7.291421E+03 -4.136021E+03 1.402169E+04 -1.107336E+04 5.554294E+03 -3.282980E+03 1.351186E+03 -3.908165E+03 + 1.219429E+03 1.265472E+03 -4.237826E+03 3.626240E+03 -2.982545E+03 2.436355E+03 -2.341486E+03 3.933292E+03 -2.028590E+03 6.764857E+02 + -4.648933E+02 -1.188162E+03 2.866279E+02 3.560120E+03 -4.361760E+03 5.437522E+03 -5.975017E+03 2.990348E+03 2.055558E+03 -4.915462E+03 + 6.395640E+03 -5.074259E+03 -4.676678E+03 1.321033E+04 -1.791828E+04 1.928812E+04 9.684384E+03 -3.375712E+04 2.357735E+04 -2.885759E+04 + 4.903258E+04 3.923552E+04 -2.767102E+04 7.697190E+04 6.237365E+04 -3.989950E+05 8.039928E+05 -2.161801E+05 2.336269E+06 2.946700E+06 + -1.047136E+07 -1.591308E+06 -8.151248E+05 3.771830E+06 -1.748442E+08 -4.706924E+08 2.353078E+08 2.657275E+08 2.886790E+08 2.136802E+09 + -4.755008E+09 7.636514E+09 -9.088226E+08 1.327490E+09 2.287070E+09 -1.634503E+08 5.333320E+08 2.696827E+08 2.734358E+07 -1.270967E+06 + -4.813886E+13 + 133 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.163725E+03 2.088390E+03 7.833287E+03 -1.418209E+04 8.653400E+03 -4.084201E+03 9.941911E+02 9.682145E+02 -6.033816E+02 + 7.401241E+02 -7.042855E+02 5.097127E+02 1.015890E+03 -7.540519E+02 -1.615692E+03 2.073698E+03 -5.794170E+02 -1.681838E+03 4.409707E+03 + -4.734152E+03 4.555856E+03 -1.447660E+03 -8.645687E+02 1.784182E+02 1.112768E+03 -8.064337E+02 -5.768534E+02 9.848585E+02 2.768813E+02 + -1.579568E+03 -6.533855E+02 1.056376E+04 -1.231068E+04 8.032595E+03 4.093086E+03 -2.583097E+04 3.171262E+04 -2.567656E+04 -2.347562E+03 + -1.068924E+04 -1.260811E+04 -3.905236E+04 5.195084E+04 9.038461E+04 -1.214798E+05 3.161465E+05 -1.475492E+06 1.815270E+05 1.028099E+06 + 5.835425E+05 3.641147E+06 -2.352368E+06 5.510248E+06 6.774240E+07 1.550969E+07 -3.137540E+07 -4.596166E+07 -1.166508E+07 -5.548532E+08 + 3.224030E+09 -9.088226E+08 2.270029E+09 -3.580700E+08 -1.737594E+09 4.901339E+08 -1.405866E+08 -2.510698E+08 6.704140E+06 -7.875944E+05 + 3.886404E+12 + 134 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.828084E+03 -1.752824E+03 -2.169028E+01 3.501055E+03 -3.803394E+03 8.727254E+02 -1.151060E+03 5.902367E+02 -7.662216E+02 + 1.917641E+02 1.958785E+02 -6.903124E+02 9.861610E+02 -1.772150E+03 1.180033E+03 -2.896082E+02 2.946342E+02 5.310931E+02 -2.413142E+03 + 2.684029E+03 -3.059434E+03 2.904881E+03 -2.812700E+03 2.765747E+03 3.915955E+02 -2.461420E+03 2.695687E+03 -4.909992E+03 3.817544E+03 + -1.887175E+03 3.828352E+03 -5.667037E+03 7.683234E+03 4.118426E+02 -2.535151E+03 6.370360E+03 -8.696681E+03 6.026099E+03 -1.854010E+04 + 3.759467E+04 -4.151881E+04 1.077753E+04 7.515832E+04 -1.549830E+05 2.627864E+05 1.781294E+05 8.937138E+05 1.858774E+06 -7.613389E+05 + -7.092607E+05 -1.570984E+06 3.261016E+06 -5.133487E+06 -3.390700E+07 -7.486943E+07 9.095042E+07 3.493541E+07 1.327346E+07 2.862016E+08 + -1.108713E+09 1.327490E+09 -3.580700E+08 2.111000E+09 -2.020055E+09 1.215701E+09 1.463807E+09 -1.075173E+08 5.772150E+06 -1.723956E+07 + -2.352161E+12 + 135 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.777000E+04 -7.654128E+04 7.474604E+04 -3.283018E+04 7.783656E+03 9.985343E+02 1.973765E+03 -7.016591E+03 1.479406E+04 + -5.254685E+03 3.369476E+03 -7.042715E+03 4.856648E+03 -1.082573E+03 8.900351E+03 -2.590631E+04 3.106568E+04 -2.414651E+04 6.254495E+03 + 5.403578E+03 -1.535447E+04 4.997177E+03 6.990040E+03 -6.205999E+03 1.230328E+04 -2.458765E+03 -1.925434E+04 2.876266E+04 -3.309541E+04 + 2.381183E+04 -4.126025E+04 4.211532E+04 -4.088412E+03 -1.451816E+04 6.928838E+04 -9.413046E+04 2.051941E+05 -1.855309E+05 -8.843509E+03 + 1.000482E+05 -1.426264E+05 2.297489E+05 -5.300560E+05 1.053700E+06 -7.863052E+05 -1.899550E+06 4.240114E+06 -3.950408E+06 5.394774E+06 + 7.966261E+06 -3.323366E+07 1.822367E+07 2.429491E+06 -2.455649E+08 2.912131E+08 1.896212E+08 1.498884E+07 -4.196659E+07 8.991046E+08 + -1.062696E+09 2.287070E+09 -1.737594E+09 -2.020055E+09 9.985754E+10 -1.549082E+10 1.385680E+10 2.114137E+09 4.582225E+08 -1.205250E+07 + -5.973120E+13 + 136 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.793893E+03 -2.371510E+04 1.441199E+04 -9.288895E+03 1.847448E+04 -1.906055E+04 2.182537E+04 -8.432482E+03 -6.831816E+02 + -2.548910E+03 7.101137E+03 -2.603992E+03 -8.254664E+03 5.512860E+03 4.634022E+03 1.712459E+03 -4.775075E+03 5.059404E+03 -4.900962E+03 + 8.595982E+03 -1.888438E+03 -1.056362E+03 1.076053E+04 -4.992703E+03 -7.220233E+03 7.316489E+03 -7.143454E+03 -8.189314E+03 1.896882E+04 + -2.191112E+04 2.681434E+04 -1.470481E+04 -8.747014E+03 1.367494E+04 -1.908270E+04 2.733461E+04 -4.898021E+04 5.395456E+04 -3.417524E+04 + 1.274052E+05 -1.585812E+05 9.116797E+05 -1.076477E+06 6.170460E+05 -1.807709E+05 8.867509E+05 1.277614E+06 -1.444769E+07 2.445972E+06 + 1.480594E+07 2.219568E+07 2.883979E+07 3.544244E+06 -1.828782E+08 -1.134430E+09 -1.417674E+07 2.454500E+08 -2.590506E+07 3.085258E+08 + 2.166672E+08 -1.634503E+08 4.901339E+08 1.215701E+09 -1.549082E+10 1.693709E+11 -3.988332E+09 4.081054E+08 -4.566198E+08 -2.815516E+07 + 1.692247E+14 + 137 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.579441E+04 -1.970351E+04 1.021418E+04 -1.520963E+04 1.170647E+04 -7.308238E+03 2.611124E+02 -1.044226E+03 5.049613E+03 + 2.337674E+02 -3.135384E+03 -2.116211E+03 4.795798E+03 -4.870750E+02 2.172032E+03 -3.173878E+03 4.447777E+03 -2.449805E+03 1.359436E+03 + -4.846896E+03 6.004372E+03 -8.024311E+03 3.521477E+03 9.703152E+03 -4.432033E+03 -4.152683E+03 -9.516290E+03 1.836673E+04 -1.193401E+04 + -6.139980E+03 3.004720E+04 -1.731653E+04 2.934569E+04 -4.098473E+04 1.896061E+04 -3.208919E+04 7.202582E+03 1.057115E+04 -1.975477E+04 + 1.158974E+05 -1.352358E+05 -7.684267E+04 4.177656E+05 -3.035939E+05 1.071747E+06 6.589586E+05 -3.281788E+06 3.663684E+06 8.484790E+05 + -2.351779E+06 -4.442350E+07 -2.114653E+07 1.690700E+07 -3.299930E+08 1.594944E+08 -1.656228E+07 -1.017590E+08 -6.898422E+07 2.479676E+08 + -3.331615E+08 5.333320E+08 -1.405866E+08 1.463807E+09 1.385680E+10 -3.988332E+09 4.112357E+10 -9.160867E+09 2.470321E+09 4.186357E+07 + -5.659074E+14 + 138 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 3.301750E+03 -8.816662E+03 8.506810E+03 2.270845E+03 -1.103997E+04 6.530834E+03 -6.385652E+02 8.138512E+02 3.668407E+02 + -7.503683E+01 -1.608282E+03 3.582901E+03 3.416226E+03 -2.828498E+03 -1.883360E+03 4.054008E+03 -1.484144E+03 -4.413870E+03 4.993888E+03 + -7.227568E+02 -5.433455E+03 7.099859E+03 -7.314407E+02 -6.820630E+03 1.635430E+03 6.368682E+03 -2.681281E+03 -4.949958E+03 3.348668E+03 + 7.993012E+03 -1.469126E+04 8.210362E+03 -1.338740E+03 -9.730895E+03 3.296892E+04 -4.146008E+04 3.959542E+04 -3.946747E+04 1.429238E+04 + 6.627484E+04 3.096202E+03 -1.849187E+04 5.957958E+04 -1.142406E+05 -3.488791E+05 2.031797E+06 -7.203688E+05 8.533900E+05 9.536788E+05 + -1.549886E+06 3.148967E+05 2.116166E+07 -1.418150E+07 -1.014627E+08 1.041971E+08 6.476228E+07 3.558671E+07 5.562413E+07 2.144075E+08 + -9.126785E+08 2.696827E+08 -2.510698E+08 -1.075173E+08 2.114137E+09 4.081054E+08 -9.160867E+09 1.496100E+10 -1.988684E+09 -1.696783E+08 + 7.530490E+13 + 139 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.912447E+03 1.097358E+03 3.219388E+02 1.675848E+03 -3.283474E+03 2.113492E+03 -1.045372E+03 2.845357E+02 4.766369E+02 + -4.339882E+02 6.358583E+02 -1.304182E+03 4.934524E+02 -3.941718E+02 9.249022E+02 -8.474201E+02 1.930642E+02 3.238957E+02 4.790111E+02 + -1.279508E+03 1.680996E+03 -1.551209E+03 5.292745E+02 2.378166E+02 1.186401E+03 1.360745E+01 -1.205308E+03 1.907345E+03 -2.577769E+03 + 1.129955E+02 1.349390E+03 -1.265646E+02 6.874452E+02 -2.536891E+03 -1.420844E+03 -4.763963E+02 1.749607E+03 2.298846E+03 -3.412750E+03 + 9.564160E+03 -2.693326E+04 1.619603E+04 -1.511137E+04 -5.596704E+04 5.109354E+04 -8.510018E+04 -1.600369E+05 1.895894E+05 5.857458E+05 + -1.254239E+05 -3.367645E+06 -3.093062E+06 2.694424E+06 7.160923E+06 -7.618526E+06 -2.090016E+06 4.090952E+05 -6.974848E+06 -2.985958E+07 + -3.416202E+06 2.734358E+07 6.704140E+06 5.772150E+06 4.582225E+08 -4.566198E+08 2.470321E+09 -1.988684E+09 8.321251E+08 8.476476E+07 + -6.335209E+13 + 140 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.147520E+03 -2.190839E+03 -9.945472E+02 2.866393E+03 -2.700588E+03 1.529808E+03 -8.122112E+02 7.242430E+02 -3.437821E+01 + -1.534597E+02 -4.147986E+01 1.295177E+02 -1.917802E+02 2.674596E+02 -9.692214E+01 -2.688101E+02 4.127839E+02 -4.315714E+02 1.827916E+02 + 1.134212E+02 3.006346E+02 -1.670741E+02 1.734665E+02 -1.161565E+02 1.670672E+02 1.103600E+02 -1.488886E+02 -1.357866E+02 -4.887326E+02 + 2.121830E+02 -5.906897E+02 2.726742E+02 1.468214E+03 -2.934116E+03 4.522866E+03 -3.638916E+03 1.895446E+03 7.627224E+03 4.983774E+03 + -1.446382E+04 1.479337E+04 -2.009370E+04 -3.799967E+04 3.175520E+04 -3.358973E+04 -4.594036E+04 3.017818E+05 1.909990E+05 -2.701172E+05 + -1.177409E+06 9.736775E+05 1.211333E+06 -2.203820E+06 9.867461E+05 1.702957E+07 4.144784E+05 1.110471E+07 7.185640E+06 -2.570585E+05 + 2.392586E+07 -1.270967E+06 -7.875944E+05 -1.723956E+07 -1.205250E+07 -2.815516E+07 4.186357E+07 -1.696783E+08 8.476476E+07 1.608698E+08 + 3.221563E+13 + 141 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -5.469084E+09 -2.918500E+09 1.244447E+10 -1.013585E+10 3.900138E+09 1.783393E+09 -7.861651E+08 3.121085E+09 -2.748881E+09 + 2.715342E+09 -2.439106E+09 2.653981E+09 -9.326944E+08 3.155416E+08 -1.168488E+09 2.537083E+08 1.043230E+08 5.991911E+07 -1.280396E+09 + 3.443897E+09 -3.572911E+09 3.313321E+09 -2.949793E+09 2.072976E+09 -1.385513E+09 2.249460E+09 2.090568E+08 -3.539363E+09 1.405264E+09 + -5.363410E+09 7.116247E+09 -5.231428E+09 2.156686E+08 6.919028E+09 -8.980145E+09 4.997677E+08 -3.910471E+09 8.109293E+09 -5.927140E+09 + 1.115186E+10 -2.866100E+10 3.284684E+10 1.351102E+10 -7.593121E+10 -2.489680E+11 2.586144E+10 6.573551E+11 -1.944804E+11 1.067786E+12 + -5.598214E+12 8.752808E+12 -1.266012E+13 7.315382E+12 -2.629126E+12 1.488450E+13 1.748647E+13 2.914020E+12 -1.585592E+12 -7.366624E+12 + -1.402988E+13 -4.813886E+13 3.886404E+12 -2.352161E+12 -5.973120E+13 1.692247E+14 -5.659074E+14 7.530490E+13 -6.335209E+13 3.221563E+13 + 3.920956E+21 + 1 3 + 5.532743E-01 5.496238E-01 5.442061E-01 5.371480E-01 5.286313E-01 5.190364E-01 5.086426E-01 4.976000E-01 4.862017E-01 4.747900E-01 + 4.634776E-01 4.524378E-01 4.418782E-01 4.311438E-01 4.208273E-01 4.110179E-01 4.011246E-01 3.912407E-01 3.809590E-01 3.697984E-01 + 3.579456E-01 3.456707E-01 3.331822E-01 3.213366E-01 3.107081E-01 3.014562E-01 2.935481E-01 2.866385E-01 2.804440E-01 2.748683E-01 + 2.696178E-01 2.642998E-01 2.587376E-01 2.526364E-01 2.455343E-01 2.374007E-01 2.281643E-01 2.176884E-01 2.073168E-01 1.974120E-01 + 1.887232E-01 1.811412E-01 1.730458E-01 1.641214E-01 1.549906E-01 1.430212E-01 1.291651E-01 1.111960E-01 9.590135E-02 7.993652E-02 + 6.935166E-02 5.838221E-02 5.014290E-02 4.273354E-02 3.683388E-02 3.076494E-02 2.592667E-02 2.129266E-02 1.734637E-02 1.364010E-02 + 1.083663E-02 8.427126E-03 6.554303E-03 5.058653E-03 3.938026E-03 3.013711E-03 2.257067E-03 1.574980E-03 8.850367E-04 3.625324E-04 + 1.977611E-04 5.417828E-01 5.594072E-01 5.797360E-01 5.959680E-01 6.166565E-01 6.461293E-01 6.829286E-01 7.354708E-01 8.007666E-01 + 8.630770E-01 9.255425E-01 9.853648E-01 1.027280E+00 1.030047E+00 1.016240E+00 9.872676E-01 9.554858E-01 9.079501E-01 8.571902E-01 + 8.046608E-01 7.469014E-01 6.849880E-01 6.174037E-01 5.555852E-01 4.984610E-01 4.442275E-01 3.898927E-01 3.379541E-01 2.934793E-01 + 2.462444E-01 2.026403E-01 1.629018E-01 1.327774E-01 1.095288E-01 8.746303E-02 6.500555E-02 4.833332E-02 3.754906E-02 2.953278E-02 + 2.321874E-02 1.573978E-02 1.110094E-02 8.094883E-03 4.622299E-03 2.344853E-03 1.185271E-03 5.549695E-04 3.266011E-04 3.624688E-04 + 2.368159E-04 4.200889E-04 3.127628E-04 1.734107E-04 1.619419E-05 4.554084E-06 2.632540E-05 4.179360E-05 4.737325E-05 2.550125E-05 + 2.406767E-06 1.943868E-05 2.864700E-05 3.476267E-05 3.885660E-06 1.596678E-06 5.791978E-06 1.048356E-05 5.959853E-05 1.071112E-04 + 1.858434E-11 + 1 + 1.417234E+05 -1.581772E+05 -3.885282E+04 3.688148E+04 3.496750E+04 -6.659064E+03 -1.905371E+04 5.043926E+03 1.039484E+04 -7.185760E+03 + -2.583754E+03 6.594653E+03 -3.848808E+03 -8.064322E+02 4.141124E+03 -3.876026E+03 2.522405E+02 3.008660E+03 -2.877331E+03 5.119112E+01 + 2.305561E+03 -1.839472E+03 -6.279739E+02 1.891446E+03 -6.429456E+02 -1.098210E+03 1.315626E+03 5.872596E+01 -1.336400E+03 7.930471E+02 + 7.778660E+02 -1.150871E+03 1.879501E+01 9.251045E+02 -6.500814E+02 -2.657034E+02 9.437816E+02 -1.163197E+03 8.550940E+02 -1.219040E+02 + -4.338625E+02 6.258410E+02 -3.807068E+02 -2.836523E+02 6.365104E+02 -3.614988E+02 -3.050381E+01 8.281475E+01 -2.107439E+01 6.078701E+01 + 6.512950E+01 -1.524172E+02 4.633619E+01 1.315763E+02 -1.720690E+02 2.624288E+01 1.050305E+02 -1.273568E+02 6.751375E+01 1.208509E+02 + 1.255321E+02 -1.139148E+02 -6.046219E+02 -3.171253E+02 4.422850E+02 1.040936E+03 1.025034E+03 5.603182E+02 2.086078E+02 7.118285E+01 + 4.600067E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 2 + -1.581772E+05 2.742038E+05 -9.185213E+04 -4.145923E+04 2.058438E+03 1.765792E+04 4.607126E+03 -8.682559E+03 -1.795096E+03 5.784649E+03 + -1.513909E+03 -2.951944E+03 3.383896E+03 -8.492285E+02 -1.889965E+03 2.484674E+03 -7.290700E+02 -1.376160E+03 1.743984E+03 -2.727638E+02 + -1.216738E+03 1.133762E+03 2.945612E+02 -1.161273E+03 5.129970E+02 5.656644E+02 -8.976320E+02 9.838124E+01 8.753292E+02 -6.389112E+02 + -4.696769E+02 8.730580E+02 -1.515090E+02 -6.760558E+02 7.043549E+02 8.524721E+01 -8.096245E+02 9.072380E+02 -5.465610E+02 3.162463E+01 + 2.253918E+02 -2.645981E+02 1.845762E+02 1.199733E+02 -3.409830E+02 2.342173E+02 -9.350552E+00 -5.087153E+01 2.818114E+00 6.578665E+01 + -1.249088E+02 5.044409E+01 5.149495E+01 -1.185550E+02 2.937864E+01 4.391678E+01 2.498981E+01 8.108792E+01 -5.142220E+01 -1.167249E+02 + -2.975149E+01 8.307601E+01 6.489440E+01 -2.024266E+02 -3.572716E+02 -3.748140E+02 -3.705984E+02 -2.542394E+02 -9.523572E+01 -1.327104E+01 + 6.997044E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 3 + -3.885282E+04 -9.185213E+04 2.727014E+05 -1.238966E+05 -5.230103E+04 2.051129E+04 2.773579E+04 -9.406619E+03 -1.335317E+04 1.027498E+04 + 2.835499E+03 -8.565485E+03 5.235384E+03 9.100420E+02 -5.247301E+03 4.869390E+03 -2.726972E+02 -3.828202E+03 3.534164E+03 7.450426E+01 + -2.900852E+03 2.274083E+03 8.358390E+02 -2.532868E+03 8.997520E+02 1.553113E+03 -1.806486E+03 -2.053427E+02 1.865620E+03 -1.001472E+03 + -1.082635E+03 1.584023E+03 -1.046430E+02 -1.332119E+03 1.079145E+03 3.161982E+02 -1.432404E+03 1.716443E+03 -1.108046E+03 7.666964E+01 + 5.119014E+02 -5.874774E+02 3.077005E+02 2.980104E+02 -6.279240E+02 3.741396E+02 -2.164886E+01 3.822232E+01 -1.462687E+02 -2.075370E+01 + 8.489945E+01 4.830997E+01 -1.100484E+02 3.672581E+01 2.144788E+02 -2.200564E+02 -8.918126E+01 2.239908E+02 -1.372288E+02 -1.245096E+02 + -4.751011E+01 1.680115E+02 6.826345E+02 3.963278E+02 -2.548369E+02 -7.945048E+02 -9.330080E+02 -5.703648E+02 -1.971443E+02 -4.543034E+01 + -2.000399E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 4 + 3.688148E+04 -4.145923E+04 -1.238966E+05 2.462258E+05 -1.170483E+05 -2.855284E+04 2.574040E+04 1.277292E+04 -1.208633E+04 -2.737170E+03 + 7.295746E+03 -2.934912E+03 -2.046772E+03 3.315631E+03 -1.762163E+03 -3.859877E+02 1.642444E+03 -1.265148E+03 -5.647052E+01 8.931976E+02 + -6.967330E+02 -1.199683E+02 5.413214E+02 -2.281378E+02 -3.329953E+02 5.207018E+02 4.323089E+01 -5.321796E+02 2.299164E+02 3.291731E+02 + -3.424584E+02 -1.324620E+02 3.994866E+02 -2.838066E+01 -4.435714E+02 2.388226E+02 2.273287E+02 -2.497321E+02 -3.284494E+01 1.322077E+02 + 1.278618E+02 -2.920570E+02 3.114080E+01 2.339680E+02 -1.268802E+02 -1.349768E+02 2.306038E+02 -1.821862E+02 1.281914E+02 -6.590884E+01 + -9.532447E+01 1.323748E+02 6.676079E+01 -1.405462E+02 4.222447E+01 8.917918E+01 -1.038615E+02 -1.224124E+02 -2.826373E+01 1.288404E+02 + -1.003157E+02 -5.799334E+01 4.771482E+02 4.123056E+02 -9.805140E+01 -5.257363E+02 -5.289808E+02 -2.273130E+02 -4.179778E+01 -1.220570E+01 + -2.112756E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 5 + 3.496750E+04 2.058438E+03 -5.230103E+04 -1.170483E+05 2.523056E+05 -1.209244E+05 -2.967638E+04 3.200807E+04 1.173368E+04 -1.844336E+04 + 1.150985E+03 1.009619E+04 -8.207327E+03 4.207015E+02 5.638241E+03 -5.875675E+03 1.007504E+03 3.810456E+03 -3.995288E+03 2.288938E+02 + 2.957664E+03 -2.499290E+03 -7.674328E+02 2.827265E+03 -1.255354E+03 -1.646058E+03 2.204436E+03 1.080372E+02 -2.108369E+03 1.219637E+03 + 1.109014E+03 -1.871236E+03 3.516757E+02 1.561462E+03 -1.602758E+03 -2.033836E+02 1.841150E+03 -2.194411E+03 1.313390E+03 6.927996E+01 + -8.084880E+02 6.800339E+02 -1.531544E+02 -3.787080E+02 5.757419E+02 -3.659725E+02 9.345707E+01 -1.441192E+02 2.854760E+02 -1.314494E+02 + -5.936190E+01 4.923298E+01 2.627276E+01 -5.094683E+01 -1.523777E+02 1.449582E+02 -1.061647E+01 -1.379221E+02 1.591127E+02 6.018667E+01 + -2.422128E+00 -3.132668E+01 -4.095576E+02 -3.550960E+02 -4.207831E+01 2.906080E+02 5.620588E+02 4.859189E+02 2.151629E+02 4.504830E+01 + -7.243561E-01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 6 + -6.659064E+03 1.765792E+04 2.051129E+04 -2.855284E+04 -1.209244E+05 2.296638E+05 -1.249884E+05 -1.185204E+04 3.375203E+04 -1.901161E+03 + -1.392289E+04 8.675041E+03 1.179144E+03 -5.805420E+03 4.774639E+03 -9.391912E+02 -2.724104E+03 3.374021E+03 -8.688214E+02 -1.771733E+03 + 2.086168E+03 -3.252144E+02 -1.370844E+03 1.329670E+03 2.250044E+02 -1.453020E+03 7.257820E+02 9.233711E+02 -1.259340E+03 -2.973049E+01 + 1.068905E+03 -6.340559E+02 -4.677737E+02 8.136247E+02 -1.907888E+02 -4.630183E+02 6.558950E+02 -6.110434E+02 4.398298E+02 -1.566940E+02 + -1.456661E+02 2.667828E+02 -1.038145E+01 -3.261236E+02 2.078782E+02 1.804972E+02 -3.339887E+02 2.553146E+02 -1.282097E+02 -3.866264E+01 + 2.162255E+02 -1.470695E+02 -1.355495E+02 2.398891E+02 -9.457278E+01 -1.346923E+01 1.011262E+02 -2.882872E+00 1.184333E+02 -1.212137E+02 + 8.078906E+00 -2.949902E+01 -5.347727E+02 -1.111335E+02 4.414913E+02 6.600721E+02 6.822190E+02 3.887724E+02 9.433363E+01 1.283306E+00 + -2.746842E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 7 + -1.905371E+04 4.607126E+03 2.773579E+04 2.574040E+04 -2.967638E+04 -1.249884E+05 2.231858E+05 -1.269532E+05 -4.812798E+03 3.738968E+04 + -8.662264E+03 -1.347198E+04 1.317378E+04 -2.576879E+03 -6.301847E+03 7.681846E+03 -2.273497E+03 -3.762994E+03 4.748402E+03 -8.912190E+02 + -2.784836E+03 2.724448E+03 4.088257E+02 -2.775161E+03 1.571185E+03 1.430761E+03 -2.278668E+03 1.431888E+02 1.814423E+03 -1.228740E+03 + -6.818915E+02 1.660769E+03 -7.185587E+02 -1.277641E+03 1.833896E+03 -1.110626E+02 -1.824034E+03 2.396672E+03 -1.430548E+03 -1.947893E+02 + 1.023250E+03 -7.441624E+02 4.951724E+01 4.487641E+02 -5.202142E+02 3.094330E+02 -1.524226E+02 2.857364E+02 -4.274642E+02 2.902402E+02 + -2.097982E+01 -1.868252E+02 1.646296E+02 -3.036438E+01 -9.914308E+00 1.196436E+02 -3.773353E+01 -4.569833E+01 5.284454E+01 1.383697E+01 + 4.135861E+01 1.334795E+01 1.238452E+02 3.993452E+02 3.096013E+02 -1.042460E+02 -4.072723E+02 -4.449263E+02 -2.782409E+02 -9.532661E+01 + -2.472002E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 8 + 5.043926E+03 -8.682559E+03 -9.406619E+03 1.277292E+04 3.200807E+04 -1.185204E+04 -1.269532E+05 2.054525E+05 -1.240310E+05 9.705299E+03 + 3.042968E+04 -1.592818E+04 -4.243159E+03 1.089326E+04 -6.604354E+03 -6.245536E+02 4.998210E+03 -4.058282E+03 -2.564820E+02 3.073069E+03 + -2.020153E+03 -7.916651E+02 2.026039E+03 -8.734715E+02 -9.040088E+02 1.441872E+03 -3.516092E+02 -1.079067E+03 1.155011E+03 2.517739E+02 + -1.216438E+03 4.717501E+02 8.285906E+02 -8.836516E+02 -1.123704E+02 7.006448E+02 -5.142342E+02 9.093400E+01 1.278301E+02 -3.477750E+01 + -5.301066E+01 -1.068671E+01 1.629442E+01 7.113684E+01 -3.727832E+01 -6.540606E+01 9.017075E+01 -1.271165E+02 1.309528E+02 -4.210837E+01 + -1.189472E+02 1.920485E+02 -1.718045E+01 -1.949491E+02 1.962769E+02 -1.389173E+02 -7.962121E+01 1.983125E+02 -2.182046E+02 -4.884404E+01 + 7.683516E+01 -6.144107E+00 2.778913E+02 6.206538E+01 -2.162924E+02 -3.005172E+02 -3.643278E+02 -2.881476E+02 -1.337534E+02 -3.790499E+01 + -1.413478E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 9 + 1.039484E+04 -1.795096E+03 -1.335317E+04 -1.208633E+04 1.173368E+04 3.375203E+04 -4.812798E+03 -1.240310E+05 1.970822E+05 -1.288693E+05 + 1.947222E+04 2.988636E+04 -2.290572E+04 1.306088E+03 1.149622E+04 -1.082567E+04 1.659277E+03 6.121170E+03 -6.043652E+03 2.477695E+02 + 3.886811E+03 -2.725037E+03 -1.094815E+03 2.991646E+03 -1.205207E+03 -1.769927E+03 2.212704E+03 1.484824E+02 -1.944857E+03 9.991147E+02 + 9.528917E+02 -1.597110E+03 4.085810E+02 1.383220E+03 -1.581358E+03 -1.231628E+02 1.776413E+03 -2.117807E+03 1.164175E+03 1.985275E+02 + -8.689108E+02 5.867731E+02 -1.416803E+00 -3.257554E+02 3.668062E+02 -3.163837E+02 3.472187E+02 -5.320256E+02 5.947006E+02 -3.459038E+02 + 2.737925E+01 2.591208E+02 -3.633703E+02 1.322027E+02 1.571584E+02 -3.025657E+02 1.117842E+02 1.432350E+02 -1.029288E+02 1.454087E+02 + -2.116276E+00 -1.660026E+02 -1.000743E+02 -5.211295E+02 -5.660617E+02 8.527106E+01 5.579945E+02 5.332985E+02 2.828780E+02 8.125026E+01 + 1.281310E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 10 + -7.185760E+03 5.784649E+03 1.027498E+04 -2.737170E+03 -1.844336E+04 -1.901161E+03 3.738968E+04 9.705299E+03 -1.288693E+05 1.839169E+05 + -1.209467E+05 2.639486E+04 2.137915E+04 -2.191675E+04 5.058235E+03 7.764091E+03 -9.146737E+03 2.337473E+03 4.366536E+03 -4.557878E+03 + -7.087094E+01 3.217330E+03 -2.001812E+03 -1.029229E+03 2.160797E+03 -6.604574E+02 -1.233931E+03 1.269361E+03 1.945780E+02 -1.170608E+03 + 6.561796E+02 7.567054E+02 -1.260174E+03 1.027349E+01 1.163319E+03 -6.837535E+02 -5.641094E+02 1.376575E+03 -1.239822E+03 3.510718E+02 + 3.083200E+02 -2.296792E+02 -3.253127E+01 9.120149E+01 -1.128961E+02 1.564808E+02 -1.374862E+02 2.009149E+02 -2.369293E+02 4.779230E+01 + 1.495992E+02 -2.158528E+02 7.836536E+01 2.253080E+02 -3.100978E+02 1.506881E+02 1.949696E+02 -4.062617E+02 1.163467E+02 1.999871E+02 + -1.919270E+02 2.218282E+00 2.384568E+02 1.669667E+02 -1.992035E+01 2.413273E+01 1.567646E+02 1.753754E+02 1.051519E+02 3.613132E+01 + 1.334960E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 11 + -2.583754E+03 -1.513909E+03 2.835499E+03 7.295746E+03 1.150985E+03 -1.392289E+04 -8.662264E+03 3.042968E+04 1.947222E+04 -1.209467E+05 + 1.691534E+05 -1.214398E+05 3.857138E+04 1.395803E+04 -2.443003E+04 1.164443E+04 4.638604E+03 -1.077283E+04 5.138650E+03 3.146510E+03 + -5.119904E+03 1.009278E+03 2.937370E+03 -2.647482E+03 -4.769225E+02 2.488762E+03 -1.310093E+03 -1.231494E+03 1.928603E+03 -1.247833E+02 + -1.634598E+03 1.193964E+03 5.907172E+02 -1.514305E+03 7.175174E+02 7.872310E+02 -1.569086E+03 1.151749E+03 -1.705278E+02 -4.378310E+02 + 4.765972E+02 -1.710830E+02 -2.190414E+02 2.545752E+02 7.377731E+00 -1.368391E+02 -8.896103E+01 4.165817E+02 -4.691215E+02 3.815617E+02 + -2.043982E+02 -1.328629E+02 4.158982E+02 -3.304381E+02 8.692207E+01 2.038387E+02 -3.094472E+02 2.173451E+02 5.979967E+01 -4.485746E+02 + 1.574790E+02 2.801106E+02 -2.812026E+02 2.032528E+02 6.002018E+02 -2.266168E+00 -6.024504E+02 -5.642477E+02 -2.471905E+02 -4.735488E+01 + 6.661508E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 12 + 6.594653E+03 -2.951944E+03 -8.565485E+03 -2.934912E+03 1.009619E+04 8.675041E+03 -1.347198E+04 -1.592818E+04 2.988636E+04 2.639486E+04 + -1.214398E+05 1.656755E+05 -1.215218E+05 4.157251E+04 1.220696E+04 -2.544749E+04 1.172083E+04 6.063728E+03 -1.151413E+04 3.751255E+03 + 5.159002E+03 -5.647883E+03 -1.297756E+02 4.107662E+03 -2.592017E+03 -1.469039E+03 3.072822E+03 -5.926352E+02 -2.332554E+03 1.863426E+03 + 8.290546E+02 -2.319216E+03 1.013601E+03 1.524400E+03 -2.108113E+03 9.868651E+01 2.059735E+03 -2.656414E+03 1.736831E+03 -2.339692E+02 + -6.611620E+02 4.404796E+02 2.329380E+02 -3.643351E+02 3.120317E+01 2.081942E+02 -1.479143E+02 -1.007879E+02 2.008859E+02 -1.688059E+02 + 1.178271E+02 4.845169E+01 -1.939121E+02 1.373778E+00 9.188912E+01 -1.375367E+02 1.390224E+01 1.250420E+02 -9.753264E+01 1.727972E+02 + 1.365598E+02 -1.629698E+02 -1.424058E+02 -8.570549E+01 -3.315805E+01 1.195048E+01 1.237777E+02 1.526159E+02 9.077392E+01 3.374513E+01 + 1.486217E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 13 + -3.848808E+03 3.383896E+03 5.235384E+03 -2.046772E+03 -8.207327E+03 1.179144E+03 1.317378E+04 -4.243159E+03 -2.290572E+04 2.137915E+04 + 3.857138E+04 -1.215218E+05 1.528381E+05 -1.117432E+05 4.334206E+04 7.204760E+03 -2.228924E+04 1.101201E+04 5.209279E+03 -9.175507E+03 + 1.591702E+03 5.176465E+03 -3.959108E+03 -1.396064E+03 3.838162E+03 -1.403072E+03 -2.129993E+03 2.323268E+03 3.582685E+02 -2.072936E+03 + 1.043447E+03 1.218605E+03 -1.763143E+03 -3.358444E+01 1.494416E+03 -9.729632E+02 -3.702913E+02 1.396182E+03 -1.590992E+03 7.818932E+02 + 1.856729E+02 -4.335510E+02 9.887174E+01 2.031984E+02 -2.520313E+02 1.118106E+02 1.591324E+02 -3.493441E+02 3.282396E+02 -2.386895E+02 + 7.806007E+01 1.100375E+02 -1.977577E+02 2.354358E+02 -1.697082E+02 4.172959E+01 1.845626E+02 -3.002190E+02 1.202797E+02 1.347008E+02 + -4.113763E+02 3.127880E+01 5.678258E+02 -1.811425E+02 -7.186612E+02 -2.390827E+02 2.918796E+02 3.045649E+02 1.002343E+02 2.083373E+00 + -1.476947E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 14 + -8.064322E+02 -8.492285E+02 9.100420E+02 3.315631E+03 4.207015E+02 -5.805420E+03 -2.576879E+03 1.089326E+04 1.306088E+03 -2.191675E+04 + 1.395803E+04 4.157251E+04 -1.117432E+05 1.433638E+05 -1.147983E+05 4.922940E+04 6.636235E+03 -2.475764E+04 1.155911E+04 6.519026E+03 + -9.753601E+03 1.128080E+03 5.462263E+03 -3.689578E+03 -1.545452E+03 3.571780E+03 -1.087457E+03 -2.135341E+03 2.275638E+03 3.144860E+02 + -2.120042E+03 1.169988E+03 8.322859E+02 -1.527847E+03 6.254940E+02 7.759091E+02 -1.569923E+03 1.203878E+03 -7.633750E+01 -6.061674E+02 + 3.904530E+02 8.966950E+01 -2.281030E+02 -6.310759E+01 4.220195E+02 -4.957324E+02 1.700527E+02 2.436952E+02 -2.976030E+02 2.223534E+02 + -1.544599E+02 -6.454501E+01 2.143444E+02 -8.286844E+01 6.632370E+01 -1.771794E+01 -1.388292E+02 1.368998E+02 -4.797707E+01 -1.537516E+02 + 2.832892E+02 -1.355990E+02 -5.180126E+02 2.618728E+02 7.904509E+02 4.270016E+02 -2.480556E+02 -4.277005E+02 -2.282020E+02 -6.198566E+01 + -1.848655E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 15 + 4.141124E+03 -1.889965E+03 -5.247301E+03 -1.762163E+03 5.638241E+03 4.774639E+03 -6.301847E+03 -6.604354E+03 1.149622E+04 5.058235E+03 + -2.443003E+04 1.220696E+04 4.334206E+04 -1.147983E+05 1.510290E+05 -1.184700E+05 4.453828E+04 1.325351E+04 -2.559383E+04 6.054935E+03 + 1.101210E+04 -9.224906E+03 -1.828489E+03 7.066003E+03 -3.261666E+03 -2.942543E+03 4.372544E+03 -3.263114E+02 -3.425046E+03 2.344980E+03 + 1.403261E+03 -2.994192E+03 9.844732E+02 1.952141E+03 -2.443630E+03 1.796764E+02 2.272460E+03 -3.025564E+03 1.875496E+03 -8.366503E+01 + -7.413062E+02 4.402670E+02 1.090841E+02 -2.379188E+02 5.113026E+01 1.120223E+02 -1.113591E+02 3.116540E+01 -9.007115E+01 1.556897E+02 + -4.337081E+01 -3.354617E+01 8.996999E+01 -2.532265E+02 7.781860E+01 6.560060E+01 -7.707185E+01 1.100443E+02 -7.631053E+00 6.357625E+01 + 1.376509E+02 6.048644E+01 -8.402381E+01 -1.426658E+02 -1.964276E+02 -3.236405E+02 -2.817436E+02 -9.745506E+01 3.145933E+01 2.535133E+01 + 7.783157E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 16 + -3.876026E+03 2.484674E+03 4.869390E+03 -3.859877E+02 -5.875675E+03 -9.391912E+02 7.681846E+03 -6.245536E+02 -1.082567E+04 7.764091E+03 + 1.164443E+04 -2.544749E+04 7.204760E+03 4.922940E+04 -1.184700E+05 1.488955E+05 -1.130016E+05 3.940865E+04 1.586341E+04 -2.219216E+04 + 1.176989E+03 1.170810E+04 -6.307070E+03 -4.242817E+03 6.889075E+03 -1.074116E+03 -4.543766E+03 3.395906E+03 1.671385E+03 -3.590705E+03 + 6.908177E+02 2.543207E+03 -2.104556E+03 -8.356122E+02 2.316353E+03 -9.507107E+02 -1.299228E+03 2.609657E+03 -2.242372E+03 6.146650E+02 + 6.760858E+02 -7.438892E+02 -6.526562E+00 6.550222E+02 -7.815762E+02 5.849344E+02 -2.492827E+02 -6.589346E+01 2.013234E+02 -2.589882E+02 + 1.529237E+02 6.576671E+01 -2.609988E+02 3.361765E+02 -7.895564E+01 -1.754374E+02 2.780902E+02 -6.623413E+01 -1.481903E+02 -1.726430E+02 + -2.797307E+02 3.522886E+02 4.984289E+02 -1.608692E+02 -2.645831E+02 1.634315E+02 5.365825E+02 4.790128E+02 1.890139E+02 2.104946E+01 + -1.177685E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 17 + 2.522405E+02 -7.290700E+02 -2.726972E+02 1.642444E+03 1.007504E+03 -2.724104E+03 -2.273497E+03 4.998210E+03 1.659277E+03 -9.146737E+03 + 4.638604E+03 1.172083E+04 -2.228924E+04 6.636235E+03 4.453828E+04 -1.130016E+05 1.518086E+05 -1.197010E+05 3.789233E+04 2.071801E+04 + -2.310678E+04 -5.637871E+02 1.254306E+04 -5.789449E+03 -4.589338E+03 6.140080E+03 -2.234804E+02 -4.179618E+03 2.518303E+03 1.573697E+03 + -2.766485E+03 5.646268E+02 1.494815E+03 -1.412213E+03 8.800696E+01 1.054519E+03 -1.206574E+03 3.940637E+02 4.817155E+02 -5.866998E+02 + 1.010093E+02 3.037585E+02 -1.535864E+02 -3.169739E+02 5.872912E+02 -5.672819E+02 3.321359E+02 -7.359284E+01 -1.892628E+01 1.295394E+02 + -1.534760E+02 -2.035969E-01 1.377890E+02 -1.655095E+02 1.567226E+01 1.833361E+02 -2.439622E+02 -9.413030E+01 3.064843E+02 1.074477E+02 + 3.426671E+01 -2.400366E+02 -3.796848E+02 1.039545E+02 3.667106E+02 2.179620E+02 -1.824204E+01 -1.300439E+02 -1.145095E+02 -3.409304E+01 + 8.145528E-01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 18 + 3.008660E+03 -1.376160E+03 -3.828202E+03 -1.265148E+03 3.810456E+03 3.374021E+03 -3.762994E+03 -4.058282E+03 6.121170E+03 2.337473E+03 + -1.077283E+04 6.063728E+03 1.101201E+04 -2.475764E+04 1.325351E+04 3.940865E+04 -1.197010E+05 1.659983E+05 -1.231788E+05 3.006097E+04 + 2.703644E+04 -2.255983E+04 -4.232096E+03 1.446005E+04 -5.288107E+03 -6.105487E+03 7.047925E+03 2.815588E+02 -5.419981E+03 2.989883E+03 + 2.461763E+03 -4.079808E+03 8.789467E+02 2.893421E+03 -3.097968E+03 -1.819384E+02 3.261198E+03 -3.732014E+03 2.113418E+03 -9.047820E+01 + -8.644681E+02 5.065388E+02 2.841865E+02 -6.129974E+02 4.417885E+02 -1.887721E+02 1.024044E+01 1.159297E+02 -1.470773E+02 5.742206E+01 + 5.971274E+01 -7.734906E+01 1.193576E+02 -1.139547E+02 1.065110E+01 6.138850E+01 5.504605E+00 2.418462E+01 -1.364110E+02 1.726632E+02 + 2.120939E+02 -2.700006E+02 -6.483469E+01 2.736947E+02 -1.050394E+02 -4.001058E+02 -3.442700E+02 -2.035649E+02 -4.371532E+01 2.188033E+01 + 2.545060E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 19 + -2.877331E+03 1.743984E+03 3.534164E+03 -5.647052E+01 -3.995288E+03 -8.688214E+02 4.748402E+03 -2.564820E+02 -6.043652E+03 4.366536E+03 + 5.138650E+03 -1.151413E+04 5.209279E+03 1.155911E+04 -2.559383E+04 1.586341E+04 3.789233E+04 -1.231788E+05 1.686548E+05 -1.207099E+05 + 2.648366E+04 2.767507E+04 -2.047987E+04 -5.538467E+03 1.399292E+04 -3.461401E+03 -7.422529E+03 6.189074E+03 2.070284E+03 -5.587206E+03 + 1.447397E+03 3.540090E+03 -3.127579E+03 -1.086194E+03 3.245444E+03 -1.279141E+03 -1.862261E+03 3.429214E+03 -2.859166E+03 9.389635E+02 + 6.243491E+02 -8.482878E+02 2.986525E+01 7.866576E+02 -1.024608E+03 9.131597E+02 -5.111849E+02 4.532442E+01 1.450862E+02 -2.009195E+02 + 1.075204E+02 6.269664E+01 -2.344934E+02 2.467014E+02 4.006631E+01 -2.634568E+02 1.022055E+02 1.262806E+02 -1.429072E+02 -1.712224E+02 + -1.647676E+02 2.372098E+02 3.556774E+02 -1.557683E+02 -2.219900E+02 7.675894E+01 1.995312E+02 1.431792E+02 5.660820E+01 -2.326009E+00 + -1.892692E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 20 + 5.119112E+01 -2.727638E+02 7.450426E+01 8.931976E+02 2.288938E+02 -1.771733E+03 -8.912190E+02 3.073069E+03 2.477695E+02 -4.557878E+03 + 3.146510E+03 3.751255E+03 -9.175507E+03 6.519026E+03 6.054935E+03 -2.219216E+04 2.071801E+04 3.006097E+04 -1.207099E+05 1.725086E+05 + -1.252117E+05 2.721654E+04 2.883954E+04 -2.182878E+04 -4.934228E+03 1.399157E+04 -3.760843E+03 -6.865946E+03 5.999797E+03 1.434683E+03 + -5.056153E+03 2.080277E+03 2.181335E+03 -2.958584E+03 7.506268E+02 1.683802E+03 -2.440975E+03 1.282811E+03 3.759904E+02 -9.796758E+02 + 4.533271E+02 3.160636E+02 -5.356492E+02 1.769453E+02 3.217795E+02 -5.931373E+02 4.246970E+02 -1.139073E+02 -2.897453E+01 8.997091E+01 + -3.918949E+01 -5.952266E+01 6.290999E+01 -1.618750E+01 -1.281324E+02 1.436785E+02 1.913660E+01 -1.089092E+02 1.260499E+02 -6.246890E+01 + -2.650021E+01 1.683354E+02 -2.183136E+02 -3.668714E+02 1.184530E+02 2.223161E+02 3.347678E+00 -2.748602E+01 -3.281752E+01 -2.763047E+01 + -1.590250E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 21 + 2.305561E+03 -1.216738E+03 -2.900852E+03 -6.967330E+02 2.957664E+03 2.086168E+03 -2.784836E+03 -2.020153E+03 3.886811E+03 -7.087094E+01 + -5.119904E+03 5.159002E+03 1.591702E+03 -9.753601E+03 1.101210E+04 1.176989E+03 -2.310678E+04 2.703644E+04 2.648366E+04 -1.252117E+05 + 1.791388E+05 -1.257292E+05 2.464979E+04 2.989112E+04 -2.138693E+04 -6.410972E+03 1.500125E+04 -3.179833E+03 -8.212004E+03 6.414863E+03 + 2.430416E+03 -6.262938E+03 1.963549E+03 3.819874E+03 -4.436396E+03 2.785687E+01 4.274118E+03 -4.996938E+03 2.831365E+03 -2.389532E+02 + -8.996582E+02 4.989457E+02 4.511531E+02 -9.348755E+02 7.359636E+02 -4.323211E+02 2.631697E+02 -1.252402E+02 5.817809E+01 1.221088E+01 + -7.448530E+01 5.292308E+01 6.285396E+01 -9.959968E+01 4.914108E+01 7.642802E+01 -5.427011E+01 -1.068848E+02 1.156287E+02 2.727767E+02 + -2.858951E+01 -3.500119E+02 5.533027E+01 4.446241E+02 1.622668E+02 -9.578834E+01 2.407686E+01 1.550378E+02 1.387721E+02 7.735288E+01 + 5.022724E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 22 + -1.839472E+03 1.133762E+03 2.274083E+03 -1.199683E+02 -2.499290E+03 -3.252144E+02 2.724448E+03 -7.916651E+02 -2.725037E+03 3.217330E+03 + 1.009278E+03 -5.647883E+03 5.176465E+03 1.128080E+03 -9.224906E+03 1.170810E+04 -5.637871E+02 -2.255983E+04 2.767507E+04 2.721654E+04 + -1.257292E+05 1.749822E+05 -1.221154E+05 2.610925E+04 2.763726E+04 -2.037164E+04 -6.045848E+03 1.368143E+04 -2.228194E+03 -7.513924E+03 + 5.121764E+03 2.330184E+03 -4.677284E+03 1.003428E+03 2.652338E+03 -2.418372E+03 -2.481304E+02 2.455182E+03 -2.749807E+03 1.344970E+03 + 2.561209E+02 -8.180570E+02 3.156041E+02 4.012177E+02 -7.330807E+02 7.124674E+02 -3.919350E+02 -2.580685E+00 1.177126E+02 -2.542754E+01 + -1.224211E+02 1.684656E+02 -2.868822E+01 -9.222078E+01 9.060271E+01 -1.503529E+02 3.026425E+01 1.867194E+02 -2.126438E+02 -1.603367E+02 + 4.536799E+01 1.248997E+02 1.955894E+02 -6.416386E+01 -4.406855E+02 -2.444910E+02 7.316707E+01 4.244542E+01 5.360765E+00 9.148778E+00 + 6.744368E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 23 + -6.279739E+02 2.945612E+02 8.358390E+02 5.413214E+02 -7.674328E+02 -1.370844E+03 4.088257E+02 2.026039E+03 -1.094815E+03 -2.001812E+03 + 2.937370E+03 -1.297756E+02 -3.959108E+03 5.462263E+03 -1.828489E+03 -6.307070E+03 1.254306E+04 -4.232096E+03 -2.047987E+04 2.883954E+04 + 2.464979E+04 -1.221154E+05 1.740200E+05 -1.253942E+05 2.783315E+04 2.910491E+04 -2.206756E+04 -6.211720E+03 1.494590E+04 -3.164255E+03 + -7.965614E+03 6.608953E+03 1.543902E+03 -5.909358E+03 3.186042E+03 2.302884E+03 -5.093854E+03 3.813322E+03 -9.136510E+02 -7.747418E+02 + 5.626099E+02 2.656579E+02 -6.988446E+02 5.898862E+02 -1.997356E+02 -2.587668E+00 -1.378175E+02 3.812228E+02 -3.773634E+02 6.233880E+01 + 2.515428E+02 -2.538234E+02 -6.058138E+01 1.758550E+02 -6.948611E+00 -6.256459E+01 1.119603E+01 8.977928E+01 -9.409482E+01 -1.842566E+02 + 2.063326E+02 2.141603E+02 -3.541483E+02 -2.117185E+02 3.193787E+02 2.035056E+02 -2.973397E+02 -4.761886E+02 -3.177570E+02 -1.351133E+02 + -6.926549E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 24 + 1.891446E+03 -1.161273E+03 -2.532868E+03 -2.281378E+02 2.827265E+03 1.329670E+03 -2.775161E+03 -8.734715E+02 2.991646E+03 -1.029229E+03 + -2.647482E+03 4.107662E+03 -1.396064E+03 -3.689578E+03 7.066003E+03 -4.242817E+03 -5.789449E+03 1.446005E+04 -5.538467E+03 -2.182878E+04 + 2.989112E+04 2.610925E+04 -1.253942E+05 1.770052E+05 -1.246754E+05 2.466326E+04 3.037434E+04 -2.011799E+04 -8.640810E+03 1.478464E+04 + -1.147441E+03 -8.953159E+03 5.393030E+03 3.307181E+03 -6.065490E+03 1.508962E+03 4.100340E+03 -5.969686E+03 4.051021E+03 -9.731566E+02 + -6.976712E+02 6.650797E+02 1.304081E+02 -7.119226E+02 7.113506E+02 -5.084884E+02 2.959166E+02 -1.413542E+02 4.682772E+01 4.264214E+01 + -4.163831E+01 -3.655519E+01 1.616017E+02 -1.075166E+02 -9.192346E+01 2.823610E+02 -9.982014E+01 -2.525713E+02 3.493757E+02 2.389867E+02 + -2.105381E+02 -2.338787E+02 -4.875450E+01 2.293429E+02 3.642587E+02 1.356696E+02 4.286305E+01 1.813097E+02 1.581433E+02 5.485277E+01 + 1.878242E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 25 + -6.429456E+02 5.129970E+02 8.997520E+02 -3.329953E+02 -1.255354E+03 2.250044E+02 1.571185E+03 -9.040088E+02 -1.205207E+03 2.160797E+03 + -4.769225E+02 -2.592017E+03 3.838162E+03 -1.545452E+03 -3.261666E+03 6.889075E+03 -4.589338E+03 -5.288107E+03 1.399292E+04 -4.934228E+03 + -2.138693E+04 2.763726E+04 2.783315E+04 -1.246754E+05 1.740871E+05 -1.228438E+05 2.522341E+04 2.936071E+04 -1.981620E+04 -7.419325E+03 + 1.379100E+04 -2.115878E+03 -7.172462E+03 5.469472E+03 1.175211E+03 -4.659480E+03 3.122528E+03 4.526024E+02 -2.492603E+03 1.797742E+03 + -4.822756E+01 -8.309125E+02 5.784049E+02 -2.191224E+01 -2.839631E+02 3.693754E+02 -1.738932E+02 -1.546343E+02 3.050479E+02 -1.716820E+02 + -1.071885E+02 1.876339E+02 -1.421467E+02 8.977946E+01 2.082547E+01 -2.181559E+02 1.305751E+02 -8.428752E+00 -1.132871E+02 1.269820E+02 + -1.809472E+02 -1.261110E+02 4.713156E+02 8.909639E+01 -5.389808E+02 -3.102506E+02 2.605480E+02 4.515022E+02 3.008093E+02 1.175687E+02 + 5.076430E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 26 + -1.098210E+03 5.656644E+02 1.553113E+03 5.207018E+02 -1.646058E+03 -1.453020E+03 1.430761E+03 1.441872E+03 -1.769927E+03 -6.604574E+02 + 2.488762E+03 -1.469039E+03 -1.403072E+03 3.571780E+03 -2.942543E+03 -1.074116E+03 6.140080E+03 -6.105487E+03 -3.461401E+03 1.399157E+04 + -6.410972E+03 -2.037164E+04 2.910491E+04 2.466326E+04 -1.228438E+05 1.767708E+05 -1.264375E+05 2.431870E+04 3.221148E+04 -2.096296E+04 + -8.753928E+03 1.580599E+04 -2.491960E+03 -8.990180E+03 7.416174E+03 1.518193E+03 -7.479484E+03 6.858142E+03 -2.760041E+03 -4.255261E+02 + 9.901926E+02 -2.237246E+02 -5.325908E+02 7.846344E+02 -5.002927E+02 5.468761E+01 1.501544E+02 -6.303402E+01 -9.055108E+01 1.282891E+02 + -2.989674E+01 1.620286E+01 -6.145993E+01 -4.405410E+01 7.015475E+01 -7.935527E+01 6.782659E+00 2.411756E+02 -2.940198E+02 -3.192254E+02 + 3.617195E+02 3.162078E+02 -2.760334E+02 -3.966161E+02 -5.533860E+00 2.400434E+02 -9.523304E+01 -4.367527E+02 -3.487477E+02 -1.320602E+02 + -5.504756E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 27 + 1.315626E+03 -8.976320E+02 -1.806486E+03 4.323089E+01 2.204436E+03 7.257820E+02 -2.278668E+03 -3.516092E+02 2.212704E+03 -1.233931E+03 + -1.310093E+03 3.072822E+03 -2.129993E+03 -1.087457E+03 4.372544E+03 -4.543766E+03 -2.234804E+02 7.047925E+03 -7.422529E+03 -3.760843E+03 + 1.500125E+04 -6.045848E+03 -2.206756E+04 3.037434E+04 2.522341E+04 -1.264375E+05 1.801651E+05 -1.250272E+05 2.181103E+04 3.144432E+04 + -1.874880E+04 -9.101934E+03 1.400158E+04 -1.182796E+03 -8.156807E+03 5.646611E+03 1.930756E+03 -6.344491E+03 5.339755E+03 -1.595543E+03 + -1.091161E+03 1.453436E+03 -3.037558E+02 -8.011390E+02 1.001720E+03 -6.805424E+02 1.946286E+02 1.979563E+02 -2.525425E+02 6.829054E+01 + 1.018318E+02 -1.823177E+02 2.771148E+02 -1.475812E+02 -6.216889E+01 3.634540E+02 -3.044866E+02 -3.939908E+01 4.086029E+02 -1.259398E+02 + -4.029824E+01 8.943008E+01 -2.662135E+02 5.624282E+01 3.888499E+02 7.886932E+01 -2.961149E+02 -3.475588E+02 -2.116319E+02 -7.300715E+01 + -2.254643E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 28 + 5.872596E+01 9.838124E+01 -2.053427E+02 -5.321796E+02 1.080372E+02 9.233711E+02 1.431888E+02 -1.079067E+03 1.484824E+02 1.269361E+03 + -1.231494E+03 -5.926352E+02 2.323268E+03 -2.135341E+03 -3.263114E+02 3.395906E+03 -4.179618E+03 2.815588E+02 6.189074E+03 -6.865946E+03 + -3.179833E+03 1.368143E+04 -6.211720E+03 -2.011799E+04 2.936071E+04 2.431870E+04 -1.250272E+05 1.778074E+05 -1.243376E+05 2.419870E+04 + 3.025456E+04 -2.078008E+04 -6.412504E+03 1.457791E+04 -4.394209E+03 -6.465118E+03 8.222465E+03 -3.600880E+03 -8.051575E+02 1.717862E+03 + -3.096851E+02 -8.789676E+02 8.853619E+02 -2.754635E+02 -2.793187E+02 6.335800E+02 -5.044408E+02 6.513114E+01 1.561468E+02 -1.400340E+02 + 4.733554E+01 1.362710E+00 -9.344280E+01 1.603693E+02 -8.952426E+01 -1.430238E+02 2.209908E+02 -2.446696E+02 -5.292043E+01 3.676073E+02 + -2.608991E+02 -3.555854E+02 4.606348E+02 5.133022E+02 -3.018802E+02 -5.351095E+02 2.021488E+02 7.064345E+02 5.303494E+02 2.012446E+02 + 8.634233E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 29 + -1.336400E+03 8.753292E+02 1.865620E+03 2.299164E+02 -2.108369E+03 -1.259340E+03 1.814423E+03 1.155011E+03 -1.944857E+03 1.945780E+02 + 1.928603E+03 -2.332554E+03 3.582685E+02 2.275638E+03 -3.425046E+03 1.671385E+03 2.518303E+03 -5.419981E+03 2.070284E+03 5.999797E+03 + -8.212004E+03 -2.228194E+03 1.494590E+04 -8.640810E+03 -1.981620E+04 3.221148E+04 2.181103E+04 -1.243376E+05 1.795181E+05 -1.266242E+05 + 2.341014E+04 3.236447E+04 -2.126666E+04 -8.227739E+03 1.640716E+04 -3.974988E+03 -8.973572E+03 1.149746E+04 -6.203215E+03 2.838130E+02 + 1.981036E+03 -1.323528E+03 -2.371694E+02 1.328725E+03 -1.324025E+03 5.434940E+02 1.306082E+02 -3.351000E+02 2.218176E+02 1.077847E+01 + -1.823663E+02 2.711273E+02 -3.266591E+02 1.113619E+02 2.495018E+02 -4.749547E+02 2.970528E+02 2.382203E+02 -5.125066E+02 1.306536E+02 + 2.958702E+02 -1.573505E+02 -1.617822E+02 -1.572622E+02 -4.581101E+01 2.281642E+02 2.970349E+02 1.962469E+02 1.057424E+02 2.804116E+01 + -4.039354E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 30 + 7.930471E+02 -6.389112E+02 -1.001472E+03 3.291731E+02 1.219637E+03 -2.973049E+01 -1.228740E+03 2.517739E+02 9.991147E+02 -1.170608E+03 + -1.247833E+02 1.863426E+03 -2.072936E+03 3.144860E+02 2.344980E+03 -3.590705E+03 1.573697E+03 2.989883E+03 -5.587206E+03 1.434683E+03 + 6.414863E+03 -7.513924E+03 -3.164255E+03 1.478464E+04 -7.419325E+03 -2.096296E+04 3.144432E+04 2.419870E+04 -1.266242E+05 1.792981E+05 + -1.240687E+05 2.383583E+04 2.889397E+04 -1.995574E+04 -5.693078E+03 1.340630E+04 -4.857558E+03 -4.206268E+03 5.644062E+03 -1.851913E+03 + -1.592240E+03 2.295552E+03 -9.721523E+02 -7.359870E+02 1.502311E+03 -1.231664E+03 4.551121E+02 1.996748E+02 -2.780836E+02 6.387412E+01 + 9.662422E+01 -1.693697E+02 2.844791E+02 -2.396665E+02 -8.859455E+01 4.629930E+02 -3.660784E+02 -3.634987E+00 4.235754E+02 -3.215567E+02 + -8.394049E+01 3.796541E+02 -2.307497E+02 -3.995861E+02 4.109423E+02 5.303653E+02 -4.175245E+02 -9.347393E+02 -6.420443E+02 -2.380478E+02 + -1.061969E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 31 + 7.778660E+02 -4.696769E+02 -1.082635E+03 -3.424584E+02 1.109014E+03 1.068905E+03 -6.818915E+02 -1.216438E+03 9.528917E+02 6.561796E+02 + -1.634598E+03 8.290546E+02 1.043447E+03 -2.120042E+03 1.403261E+03 6.908177E+02 -2.766485E+03 2.461763E+03 1.447397E+03 -5.056153E+03 + 2.430416E+03 5.121764E+03 -7.965614E+03 -1.147441E+03 1.379100E+04 -8.753928E+03 -1.874880E+04 3.025456E+04 2.341014E+04 -1.240687E+05 + 1.765052E+05 -1.251104E+05 2.716176E+04 2.933086E+04 -2.395381E+04 -3.188402E+03 1.667564E+04 -1.299152E+04 4.175424E+03 9.281738E+02 + -1.101212E+03 -3.477182E+02 1.118205E+03 -9.435206E+02 3.717782E+02 2.237935E+02 -3.888745E+02 1.305797E+02 5.059832E+01 -9.271128E+01 + 1.118191E+02 -1.163656E+02 1.234894E+02 1.027807E+00 -1.567699E+02 2.554915E+02 -2.074362E+02 -1.848948E+02 3.211337E+02 -1.796554E+02 + -2.191211E+02 2.519575E+02 2.343466E+02 3.298740E+01 -1.869397E+02 -3.437152E+02 -2.462664E+02 -7.459154E+01 -1.948400E+01 1.855328E+00 + 1.307612E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 32 + -1.150871E+03 8.730580E+02 1.584023E+03 -1.324620E+02 -1.871236E+03 -6.340559E+02 1.660769E+03 4.717501E+02 -1.597110E+03 7.567054E+02 + 1.193964E+03 -2.319216E+03 1.218605E+03 1.169988E+03 -2.994192E+03 2.543207E+03 5.646268E+02 -4.079808E+03 3.540090E+03 2.080277E+03 + -6.262938E+03 2.330184E+03 6.608953E+03 -8.953159E+03 -2.115878E+03 1.580599E+04 -9.101934E+03 -2.078008E+04 3.236447E+04 2.383583E+04 + -1.251104E+05 1.766262E+05 -1.245526E+05 2.579090E+04 2.954608E+04 -2.275608E+04 -4.226497E+03 1.685158E+04 -1.169561E+04 1.499653E+03 + 3.505265E+03 -2.892152E+03 2.569480E+02 1.800865E+03 -2.265997E+03 1.432769E+03 -3.436591E+02 -2.419886E+02 2.046876E+02 2.182387E+01 + -1.726829E+02 2.323162E+02 -3.659926E+02 2.817343E+02 1.070186E+02 -5.984374E+02 5.669108E+02 7.275310E+01 -6.110561E+02 4.785082E+02 + 2.239475E+02 -6.310667E+02 2.996263E+00 4.048387E+02 -2.503890E+02 -3.293317E+02 5.265078E+02 9.491244E+02 6.248094E+02 2.274220E+02 + 1.030241E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 33 + 1.879501E+01 -1.515090E+02 -1.046430E+02 3.994866E+02 3.516757E+02 -4.677737E+02 -7.185587E+02 8.285906E+02 4.085810E+02 -1.260174E+03 + 5.907172E+02 1.013601E+03 -1.763143E+03 8.322859E+02 9.844732E+02 -2.104556E+03 1.494815E+03 8.789467E+02 -3.127579E+03 2.181335E+03 + 1.963549E+03 -4.677284E+03 1.543902E+03 5.393030E+03 -7.172462E+03 -2.491960E+03 1.400158E+04 -6.412504E+03 -2.126666E+04 2.889397E+04 + 2.716176E+04 -1.245526E+05 1.741618E+05 -1.236464E+05 2.939528E+04 2.504663E+04 -2.490121E+04 5.643523E+03 4.390300E+03 -2.639182E+03 + -2.028487E+03 3.513937E+03 -1.900050E+03 -5.370592E+02 1.862015E+03 -1.752672E+03 8.390933E+02 7.443269E+01 -3.197418E+02 1.410569E+02 + 6.139300E+01 -1.598197E+02 2.052644E+02 -1.621308E+02 1.146940E+01 1.204034E+02 -9.749921E+01 1.302792E+02 6.801817E+01 -1.090907E+02 + 1.381078E+01 1.371002E+02 -9.889578E+01 -2.373886E+02 5.277095E+01 2.474028E+02 4.169833E+01 -1.447195E+02 -9.706082E+01 -2.194932E+01 + -3.157996E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 34 + 9.251045E+02 -6.760558E+02 -1.332119E+03 -2.838066E+01 1.561462E+03 8.136247E+02 -1.277641E+03 -8.836516E+02 1.383220E+03 1.027349E+01 + -1.514305E+03 1.524400E+03 -3.358444E+01 -1.527847E+03 1.952141E+03 -8.356122E+02 -1.412213E+03 2.893421E+03 -1.086194E+03 -2.958584E+03 + 3.819874E+03 1.003428E+03 -5.909358E+03 3.307181E+03 5.469472E+03 -8.990180E+03 -1.182796E+03 1.457791E+04 -8.227739E+03 -1.995574E+04 + 2.933086E+04 2.579090E+04 -1.236464E+05 1.731974E+05 -1.247981E+05 3.226969E+04 2.617046E+04 -3.221897E+04 1.415737E+04 7.647299E+02 + -3.906313E+03 9.904375E+02 1.805531E+03 -2.355289E+03 1.360362E+03 -2.276754E+02 -2.046744E+02 6.768356E+01 1.275868E+02 -1.762453E+02 + 1.595971E+02 -1.388380E+02 2.434793E+02 -1.871448E+02 -8.628611E+01 5.419591E+02 -5.702024E+02 -1.859770E+02 5.905423E+02 -3.632352E+02 + -2.785080E+02 4.719954E+02 1.066333E+02 -1.979401E+02 1.606217E+02 1.675246E+02 -3.850093E+02 -6.436189E+02 -4.081128E+02 -1.312218E+02 + -5.077046E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 35 + -6.500814E+02 7.043549E+02 1.079145E+03 -4.435714E+02 -1.602758E+03 -1.907888E+02 1.833896E+03 -1.123704E+02 -1.581358E+03 1.163319E+03 + 7.175174E+02 -2.108113E+03 1.494416E+03 6.254940E+02 -2.443630E+03 2.316353E+03 8.800696E+01 -3.097968E+03 3.245444E+03 7.506268E+02 + -4.436396E+03 2.652338E+03 3.186042E+03 -6.065490E+03 1.175211E+03 7.416174E+03 -8.156807E+03 -4.394209E+03 1.640716E+04 -5.693078E+03 + -2.395381E+04 2.954608E+04 2.939528E+04 -1.247981E+05 1.697318E+05 -1.214821E+05 3.397822E+04 1.887275E+04 -2.139632E+04 3.084175E+03 + 8.166514E+03 -7.175100E+03 1.119634E+03 3.457506E+03 -4.229844E+03 2.700437E+03 -9.595856E+02 -1.213822E+02 2.790691E+02 7.698584E+00 + -2.998477E+02 3.750962E+02 -4.097164E+02 2.609582E+02 9.424304E+01 -5.124344E+02 4.908300E+02 8.685883E+01 -5.444784E+02 3.212363E+02 + 3.115690E+02 -4.430814E+02 -5.742868E+01 4.059408E+02 1.707840E+01 -1.703341E+02 2.661572E+02 4.829765E+02 2.612941E+02 5.521580E+01 + 4.404590E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 36 + -2.657034E+02 8.524721E+01 3.161982E+02 2.388226E+02 -2.033836E+02 -4.630183E+02 -1.110626E+02 7.006448E+02 -1.231628E+02 -6.837535E+02 + 7.872310E+02 9.868651E+01 -9.729632E+02 7.759091E+02 1.796764E+02 -9.507107E+02 1.054519E+03 -1.819384E+02 -1.279141E+03 1.683802E+03 + 2.785687E+01 -2.418372E+03 2.302884E+03 1.508962E+03 -4.659480E+03 1.518193E+03 5.646611E+03 -6.465118E+03 -3.974988E+03 1.340630E+04 + -3.188402E+03 -2.275608E+04 2.504663E+04 3.226969E+04 -1.214821E+05 1.634341E+05 -1.219518E+05 4.848985E+04 -3.328618E+03 -4.824031E+03 + -2.513832E+03 6.571775E+03 -4.410524E+03 1.191869E+02 2.220947E+03 -2.023708E+03 6.577039E+02 5.229648E+02 -7.024253E+02 2.960422E+02 + 7.652725E+01 -2.089660E+02 1.535032E+02 -1.244096E+02 4.143469E+01 -3.460568E+01 6.474494E+00 1.074771E+02 -4.365598E+01 -3.232092E+01 + 9.740045E+00 1.025782E+02 -3.309289E+01 -2.660106E+02 -2.121641E+02 5.645180E+01 3.015577E+02 3.115217E+02 1.395859E+02 1.255218E+01 + -7.113840E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 37 + 9.437816E+02 -8.096245E+02 -1.432404E+03 2.273287E+02 1.841150E+03 6.558950E+02 -1.824034E+03 -5.142342E+02 1.776413E+03 -5.641094E+02 + -1.569086E+03 2.059735E+03 -3.702913E+02 -1.569923E+03 2.272460E+03 -1.299228E+03 -1.206574E+03 3.261198E+03 -1.862261E+03 -2.440975E+03 + 4.274118E+03 -2.481304E+02 -5.093854E+03 4.100340E+03 3.122528E+03 -7.479484E+03 1.930756E+03 8.222465E+03 -8.973572E+03 -4.857558E+03 + 1.667564E+04 -4.226497E+03 -2.490121E+04 2.617046E+04 3.397822E+04 -1.219518E+05 1.591404E+05 -1.209047E+05 5.065807E+04 -2.847762E+02 + -1.246160E+04 5.781415E+03 2.510447E+03 -5.567779E+03 4.327805E+03 -2.314544E+03 1.155033E+03 -5.950370E+02 3.702997E+02 -2.538145E+02 + 2.927138E+02 -3.325447E+02 3.511650E+02 -6.009077E+01 -2.292260E+02 4.738688E+02 -3.563766E+02 -1.671272E+02 4.926578E+02 -2.678773E+02 + -2.860445E+02 2.534120E+02 -6.970465E+00 -6.654445E+01 2.021177E+02 -1.609457E+02 -9.033577E+02 -9.723514E+02 -4.573579E+02 -9.005390E+01 + -3.705361E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 38 + -1.163197E+03 9.072380E+02 1.716443E+03 -2.497321E+02 -2.194411E+03 -6.110434E+02 2.396672E+03 9.093400E+01 -2.117807E+03 1.376575E+03 + 1.151749E+03 -2.656414E+03 1.396182E+03 1.203878E+03 -3.025564E+03 2.609657E+03 3.940637E+02 -3.732014E+03 3.429214E+03 1.282811E+03 + -4.996938E+03 2.455182E+03 3.813322E+03 -5.969686E+03 4.526024E+02 6.858142E+03 -6.344491E+03 -3.600880E+03 1.149746E+04 -4.206268E+03 + -1.299152E+04 1.685158E+04 5.643523E+03 -3.221897E+04 1.887275E+04 4.848985E+04 -1.209047E+05 1.378333E+05 -8.628139E+04 1.471517E+04 + 2.222620E+04 -2.179210E+04 6.305711E+03 5.934625E+03 -8.534630E+03 5.512470E+03 -1.736297E+03 -8.604638E+02 1.287563E+03 -5.645328E+02 + -3.252552E+02 9.548795E+02 -1.038010E+03 4.567691E+02 2.493617E+02 -6.776372E+02 5.958181E+02 -1.814299E+01 -5.155483E+02 5.058577E+02 + 2.178520E+02 -5.271341E+02 4.752379E+01 2.628748E+02 -1.793288E+01 3.208424E+02 8.735118E+02 8.905804E+02 5.393947E+02 2.268654E+02 + 1.015280E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 39 + 8.550940E+02 -5.465610E+02 -1.108046E+03 -3.284494E+01 1.313390E+03 4.398298E+02 -1.430548E+03 1.278301E+02 1.164175E+03 -1.239822E+03 + -1.705278E+02 1.736831E+03 -1.590992E+03 -7.633750E+01 1.875496E+03 -2.242372E+03 4.817155E+02 2.113418E+03 -2.859166E+03 3.759904E+02 + 2.831365E+03 -2.749807E+03 -9.136510E+02 4.051021E+03 -2.492603E+03 -2.760041E+03 5.339755E+03 -8.051575E+02 -6.203215E+03 5.644062E+03 + 4.175424E+03 -1.169561E+04 4.390300E+03 1.415737E+04 -2.139632E+04 -3.328618E+03 5.065807E+04 -8.628139E+04 7.802206E+04 -3.381559E+04 + -5.047692E+03 1.774910E+04 -1.150479E+04 1.453388E+03 2.739713E+03 -1.319536E+03 -1.420722E+03 3.062591E+03 -2.688883E+03 1.182611E+03 + 1.680847E+02 -1.044812E+03 1.095291E+03 -5.141779E+02 -1.376522E+02 4.265129E+02 -4.320274E+02 3.640336E+01 1.933034E+02 -2.510723E+02 + -1.083930E+02 4.059233E+02 1.383163E+02 -1.785904E+02 -1.085692E+02 -2.693732E+02 -5.274808E+02 -6.366982E+02 -5.565178E+02 -3.330212E+02 + -1.961128E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 40 + -1.219040E+02 3.162463E+01 7.666964E+01 1.322077E+02 6.927996E+01 -1.566940E+02 -1.947893E+02 -3.477750E+01 1.985275E+02 3.510718E+02 + -4.378310E+02 -2.339692E+02 7.818932E+02 -6.061674E+02 -8.366503E+01 6.146650E+02 -5.866998E+02 -9.047820E+01 9.389635E+02 -9.796758E+02 + -2.389532E+02 1.344970E+03 -7.747418E+02 -9.731566E+02 1.797742E+03 -4.255261E+02 -1.595543E+03 1.717862E+03 2.838130E+02 -1.851913E+03 + 9.281738E+02 1.499653E+03 -2.639182E+03 7.647299E+02 3.084175E+03 -4.824031E+03 -2.847762E+02 1.471517E+04 -3.381559E+04 4.426276E+04 + -3.866290E+04 2.226566E+04 -4.893516E+03 -6.463996E+03 1.068903E+04 -1.087001E+04 8.236020E+03 -4.460324E+03 1.835832E+03 4.729547E+01 + -8.228026E+02 6.683442E+02 5.444118E+01 -6.077922E+02 4.791023E+02 1.058896E+02 -4.139052E+02 5.363435E+02 4.536497E+01 -4.888212E+02 + 2.898850E+02 -5.007193E+01 -4.415596E+02 1.728997E+02 9.560484E+01 -1.453280E+02 2.848826E+02 7.026725E+02 5.966352E+02 2.994684E+02 + 1.637323E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 41 + -4.338625E+02 2.253918E+02 5.119014E+02 1.278618E+02 -8.084880E+02 -1.456661E+02 1.023250E+03 -5.301066E+01 -8.689108E+02 3.083200E+02 + 4.765972E+02 -6.611620E+02 1.856729E+02 3.904530E+02 -7.413062E+02 6.760858E+02 1.010093E+02 -8.644681E+02 6.243491E+02 4.533271E+02 + -8.996582E+02 2.561209E+02 5.626099E+02 -6.976712E+02 -4.822756E+01 9.901926E+02 -1.091161E+03 -3.096851E+02 1.981036E+03 -1.592240E+03 + -1.101212E+03 3.505265E+03 -2.028487E+03 -3.906313E+03 8.166514E+03 -2.513832E+03 -1.246160E+04 2.222620E+04 -5.047692E+03 -3.866290E+04 + 7.790746E+04 -8.232570E+04 5.083618E+04 -9.576067E+03 -1.615770E+04 2.263100E+04 -1.582792E+04 5.360688E+03 5.456242E+02 -3.195170E+03 + 2.965885E+03 -7.896006E+02 -1.491020E+03 2.461528E+03 -1.494300E+03 -3.352001E+02 1.467180E+03 -1.492099E+03 9.084985E+01 1.224370E+03 + -8.145154E+02 -2.822406E+02 7.606266E+02 -3.739139E+02 -3.258264E+02 4.769386E+02 5.664482E+01 -5.339053E+02 -4.142396E+02 -1.384130E+02 + -5.220200E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 42 + 6.258410E+02 -2.645981E+02 -5.874774E+02 -2.920570E+02 6.800339E+02 2.667828E+02 -7.441624E+02 -1.068671E+01 5.867731E+02 -2.296792E+02 + -1.710830E+02 4.404796E+02 -4.335510E+02 8.966950E+01 4.402670E+02 -7.438892E+02 3.037585E+02 5.065388E+02 -8.482878E+02 3.160636E+02 + 4.989457E+02 -8.180570E+02 2.656579E+02 6.650797E+02 -8.309125E+02 -2.237246E+02 1.453436E+03 -8.789676E+02 -1.323528E+03 2.295552E+03 + -3.477182E+02 -2.892152E+03 3.513937E+03 9.904375E+02 -7.175100E+03 6.571775E+03 5.781415E+03 -2.179210E+04 1.774910E+04 2.226566E+04 + -8.232570E+04 1.214590E+05 -1.086117E+05 5.637324E+04 -5.606122E+03 -1.925468E+04 1.846702E+04 -7.218948E+03 -1.163518E+03 5.826582E+03 + -5.744202E+03 2.243930E+03 1.509748E+03 -3.536884E+03 2.641105E+03 -1.893738E+02 -1.766218E+03 2.168260E+03 -7.192321E+02 -1.306890E+03 + 1.460251E+03 1.388575E+02 -9.807521E+02 7.340652E+02 7.467829E+02 -6.011536E+02 -6.992284E+02 -1.165245E+02 5.949125E+01 7.473743E+00 + -9.451388E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 43 + -3.807068E+02 1.845762E+02 3.077005E+02 3.114080E+01 -1.531544E+02 -1.038145E+01 4.951724E+01 1.629442E+01 -1.416803E+00 -3.253127E+01 + -2.190414E+02 2.329380E+02 9.887174E+01 -2.281030E+02 1.090841E+02 -6.526562E+00 -1.535864E+02 2.841865E+02 2.986525E+01 -5.356492E+02 + 4.511531E+02 3.156041E+02 -6.988446E+02 1.304081E+02 5.784049E+02 -5.325908E+02 -3.037558E+02 8.853619E+02 -2.371694E+02 -9.721523E+02 + 1.118205E+03 2.569480E+02 -1.900050E+03 1.805531E+03 1.119634E+03 -4.410524E+03 2.510447E+03 6.305711E+03 -1.150479E+04 -4.893516E+03 + 5.083618E+04 -1.086117E+05 1.386364E+05 -1.153987E+05 5.812489E+04 -9.965688E+03 -9.551454E+03 1.019536E+04 -3.442574E+03 -4.030645E+03 + 6.712151E+03 -4.752569E+03 8.419690E+02 2.672538E+03 -3.256069E+03 1.440097E+03 8.672298E+02 -2.126435E+03 1.496796E+03 6.367820E+02 + -1.700522E+03 4.116331E+02 9.053478E+02 -9.397358E+02 -8.734843E+02 4.901920E+02 1.018240E+03 6.444978E+02 2.098678E+02 3.632450E+01 + 5.763451E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 44 + -2.836523E+02 1.199733E+02 2.980104E+02 2.339680E+02 -3.787080E+02 -3.261236E+02 4.487641E+02 7.113684E+01 -3.257554E+02 9.120149E+01 + 2.545752E+02 -3.643351E+02 2.031984E+02 -6.310758E+01 -2.379188E+02 6.550222E+02 -3.169739E+02 -6.129974E+02 7.866576E+02 1.769453E+02 + -9.348755E+02 4.012177E+02 5.898862E+02 -7.119226E+02 -2.191224E+01 7.846344E+02 -8.011390E+02 -2.754635E+02 1.328725E+03 -7.359870E+02 + -9.435206E+02 1.800865E+03 -5.370592E+02 -2.355289E+03 3.457506E+03 1.191869E+02 -5.567779E+03 5.934625E+03 1.453388E+03 -6.463996E+03 + -9.576067E+03 5.637324E+04 -1.153987E+05 1.428241E+05 -1.131460E+05 5.878763E+04 -1.454638E+04 -9.928945E+03 1.174110E+04 -3.937228E+03 + -2.896740E+03 5.870723E+03 -4.351224E+03 3.748088E+02 2.321266E+03 -2.448847E+03 8.494808E+02 1.044330E+03 -1.616417E+03 4.070069E+02 + 9.897974E+02 -7.857586E+02 -2.686350E+02 6.686444E+02 4.022830E+02 -1.456308E+02 -5.175398E+02 -5.032183E+02 -2.188283E+02 -4.277016E+01 + -8.381905E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 45 + 6.365104E+02 -3.409830E+02 -6.279240E+02 -1.268802E+02 5.757419E+02 2.078782E+02 -5.202142E+02 -3.727832E+01 3.668062E+02 -1.128961E+02 + 7.377731E+00 3.120317E+01 -2.520313E+02 4.220195E+02 5.113026E+01 -7.815762E+02 5.872912E+02 4.417885E+02 -1.024608E+03 3.217795E+02 + 7.359636E+02 -7.330807E+02 -1.997356E+02 7.113506E+02 -2.839631E+02 -5.002927E+02 1.001720E+03 -2.793187E+02 -1.324025E+03 1.502311E+03 + 3.717782E+02 -2.265997E+03 1.862015E+03 1.360362E+03 -4.229844E+03 2.220947E+03 4.327805E+03 -8.534630E+03 2.739713E+03 1.068903E+04 + -1.615770E+04 -5.606122E+03 5.812489E+04 -1.131460E+05 1.268352E+05 -9.846088E+04 4.756673E+04 -2.123002E+03 -1.317042E+04 1.201717E+04 + -4.987256E+03 -2.325334E+03 4.976370E+03 -3.440858E+03 4.858422E+02 1.541995E+03 -1.879607E+03 7.944968E+02 4.741925E+02 -1.056189E+03 + 3.631750E+02 4.759775E+02 -4.708746E+02 -5.046056E+00 1.905176E+02 -1.266302E+02 1.112440E+01 2.527499E+02 1.866510E+02 6.006098E+01 + 1.942445E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 46 + -3.614988E+02 2.342173E+02 3.741396E+02 -1.349768E+02 -3.659725E+02 1.804972E+02 3.094330E+02 -6.540606E+01 -3.163837E+02 1.564808E+02 + -1.368391E+02 2.081942E+02 1.118106E+02 -4.957324E+02 1.120223E+02 5.849344E+02 -5.672819E+02 -1.887721E+02 9.131597E+02 -5.931373E+02 + -4.323211E+02 7.124674E+02 -2.587668E+00 -5.084884E+02 3.693754E+02 5.468762E+01 -6.805424E+02 6.335800E+02 5.434940E+02 -1.231664E+03 + 2.237935E+02 1.432769E+03 -1.752672E+03 -2.276754E+02 2.700437E+03 -2.023708E+03 -2.314544E+03 5.512470E+03 -1.319536E+03 -1.087001E+04 + 2.263100E+04 -1.925468E+04 -9.965688E+03 5.878763E+04 -9.846088E+04 1.113753E+05 -8.239186E+04 3.533284E+04 -6.966826E+03 -8.377214E+03 + 1.149821E+04 -7.528118E+03 2.055503E+03 2.595492E+03 -4.133948E+03 2.715350E+03 1.054179E+02 -2.290202E+03 2.200363E+03 -1.995030E+01 + -1.420981E+03 1.085006E+03 5.757245E+01 -8.982664E+02 2.796697E+02 5.656522E+02 -2.492096E+02 -7.272238E+02 -5.624662E+02 -2.367166E+02 + -1.002433E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 47 + -3.050381E+01 -9.350552E+00 -2.164886E+01 2.306038E+02 9.345707E+01 -3.339887E+02 -1.524226E+02 9.017075E+01 3.472187E+02 -1.374862E+02 + -8.896103E+01 -1.479143E+02 1.591324E+02 1.700527E+02 -1.113591E+02 -2.492827E+02 3.321359E+02 1.024044E+01 -5.111849E+02 4.246969E+02 + 2.631697E+02 -3.919350E+02 -1.378175E+02 2.959166E+02 -1.738932E+02 1.501544E+02 1.946286E+02 -5.044408E+02 1.306082E+02 4.551121E+02 + -3.888745E+02 -3.436591E+02 8.390933E+02 -2.046744E+02 -9.595856E+02 6.577039E+02 1.155033E+03 -1.736297E+03 -1.420722E+03 8.236020E+03 + -1.582792E+04 1.846702E+04 -9.551454E+03 -1.454638E+04 4.756673E+04 -8.239186E+04 9.108067E+04 -7.407943E+04 4.749760E+04 -1.580050E+04 + -5.761081E+03 1.647875E+04 -1.452892E+04 4.436940E+03 5.040527E+03 -8.182529E+03 4.713506E+03 1.491857E+03 -4.767409E+03 2.954047E+03 + 9.312128E+02 -3.149498E+03 1.860337E+03 1.407212E+03 -1.991802E+03 -1.059809E+03 1.189098E+03 1.724687E+03 1.083366E+03 4.194624E+02 + 1.713012E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 48 + 8.281475E+01 -5.087153E+01 3.822232E+01 -1.821862E+02 -1.441192E+02 2.553146E+02 2.857364E+02 -1.271165E+02 -5.320256E+02 2.009149E+02 + 4.165817E+02 -1.007879E+02 -3.493441E+02 2.436952E+02 3.116540E+01 -6.589346E+01 -7.359284E+01 1.159297E+02 4.532442E+01 -1.139073E+02 + -1.252402E+02 -2.580685E+00 3.812228E+02 -1.413542E+02 -1.546343E+02 -6.303402E+01 1.979563E+02 6.513114E+01 -3.351000E+02 1.996748E+02 + 1.305797E+02 -2.419886E+02 7.443269E+01 6.768356E+01 -1.213822E+02 5.229648E+02 -5.950370E+02 -8.604638E+02 3.062591E+03 -4.460324E+03 + 5.360688E+03 -7.218948E+03 1.019536E+04 -9.928945E+03 -2.123002E+03 3.533284E+04 -7.407943E+04 1.033012E+05 -9.824622E+04 6.290278E+04 + -2.194000E+04 -1.422466E+04 2.561246E+04 -1.625879E+04 -8.867530E+02 1.184568E+04 -1.091383E+04 1.961749E+03 5.870821E+03 -6.749477E+03 + 1.135407E+03 4.913711E+03 -4.715802E+03 -1.567428E+03 4.429309E+03 1.701610E+03 -2.613637E+03 -3.250349E+03 -1.819517E+03 -6.270582E+02 + -2.287232E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 49 + -2.107439E+01 2.818114E+00 -1.462687E+02 1.281914E+02 2.854760E+02 -1.282097E+02 -4.274642E+02 1.309528E+02 5.947006E+02 -2.369293E+02 + -4.691215E+02 2.008859E+02 3.282396E+02 -2.976030E+02 -9.007115E+01 2.013234E+02 -1.892628E+01 -1.470773E+02 1.450862E+02 -2.897453E+01 + 5.817809E+01 1.177126E+02 -3.773634E+02 4.682772E+01 3.050479E+02 -9.055108E+01 -2.525425E+02 1.561468E+02 2.218176E+02 -2.780836E+02 + 5.059832E+01 2.046876E+02 -3.197418E+02 1.275868E+02 2.790691E+02 -7.024253E+02 3.702997E+02 1.287563E+03 -2.688883E+03 1.835832E+03 + 5.456242E+02 -1.163518E+03 -3.442574E+03 1.174110E+04 -1.317042E+04 -6.966826E+03 4.749760E+04 -9.824622E+04 1.196180E+05 -1.052884E+05 + 6.322736E+04 -7.764814E+03 -2.141900E+04 2.326487E+04 -7.044059E+03 -8.977578E+03 1.290319E+04 -5.687660E+03 -3.950219E+03 7.944125E+03 + -3.228320E+03 -4.621950E+03 6.042384E+03 1.158276E+03 -5.415817E+03 -1.685614E+03 3.567178E+03 4.188241E+03 2.297477E+03 7.979174E+02 + 3.126616E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 50 + 6.078701E+01 6.578665E+01 -2.075370E+01 -6.590884E+01 -1.314494E+02 -3.866264E+01 2.902402E+02 -4.210837E+01 -3.459038E+02 4.779230E+01 + 3.815617E+02 -1.688059E+02 -2.386895E+02 2.223534E+02 1.556897E+02 -2.589882E+02 1.295394E+02 5.742206E+01 -2.009195E+02 8.997091E+01 + 1.221088E+01 -2.542754E+01 6.233880E+01 4.264214E+01 -1.716820E+02 1.282891E+02 6.829054E+01 -1.400340E+02 1.077847E+01 6.387412E+01 + -9.271128E+01 2.182387E+01 1.410569E+02 -1.762453E+02 7.698584E+00 2.960422E+02 -2.538145E+02 -5.645328E+02 1.182611E+03 4.729547E+01 + -3.195170E+03 5.826582E+03 -4.030645E+03 -3.937228E+03 1.201717E+04 -8.377214E+03 -1.580050E+04 6.290278E+04 -1.052884E+05 1.328238E+05 + -1.153326E+05 5.629812E+04 -8.095894E+03 -1.868068E+04 1.733360E+04 -2.558927E+03 -7.996146E+03 8.517221E+03 -1.649335E+03 -5.193913E+03 + 4.837110E+03 1.486466E+03 -4.950670E+03 2.684119E+02 4.143828E+03 5.800232E+02 -3.385686E+03 -3.572273E+03 -1.878192E+03 -6.613308E+02 + -2.900245E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 51 + 6.512950E+01 -1.249088E+02 8.489945E+01 -9.532447E+01 -5.936190E+01 2.162255E+02 -2.097983E+01 -1.189472E+02 2.737925E+01 1.495992E+02 + -2.043982E+02 1.178271E+02 7.806007E+01 -1.544599E+02 -4.337081E+01 1.529237E+02 -1.534760E+02 5.971274E+01 1.075204E+02 -3.918949E+01 + -7.448530E+01 -1.224211E+02 2.515428E+02 -4.163831E+01 -1.071885E+02 -2.989674E+01 1.018318E+02 4.733554E+01 -1.823663E+02 9.662422E+01 + 1.118191E+02 -1.726829E+02 6.139300E+01 1.595971E+02 -2.998477E+02 7.652725E+01 2.927138E+02 -3.252552E+02 1.680847E+02 -8.228026E+02 + 2.965885E+03 -5.744202E+03 6.712151E+03 -2.896740E+03 -4.987256E+03 1.149821E+04 -5.761081E+03 -2.194000E+04 6.322736E+04 -1.153326E+05 + 1.328212E+05 -1.004153E+05 5.303500E+04 -4.678072E+03 -1.833589E+04 1.584557E+04 -3.321284E+03 -6.686800E+03 7.263436E+03 -8.200826E+02 + -4.243658E+03 2.779322E+03 1.304490E+03 -1.965802E+03 -7.388088E+02 1.076874E+03 1.384627E+03 8.093263E+02 2.362130E+02 3.394816E+01 + 1.188567E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 52 + -1.524172E+02 5.044409E+01 4.830997E+01 1.323748E+02 4.923298E+01 -1.470695E+02 -1.868252E+02 1.920485E+02 2.591208E+02 -2.158528E+02 + -1.328629E+02 4.845169E+01 1.100375E+02 -6.454501E+01 -3.354617E+01 6.576671E+01 -2.035967E-01 -7.734906E+01 6.269664E+01 -5.952266E+01 + 5.292308E+01 1.684656E+02 -2.538234E+02 -3.655519E+01 1.876339E+02 1.620286E+01 -1.823177E+02 1.362710E+00 2.711273E+02 -1.693697E+02 + -1.163656E+02 2.323162E+02 -1.598197E+02 -1.388380E+02 3.750962E+02 -2.089660E+02 -3.325447E+02 9.548795E+02 -1.044812E+03 6.683442E+02 + -7.896006E+02 2.243930E+03 -4.752569E+03 5.870723E+03 -2.325334E+03 -7.528118E+03 1.647875E+04 -1.422466E+04 -7.764814E+03 5.629812E+04 + -1.004153E+05 1.192263E+05 -1.045650E+05 5.386116E+04 -2.732150E+03 -2.222770E+04 1.863763E+04 -2.269152E+03 -9.094274E+03 8.524318E+03 + 8.985546E+01 -6.324590E+03 4.189246E+03 2.811166E+03 -3.862422E+03 -2.276860E+03 2.092274E+03 3.095201E+03 1.840493E+03 6.945500E+02 + 3.041184E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 53 + 4.633619E+01 5.149495E+01 -1.100484E+02 6.676079E+01 2.627276E+01 -1.355495E+02 1.646296E+02 -1.718045E+01 -3.633703E+02 7.836536E+01 + 4.158982E+02 -1.939121E+02 -1.977577E+02 2.143444E+02 8.996999E+01 -2.609988E+02 1.377890E+02 1.193576E+02 -2.344934E+02 6.290999E+01 + 6.285396E+01 -2.868822E+01 -6.058138E+01 1.616017E+02 -1.421467E+02 -6.145993E+01 2.771148E+02 -9.344280E+01 -3.266591E+02 2.844791E+02 + 1.234894E+02 -3.659926E+02 2.052644E+02 2.434793E+02 -4.097164E+02 1.535032E+02 3.511650E+02 -1.038010E+03 1.095291E+03 5.444118E+01 + -1.491020E+03 1.509748E+03 8.419690E+02 -4.351224E+03 4.976370E+03 2.055503E+03 -1.452892E+04 2.561246E+04 -2.141900E+04 -8.095894E+03 + 5.303500E+04 -1.045650E+05 1.359126E+05 -1.142905E+05 5.120761E+04 6.042636E+03 -2.520679E+04 1.518496E+04 2.725007E+03 -1.201980E+04 + 6.248821E+03 5.510329E+03 -7.917188E+03 -1.400202E+03 6.395975E+03 1.876946E+03 -4.409400E+03 -5.045556E+03 -2.650416E+03 -8.889571E+02 + -3.597611E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 54 + 1.315763E+02 -1.185550E+02 3.672581E+01 -1.405462E+02 -5.094683E+01 2.398891E+02 -3.036438E+01 -1.949491E+02 1.322027E+02 2.253080E+02 + -3.304381E+02 1.373778E+00 2.354358E+02 -8.286844E+01 -2.532265E+02 3.361765E+02 -1.655095E+02 -1.139547E+02 2.467014E+02 -1.618750E+01 + -9.959968E+01 -9.222078E+01 1.758550E+02 -1.075166E+02 8.977946E+01 -4.405410E+01 -1.475812E+02 1.603693E+02 1.113619E+02 -2.396665E+02 + 1.027807E+00 2.817343E+02 -1.621308E+02 -1.871448E+02 2.609582E+02 -1.244096E+02 -6.009077E+01 4.567691E+02 -5.141779E+02 -6.077922E+02 + 2.461528E+03 -3.536884E+03 2.672538E+03 3.748088E+02 -3.440858E+03 2.595492E+03 4.436940E+03 -1.625879E+04 2.326487E+04 -1.868068E+04 + -4.678072E+03 5.386116E+04 -1.142905E+05 1.474777E+05 -1.145118E+05 4.624318E+04 3.523696E+03 -2.190290E+04 1.275847E+04 4.835465E+03 + -1.064723E+04 1.826689E+03 6.089018E+03 -2.625012E+03 -4.221545E+03 6.620099E+02 3.749621E+03 3.242688E+03 1.471690E+03 4.187966E+02 + 1.413535E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 55 + -1.720690E+02 2.937864E+01 2.144788E+02 4.222447E+01 -1.523777E+02 -9.457278E+01 -9.914308E+00 1.962769E+02 1.571584E+02 -3.100978E+02 + 8.692207E+01 9.188914E+01 -1.697082E+02 6.632370E+01 7.781860E+01 -7.895564E+01 1.567226E+01 1.065110E+01 4.006631E+01 -1.281324E+02 + 4.914108E+01 9.060271E+01 -6.948611E+00 -9.192346E+01 2.082547E+01 7.015475E+01 -6.216889E+01 -8.952426E+01 2.495018E+02 -8.859455E+01 + -1.567699E+02 1.070186E+02 1.146940E+01 -8.628611E+01 9.424304E+01 4.143469E+01 -2.292260E+02 2.493617E+02 -1.376522E+02 4.791023E+02 + -1.494300E+03 2.641105E+03 -3.256069E+03 2.321266E+03 4.858422E+02 -4.133948E+03 5.040527E+03 -8.867530E+02 -7.044059E+03 1.733360E+04 + -1.833589E+04 -2.732150E+03 5.120761E+04 -1.145118E+05 1.408031E+05 -1.100741E+05 5.034983E+04 4.195920E+03 -2.344810E+04 1.204270E+04 + 5.808072E+03 -1.036303E+04 1.975031E+03 5.472155E+03 -2.375750E+03 -3.511724E+03 2.375368E+02 1.878212E+03 1.238483E+03 4.433060E+02 + 1.780038E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 56 + 2.624288E+01 4.391678E+01 -2.200564E+02 8.917918E+01 1.449582E+02 -1.346923E+01 1.196436E+02 -1.389173E+02 -3.025657E+02 1.506881E+02 + 2.038387E+02 -1.375367E+02 4.172959E+01 -1.771794E+01 6.560060E+01 -1.754374E+02 1.833361E+02 6.138850E+01 -2.634568E+02 1.436785E+02 + 7.642802E+01 -1.503529E+02 -6.256459E+01 2.823610E+02 -2.181559E+02 -7.935527E+01 3.634540E+02 -1.430238E+02 -4.749547E+02 4.629930E+02 + 2.554915E+02 -5.984374E+02 1.204034E+02 5.419591E+02 -5.124344E+02 -3.460568E+01 4.738688E+02 -6.776372E+02 4.265129E+02 1.058896E+02 + -3.352001E+02 -1.893738E+02 1.440097E+03 -2.448847E+03 1.541995E+03 2.715350E+03 -8.182529E+03 1.184568E+04 -8.977578E+03 -2.558927E+03 + 1.584557E+04 -2.222770E+04 6.042636E+03 4.624318E+04 -1.100741E+05 1.436892E+05 -1.152971E+05 4.642742E+04 8.802517E+03 -2.613424E+04 + 9.301687E+03 1.119778E+04 -1.084192E+04 -3.396920E+03 7.967431E+03 3.383450E+03 -4.438543E+03 -5.792830E+03 -3.172409E+03 -1.052052E+03 + -3.958475E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 57 + 1.050305E+02 2.498981E+01 -8.918126E+01 -1.038615E+02 -1.061647E+01 1.011262E+02 -3.773353E+01 -7.962121E+01 1.117842E+02 1.949696E+02 + -3.094472E+02 1.390224E+01 1.845626E+02 -1.388292E+02 -7.707185E+01 2.780902E+02 -2.439622E+02 5.504605E+00 1.022055E+02 1.913660E+01 + -5.427011E+01 3.026425E+01 1.119603E+01 -9.982014E+01 1.305751E+02 6.782659E+00 -3.044866E+02 2.209908E+02 2.970528E+02 -3.660784E+02 + -2.074362E+02 5.669108E+02 -9.749921E+01 -5.702024E+02 4.908300E+02 6.474494E+00 -3.563766E+02 5.958181E+02 -4.320274E+02 -4.139052E+02 + 1.467180E+03 -1.766218E+03 8.672298E+02 8.494808E+02 -1.879607E+03 1.054179E+02 4.713506E+03 -1.091383E+04 1.290319E+04 -7.996146E+03 + -3.321284E+03 1.863763E+04 -2.520679E+04 3.523696E+03 5.034983E+04 -1.152971E+05 1.454801E+05 -1.129000E+05 4.278654E+04 1.555450E+04 + -2.507069E+04 1.442741E+03 1.305083E+04 -3.620852E+03 -8.231278E+03 1.609210E+02 6.134995E+03 5.743979E+03 2.918216E+03 1.016196E+03 + 4.359721E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 58 + -1.273568E+02 8.108792E+01 2.239908E+02 -1.224124E+02 -1.379221E+02 -2.882872E+00 -4.569833E+01 1.983125E+02 1.432350E+02 -4.062617E+02 + 2.173451E+02 1.250420E+02 -3.002190E+02 1.368998E+02 1.100443E+02 -6.623413E+01 -9.413030E+01 2.418462E+01 1.262806E+02 -1.089092E+02 + -1.068848E+02 1.867194E+02 8.977928E+01 -2.525713E+02 -8.428752E+00 2.411756E+02 -3.939908E+01 -2.446696E+02 2.382203E+02 -3.634987E+00 + -1.848948E+02 7.275310E+01 1.302792E+02 -1.859770E+02 8.685883E+01 1.074771E+02 -1.671272E+02 -1.814299E+01 3.640336E+01 5.363435E+02 + -1.492099E+03 2.168260E+03 -2.126435E+03 1.044330E+03 7.944968E+02 -2.290202E+03 1.491857E+03 1.961749E+03 -5.687660E+03 8.517221E+03 + -6.686800E+03 -2.269152E+03 1.518496E+04 -2.190290E+04 4.195920E+03 4.642742E+04 -1.129000E+05 1.549295E+05 -1.203084E+05 3.677918E+04 + 1.964620E+04 -2.255532E+04 -2.022853E+03 1.115835E+04 5.906377E+02 -5.371861E+03 -2.305459E+03 8.000078E+02 1.117642E+03 4.764922E+02 + 1.859095E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 59 + 6.751375E+01 -5.142220E+01 -1.372288E+02 -2.826373E+01 1.591127E+02 1.184333E+02 5.284454E+01 -2.182046E+02 -1.029288E+02 1.163467E+02 + 5.979967E+01 -9.753265E+01 1.202797E+02 -4.797707E+01 -7.631053E+00 -1.481903E+02 3.064843E+02 -1.364110E+02 -1.429072E+02 1.260499E+02 + 1.156287E+02 -2.126438E+02 -9.409482E+01 3.493757E+02 -1.132871E+02 -2.940198E+02 4.086029E+02 -5.292043E+01 -5.125066E+02 4.235754E+02 + 3.211337E+02 -6.110561E+02 6.801817E+01 5.905423E+02 -5.444784E+02 -4.365598E+01 4.926578E+02 -5.155483E+02 1.933034E+02 4.536497E+01 + 9.084985E+01 -7.192321E+02 1.496796E+03 -1.616417E+03 4.741925E+02 2.200363E+03 -4.767409E+03 5.870821E+03 -3.950219E+03 -1.649335E+03 + 7.263436E+03 -9.094274E+03 2.725007E+03 1.275847E+04 -2.344810E+04 8.802517E+03 4.278654E+04 -1.203084E+05 1.609888E+05 -1.170791E+05 + 3.070813E+04 2.534110E+04 -2.029712E+04 -7.513802E+03 1.136169E+04 6.028639E+03 -4.653482E+03 -7.027772E+03 -4.041361E+03 -1.445321E+03 + -6.166116E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 60 + 1.208509E+02 -1.167249E+02 -1.245096E+02 1.288404E+02 6.018667E+01 -1.212137E+02 1.383697E+01 -4.884404E+01 1.454087E+02 1.999871E+02 + -4.485746E+02 1.727972E+02 1.347008E+02 -1.537516E+02 6.357625E+01 -1.726430E+02 1.074477E+02 1.726632E+02 -1.712224E+02 -6.246890E+01 + 2.727767E+02 -1.603367E+02 -1.842566E+02 2.389867E+02 1.269820E+02 -3.192254E+02 -1.259398E+02 3.676073E+02 1.306536E+02 -3.215567E+02 + -1.796554E+02 4.785082E+02 -1.090907E+02 -3.632352E+02 3.212363E+02 -3.232092E+01 -2.678773E+02 5.058577E+02 -2.510723E+02 -4.888212E+02 + 1.224370E+03 -1.306890E+03 6.367820E+02 4.070069E+02 -1.056189E+03 -1.995030E+01 2.954047E+03 -6.749477E+03 7.944125E+03 -5.193913E+03 + -8.200826E+02 8.524318E+03 -1.201980E+04 4.835465E+03 1.204270E+04 -2.613424E+04 1.555450E+04 3.677918E+04 -1.170791E+05 1.734334E+05 + -1.255319E+05 2.037724E+04 2.966717E+04 -1.214790E+04 -1.521857E+04 2.186082E+03 9.802630E+03 7.081746E+03 2.885504E+03 8.926837E+02 + 3.973152E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 61 + 1.255321E+02 -2.975149E+01 -4.751011E+01 -1.003157E+02 -2.422128E+00 8.078906E+00 4.135861E+01 7.683516E+01 -2.116276E+00 -1.919270E+02 + 1.574790E+02 1.365598E+02 -4.113763E+02 2.832892E+02 1.376509E+02 -2.797307E+02 3.426671E+01 2.120939E+02 -1.647676E+02 -2.650021E+01 + -2.858951E+01 4.536799E+01 2.063326E+02 -2.105381E+02 -1.809472E+02 3.617195E+02 -4.029824E+01 -2.608991E+02 2.958702E+02 -8.394049E+01 + -2.191211E+02 2.239475E+02 1.381078E+01 -2.785080E+02 3.115690E+02 9.740045E+00 -2.860445E+02 2.178520E+02 -1.083930E+02 2.898850E+02 + -8.145154E+02 1.460251E+03 -1.700522E+03 9.897974E+02 3.631750E+02 -1.420981E+03 9.312128E+02 1.135407E+03 -3.228320E+03 4.837110E+03 + -4.243658E+03 8.985546E+01 6.248821E+03 -1.064723E+04 5.808072E+03 9.301687E+03 -2.507069E+04 1.964620E+04 3.070813E+04 -1.255319E+05 + 1.845863E+05 -1.188138E+05 1.306844E+04 2.635926E+04 -4.097512E+03 -1.109387E+04 -2.880000E+03 2.115635E+03 2.023249E+03 8.915366E+02 + 4.477403E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 62 + -1.139148E+02 8.307601E+01 1.680115E+02 -5.799334E+01 -3.132668E+01 -2.949902E+01 1.334795E+01 -6.144107E+00 -1.660026E+02 2.218282E+00 + 2.801106E+02 -1.629698E+02 3.127880E+01 -1.355990E+02 6.048644E+01 3.522886E+02 -2.400366E+02 -2.700006E+02 2.372098E+02 1.683354E+02 + -3.500119E+02 1.248997E+02 2.141603E+02 -2.338787E+02 -1.261110E+02 3.162078E+02 8.943008E+01 -3.555854E+02 -1.573505E+02 3.796541E+02 + 2.519575E+02 -6.310667E+02 1.371002E+02 4.719954E+02 -4.430814E+02 1.025782E+02 2.534120E+02 -5.271341E+02 4.059233E+02 -5.007193E+01 + -2.822406E+02 1.388575E+02 4.116331E+02 -7.857586E+02 4.759775E+02 1.085006E+03 -3.149498E+03 4.913711E+03 -4.621950E+03 1.486466E+03 + 2.779322E+03 -6.324590E+03 5.510329E+03 1.826689E+03 -1.036303E+04 1.119778E+04 1.442741E+03 -2.255532E+04 2.534110E+04 2.037724E+04 + -1.188138E+05 1.940280E+05 -1.233710E+05 1.174559E+03 3.194766E+04 5.768348E+03 -1.372758E+04 -1.333534E+04 -6.304912E+03 -2.040671E+03 + -8.774519E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 63 + -6.046219E+02 6.489440E+01 6.826345E+02 4.771482E+02 -4.095576E+02 -5.347727E+02 1.238452E+02 2.778913E+02 -1.000743E+02 2.384568E+02 + -2.812026E+02 -1.424058E+02 5.678258E+02 -5.180126E+02 -8.402381E+01 4.984289E+02 -3.796848E+02 -6.483469E+01 3.556774E+02 -2.183136E+02 + 5.533027E+01 1.955894E+02 -3.541483E+02 -4.875450E+01 4.713156E+02 -2.760334E+02 -2.662135E+02 4.606348E+02 -1.617822E+02 -2.307497E+02 + 2.343466E+02 2.996263E+00 -9.889578E+01 1.066333E+02 -5.742868E+01 -3.309289E+01 -6.970465E+00 4.752379E+01 1.383163E+02 -4.415596E+02 + 7.606266E+02 -9.807521E+02 9.053478E+02 -2.686350E+02 -4.708746E+02 5.757245E+01 1.860337E+03 -4.715802E+03 6.042384E+03 -4.950670E+03 + 1.304490E+03 4.189246E+03 -7.917188E+03 6.089018E+03 1.975031E+03 -1.084192E+04 1.305083E+04 -2.022853E+03 -2.029712E+04 2.966717E+04 + 1.306844E+04 -1.233710E+05 2.118313E+05 -1.151980E+05 -1.827222E+04 1.712387E+04 1.276948E+04 3.863668E+03 4.155139E+02 -1.770899E+02 + -2.345340E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 64 + -3.171253E+02 -2.024266E+02 3.963278E+02 4.123056E+02 -3.550960E+02 -1.111335E+02 3.993452E+02 6.206538E+01 -5.211295E+02 1.669667E+02 + 2.032528E+02 -8.570549E+01 -1.811425E+02 2.618728E+02 -1.426658E+02 -1.608692E+02 1.039545E+02 2.736947E+02 -1.557683E+02 -3.668714E+02 + 4.446241E+02 -6.416386E+01 -2.117185E+02 2.293429E+02 8.909639E+01 -3.966161E+02 5.624282E+01 5.133022E+02 -1.572622E+02 -3.995861E+02 + 3.298740E+01 4.048387E+02 -2.373886E+02 -1.979401E+02 4.059408E+02 -2.660106E+02 -6.654445E+01 2.628748E+02 -1.785904E+02 1.728997E+02 + -3.739139E+02 7.340652E+02 -9.397358E+02 6.686444E+02 -5.046056E+00 -8.982664E+02 1.407212E+03 -1.567428E+03 1.158276E+03 2.684119E+02 + -1.965802E+03 2.811166E+03 -1.400202E+03 -2.625012E+03 5.472155E+03 -3.396920E+03 -3.620852E+03 1.115835E+04 -7.513802E+03 -1.214790E+04 + 2.635926E+04 1.174559E+03 -1.151980E+05 2.354064E+05 -1.158474E+05 -3.223672E+04 1.132374E+04 1.604339E+04 8.050139E+03 2.645398E+03 + 1.150799E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 65 + 4.422850E+02 -3.572716E+02 -2.548369E+02 -9.805140E+01 -4.207831E+01 4.414913E+02 3.096013E+02 -2.162924E+02 -5.660617E+02 -1.992035E+01 + 6.002018E+02 -3.315805E+01 -7.186612E+02 7.904509E+02 -1.964276E+02 -2.645831E+02 3.667106E+02 -1.050394E+02 -2.219900E+02 1.184530E+02 + 1.622668E+02 -4.406855E+02 3.193787E+02 3.642587E+02 -5.389808E+02 -5.533860E+00 3.888499E+02 -3.018802E+02 -4.581101E+01 4.109423E+02 + -1.869397E+02 -2.503890E+02 5.277095E+01 1.606217E+02 1.707840E+01 -2.121641E+02 2.021177E+02 -1.793288E+01 -1.085692E+02 9.560484E+01 + -3.258264E+02 7.467829E+02 -8.734843E+02 4.022830E+02 1.905176E+02 2.796697E+02 -1.991802E+03 4.429309E+03 -5.415817E+03 4.143828E+03 + -7.388088E+02 -3.862422E+03 6.395975E+03 -4.221545E+03 -2.375750E+03 7.967431E+03 -8.231278E+03 5.906377E+02 1.136169E+04 -1.521857E+04 + -4.097512E+03 3.194766E+04 -1.827222E+04 -1.158474E+05 2.640043E+05 -9.763831E+04 -4.290793E+04 -1.270192E+04 -2.797170E+03 -3.103460E+02 + 3.391666E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 66 + 1.040936E+03 -3.748140E+02 -7.945048E+02 -5.257363E+02 2.906080E+02 6.600721E+02 -1.042460E+02 -3.005172E+02 8.527106E+01 2.413273E+01 + -2.266168E+00 1.195048E+01 -2.390827E+02 4.270016E+02 -3.236405E+02 1.634315E+02 2.179620E+02 -4.001058E+02 7.675894E+01 2.223161E+02 + -9.578834E+01 -2.444910E+02 2.035056E+02 1.356696E+02 -3.102506E+02 2.400434E+02 7.886932E+01 -5.351095E+02 2.281642E+02 5.303653E+02 + -3.437152E+02 -3.293317E+02 2.474028E+02 1.675246E+02 -1.703341E+02 5.645180E+01 -1.609457E+02 3.208424E+02 -2.693732E+02 -1.453280E+02 + 4.769386E+02 -6.011536E+02 4.901920E+02 -1.456308E+02 -1.266302E+02 5.656522E+02 -1.059809E+03 1.701610E+03 -1.685614E+03 5.800232E+02 + 1.076874E+03 -2.276860E+03 1.876946E+03 6.620099E+02 -3.511724E+03 3.383450E+03 1.609210E+02 -5.371861E+03 6.028639E+03 2.186082E+03 + -1.109387E+04 5.768348E+03 1.712387E+04 -3.223672E+04 -9.763831E+04 2.878674E+05 -9.012977E+04 -4.815396E+04 -1.880672E+04 -5.313260E+03 + -1.516915E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 67 + 1.025034E+03 -3.705984E+02 -9.330080E+02 -5.289808E+02 5.620588E+02 6.822190E+02 -4.072723E+02 -3.643278E+02 5.579945E+02 1.567646E+02 + -6.024504E+02 1.237777E+02 2.918796E+02 -2.480556E+02 -2.817436E+02 5.365825E+02 -1.824204E+01 -3.442700E+02 1.995312E+02 3.347678E+00 + 2.407686E+01 7.316707E+01 -2.973397E+02 4.286305E+01 2.605480E+02 -9.523304E+01 -2.961149E+02 2.021488E+02 2.970349E+02 -4.175245E+02 + -2.462664E+02 5.265078E+02 4.169833E+01 -3.850093E+02 2.661572E+02 3.015577E+02 -9.033577E+02 8.735118E+02 -5.274808E+02 2.848826E+02 + 5.664482E+01 -6.992284E+02 1.018240E+03 -5.175398E+02 1.112440E+01 -2.492096E+02 1.189098E+03 -2.613637E+03 3.567178E+03 -3.385686E+03 + 1.384627E+03 2.092274E+03 -4.409400E+03 3.749621E+03 2.375368E+02 -4.438543E+03 6.134995E+03 -2.305459E+03 -4.653482E+03 9.802630E+03 + -2.880000E+03 -1.372758E+04 1.276948E+04 1.132374E+04 -4.290793E+04 -9.012977E+04 3.203356E+05 -5.261623E+04 -2.203432E+04 -6.560099E+03 + -2.147309E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 68 + 5.603182E+02 -2.542394E+02 -5.703648E+02 -2.273130E+02 4.859189E+02 3.887724E+02 -4.449263E+02 -2.881476E+02 5.332985E+02 1.753754E+02 + -5.642477E+02 1.526159E+02 3.045649E+02 -4.277005E+02 -9.745506E+01 4.790128E+02 -1.300439E+02 -2.035649E+02 1.431792E+02 -2.748602E+01 + 1.550378E+02 4.244542E+01 -4.761886E+02 1.813097E+02 4.515022E+02 -4.367527E+02 -3.475588E+02 7.064345E+02 1.962469E+02 -9.347393E+02 + -7.459154E+01 9.491244E+02 -1.447195E+02 -6.436189E+02 4.829765E+02 3.115217E+02 -9.723514E+02 8.905804E+02 -6.366982E+02 7.026725E+02 + -5.339053E+02 -1.165245E+02 6.444978E+02 -5.032183E+02 2.527499E+02 -7.272238E+02 1.724687E+03 -3.250349E+03 4.188241E+03 -3.572273E+03 + 8.093263E+02 3.095201E+03 -5.045556E+03 3.242688E+03 1.878212E+03 -5.792830E+03 5.743979E+03 8.000078E+02 -7.027772E+03 7.081746E+03 + 2.115635E+03 -1.333534E+04 3.863668E+03 1.604339E+04 -1.270192E+04 -4.815396E+04 -5.261623E+04 3.727009E+05 -1.465980E+04 -4.453217E+03 + -1.521768E+03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 69 + 2.086078E+02 -9.523572E+01 -1.971443E+02 -4.179779E+01 2.151629E+02 9.433363E+01 -2.782409E+02 -1.337534E+02 2.828780E+02 1.051519E+02 + -2.471905E+02 9.077392E+01 1.002343E+02 -2.282020E+02 3.145933E+01 1.890139E+02 -1.145095E+02 -4.371532E+01 5.660820E+01 -3.281752E+01 + 1.387721E+02 5.360766E+00 -3.177570E+02 1.581433E+02 3.008093E+02 -3.487477E+02 -2.116319E+02 5.303494E+02 1.057424E+02 -6.420443E+02 + -1.948400E+01 6.248094E+02 -9.706082E+01 -4.081128E+02 2.612941E+02 1.395859E+02 -4.573579E+02 5.393947E+02 -5.565178E+02 5.966352E+02 + -4.142396E+02 5.949125E+01 2.098678E+02 -2.188283E+02 1.866510E+02 -5.624662E+02 1.083366E+03 -1.819517E+03 2.297477E+03 -1.878192E+03 + 2.362130E+02 1.840493E+03 -2.650416E+03 1.471690E+03 1.238483E+03 -3.172409E+03 2.918216E+03 1.117642E+03 -4.041361E+03 2.885504E+03 + 2.023249E+03 -6.304912E+03 4.155139E+02 8.050139E+03 -2.797170E+03 -1.880672E+04 -2.203432E+04 -1.465980E+04 4.002997E+05 -1.965662E+03 + -6.839518E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 70 + 7.118285E+01 -1.327104E+01 -4.543034E+01 -1.220569E+01 4.504831E+01 1.283306E+00 -9.532662E+01 -3.790499E+01 8.125026E+01 3.613132E+01 + -4.735488E+01 3.374513E+01 2.083372E+00 -6.198566E+01 2.535132E+01 2.104946E+01 -3.409304E+01 2.188033E+01 -2.326009E+00 -2.763047E+01 + 7.735288E+01 9.148778E+00 -1.351133E+02 5.485277E+01 1.175687E+02 -1.320602E+02 -7.300715E+01 2.012446E+02 2.804116E+01 -2.380478E+02 + 1.855328E+00 2.274220E+02 -2.194932E+01 -1.312218E+02 5.521580E+01 1.255218E+01 -9.005390E+01 2.268654E+02 -3.330212E+02 2.994684E+02 + -1.384130E+02 7.473743E+00 3.632450E+01 -4.277016E+01 6.006098E+01 -2.367166E+02 4.194624E+02 -6.270582E+02 7.979174E+02 -6.613308E+02 + 3.394816E+01 6.945500E+02 -8.889571E+02 4.187966E+02 4.433060E+02 -1.052052E+03 1.016196E+03 4.764922E+02 -1.445321E+03 8.926837E+02 + 8.915366E+02 -2.040671E+03 -1.770899E+02 2.645398E+03 -3.103460E+02 -5.313260E+03 -6.560099E+03 -4.453217E+03 -1.965662E+03 4.061020E+05 + -2.137330E+02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 71 + 4.600067E+01 6.997044E+00 -2.000399E+01 -2.112756E+01 -7.243574E-01 -2.746842E+00 -2.472002E+01 -1.413479E+01 1.281310E+01 1.334960E+01 + 6.661506E+00 1.486217E+01 -1.476947E+01 -1.848655E+01 7.783160E+00 -1.177685E+01 8.145540E-01 2.545060E+01 -1.892692E+01 -1.590250E+01 + 5.022724E+01 6.744367E+00 -6.926549E+01 1.878242E+01 5.076430E+01 -5.504756E+01 -2.254643E+01 8.634233E+01 -4.039354E+00 -1.061969E+02 + 1.307612E+01 1.030241E+02 -3.157996E+00 -5.077046E+01 4.404590E+00 -7.113840E+00 -3.705361E+00 1.015280E+02 -1.961128E+02 1.637323E+02 + -5.220200E+01 -9.451388E+00 5.763451E+00 -8.381905E+00 1.942445E+01 -1.002433E+02 1.713012E+02 -2.287232E+02 3.126616E+02 -2.900245E+02 + 1.188567E+01 3.041184E+02 -3.597611E+02 1.413535E+02 1.780038E+02 -3.958475E+02 4.359721E+02 1.859095E+02 -6.166116E+02 3.973152E+02 + 4.477403E+02 -8.774519E+02 -2.345340E+02 1.150799E+03 3.391666E+02 -1.516915E+03 -2.147309E+03 -1.521768E+03 -6.839518E+02 -2.137330E+02 + 4.066318E+05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 + 72 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.105130E+02 -3.079808E+02 1.354936E+02 -3.847975E+01 1.008819E+01 -1.008169E+00 -5.443471E-02 1.795792E-01 -2.243962E-01 + -4.615756E-02 6.112228E-02 -1.492188E-01 -1.110715E-01 1.567997E-01 -2.572811E-01 1.798762E-01 1.368689E-01 -1.812640E-01 -9.390823E-02 + 4.536324E-01 -9.286019E-01 8.801527E-01 -6.736888E-01 1.415902E+00 -1.741692E+00 7.082784E-01 6.407344E-01 -2.362493E+00 2.889708E+00 + -2.851800E+00 3.590311E+00 -5.300153E+00 5.519256E+00 -5.594707E+00 5.591497E+00 -6.699553E+00 1.473265E+01 -1.359472E+01 4.403214E+00 + 1.083065E+00 9.539215E+00 -1.016561E+01 2.232596E+01 7.307891E+00 3.665296E+01 -1.518565E+01 7.337356E+01 2.000328E+02 9.493193E+01 + 9.498857E+01 -3.709506E+02 6.115176E+02 1.363901E+03 -4.297799E+03 2.379822E+04 -1.015685E+04 -9.603727E+01 2.682455E+03 -1.703380E+03 + 1.232404E+04 1.389880E+03 4.807345E+03 -1.163300E+03 -4.052916E+04 -3.044333E+04 -1.193229E+04 3.074538E+03 -8.567786E+02 5.661757E+02 + 5.890859E+09 + 73 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -3.079808E+02 6.342742E+02 -4.542257E+02 1.601530E+02 -4.218952E+01 7.880662E+00 -1.554839E+00 -1.970840E-01 8.556368E-01 + 2.177762E-02 3.438457E-02 1.831884E-01 1.710577E-01 -3.303666E-01 1.899586E-01 -1.229808E-01 -1.135199E-01 -2.903860E-01 1.513640E+00 + -2.298638E+00 2.052710E+00 -1.787143E+00 1.683982E+00 -1.719630E+00 1.080813E+00 3.414610E-01 -3.480126E+00 5.613787E+00 -4.044814E+00 + 4.361568E+00 -5.975894E+00 9.512309E+00 -1.084049E+01 9.846266E+00 -6.917186E+00 4.564477E+00 -1.726626E+01 1.199410E+01 6.659039E+00 + -2.245394E+01 5.118478E+00 1.213092E+01 -2.608280E+01 -1.058222E+02 8.774615E+01 -1.823254E+02 -2.093618E+02 3.697249E+01 1.351757E+02 + -2.840224E+02 2.454277E+02 -4.717429E+02 -1.683214E+03 -1.580732E+03 -6.859132E+04 1.421956E+04 -1.575947E+02 2.747194E+03 3.813890E+03 + 1.140713E+03 -2.849021E+03 -4.815780E+03 3.222601E+03 6.305515E+04 8.929571E+04 2.155777E+04 -3.096672E+03 4.513991E+03 -5.312590E+01 + -7.725383E+09 + 74 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.354936E+02 -4.542257E+02 5.764642E+02 -3.503021E+02 1.128036E+02 -2.544727E+01 5.747190E+00 -9.401564E-01 -5.402573E-01 + 2.156867E-01 -3.452321E-01 1.779676E-01 -1.289020E-01 1.783966E-01 5.449375E-02 -1.432228E-01 -2.249065E-01 6.358568E-01 -1.831499E+00 + 1.870378E+00 -1.155593E+00 1.085792E+00 -1.367158E+00 9.444736E-01 4.515220E-01 -1.269220E+00 3.500189E+00 -4.679650E+00 1.899341E+00 + -1.333804E+00 1.664284E+00 -5.713790E+00 5.574121E+00 -1.295855E-01 -5.362100E+00 9.862980E+00 8.154522E+00 -8.701416E+00 -7.902574E+00 + 3.100598E+01 -2.592302E+01 -1.872338E+01 1.518818E+01 1.643158E+02 -2.313598E+02 3.356966E+02 -1.441459E+01 -5.467423E+02 1.317463E+00 + 5.830362E+02 5.830626E+01 1.945957E+02 4.913783E+02 6.497093E+03 5.494776E+04 -8.300154E+03 -6.932156E+02 -9.326368E+03 2.483858E+03 + -1.064864E+04 -1.145550E+03 -1.180215E+03 -8.485626E+03 -1.061320E+04 -1.267357E+05 -1.801831E+04 -4.633398E+03 -3.720700E+03 3.467785E+02 + -2.678551E+09 + 75 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -3.847975E+01 1.601530E+02 -3.503021E+02 3.939985E+02 -2.149475E+02 6.018203E+01 -1.246120E+01 2.232482E+00 -1.191545E-01 + -3.626723E-01 3.986995E-01 -2.024837E-01 -1.332274E-01 1.636123E-01 -9.942162E-02 -2.630756E-01 4.854984E-01 -4.222750E-01 1.027371E+00 + -8.800154E-01 6.927103E-01 -8.375814E-01 7.323270E-01 -6.942958E-01 -3.549863E-01 7.149928E-01 -1.064817E+00 2.039658E+00 -2.189629E+00 + 5.327458E-01 7.827930E-01 2.673552E+00 -1.859327E+00 -5.205138E+00 8.970454E+00 -1.475968E+01 -9.516916E-01 7.793588E+00 1.571011E+00 + -2.263699E+01 3.509755E+01 1.014422E+01 -2.478277E+01 -9.389696E+01 2.242806E+02 -1.131105E+02 5.274466E+01 9.268111E+02 -3.618302E+02 + -2.115372E+02 -4.041853E+02 5.217144E+02 -8.448280E+02 -4.422236E+03 -8.695468E+03 3.017220E+03 7.145837E+02 4.227808E+03 -6.058046E+03 + 4.757627E+04 -4.150261E+02 2.351170E+03 6.279030E+03 3.387038E+03 2.394571E+04 1.478654E+04 1.307666E+04 1.502254E+03 2.594557E+02 + 3.594968E+09 + 76 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.008819E+01 -4.218952E+01 1.128036E+02 -2.149475E+02 2.241047E+02 -1.143190E+02 3.021092E+01 -5.990320E+00 2.814902E-01 + 7.981541E-01 -7.701314E-01 3.604274E-01 -1.084414E-01 7.457946E-02 -3.245563E-01 5.838762E-01 -1.489337E-01 -3.340846E-01 -2.097409E-01 + 5.104000E-01 -2.347896E-01 7.530121E-02 -1.682328E-01 2.673522E-01 5.582414E-01 -6.464197E-01 5.892924E-01 -1.943570E+00 3.107794E+00 + -2.482184E+00 1.894061E+00 -3.453058E+00 2.973487E+00 3.848749E-01 -3.924179E+00 7.530721E+00 8.450729E-01 -2.544829E+00 -9.160170E+00 + 1.815334E+01 -1.700810E+01 -7.592798E+00 1.959658E+01 2.597159E+01 -1.188872E+02 -8.678461E+01 2.644518E+02 -8.059355E+02 2.802296E+02 + 3.149788E+02 6.255397E+02 -1.200644E+03 1.157734E+03 9.857669E+03 -1.325695E+03 -1.701427E+03 -6.038124E+02 6.634030E+02 1.332368E+02 + -6.498640E+04 3.949788E+03 -1.372720E+03 -4.494017E+02 -2.603023E+04 2.506794E+04 -1.447811E+04 -9.924108E+03 -1.750081E+03 -1.213013E+03 + 1.227773E+09 + 77 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.008169E+00 7.880662E+00 -2.544727E+01 6.018203E+01 -1.143190E+02 1.195466E+02 -6.104340E+01 1.721050E+01 -3.104605E+00 + -3.820998E-01 4.060387E-01 -1.841000E-01 3.518690E-01 -4.872232E-01 5.457131E-01 -2.273263E-01 -7.051991E-01 1.194512E+00 -8.638733E-01 + 2.562636E-01 -4.255178E-02 1.230704E-01 3.627265E-01 -7.242451E-01 3.346322E-01 -1.144921E-01 -1.462151E-01 2.128264E+00 -3.125692E+00 + 3.280249E+00 -3.297016E+00 2.868235E+00 -9.069520E-01 -4.756834E-01 3.808312E+00 -3.463534E+00 -4.812410E+00 4.729261E+00 9.575884E+00 + -1.204950E+01 -9.554021E-01 1.378721E+00 4.926406E+00 -1.312607E+01 2.360821E+01 1.731121E+01 -2.962084E+02 4.753085E+02 1.781068E+01 + -2.979692E+02 -4.559089E+02 1.204189E+03 -8.007334E+02 -7.601918E+03 6.088795E+03 4.326530E+02 4.725524E+02 7.889897E+01 -1.489918E+03 + 4.331207E+04 -2.351620E+03 2.481499E+03 1.018263E+03 -5.119010E+03 3.684536E+04 -1.149118E+04 3.869566E+03 5.943839E+02 8.086835E+02 + -3.428461E+09 + 78 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -5.443471E-02 -1.554839E+00 5.747190E+00 -1.246120E+01 3.021092E+01 -6.104340E+01 6.654833E+01 -3.599940E+01 1.124014E+01 + -2.646828E+00 8.211973E-01 -4.240553E-01 -1.997321E-02 3.099270E-01 -3.635170E-01 8.127517E-02 5.239739E-01 -7.379154E-01 4.352902E-01 + 2.296505E-01 -7.781170E-01 9.627216E-01 -8.912161E-01 6.494207E-01 -6.131389E-01 5.010700E-01 -4.543488E-01 -1.351349E+00 3.039935E+00 + -3.512426E+00 2.514935E+00 -9.967034E-01 8.687322E-04 1.368216E+00 -3.071044E+00 2.601257E+00 1.974122E+00 -3.277762E+00 -5.410748E+00 + 7.173906E+00 -4.196525E+00 4.697492E+00 4.640563E+00 2.718056E-02 -2.665036E+00 4.928548E+01 8.676544E+01 -1.289345E+02 -1.273469E+02 + 8.952377E+01 2.451967E+02 -7.656313E+02 5.990844E+02 -7.841562E+02 2.557742E+02 3.866084E+03 -3.041980E+02 7.824008E+02 6.445081E+02 + -3.866014E+04 -2.930666E+03 -2.943263E+03 -1.303440E+03 1.644163E+04 -2.369345E+04 3.544210E+04 -5.960999E+03 9.651323E+02 4.028573E+02 + 3.890406E+09 + 79 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.795792E-01 -1.970840E-01 -9.401564E-01 2.232482E+00 -5.990320E+00 1.721050E+01 -3.599940E+01 4.099232E+01 -2.335184E+01 + 7.903291E+00 -2.422646E+00 6.322655E-01 -6.619843E-02 -1.220662E-01 1.429121E-01 -7.845684E-02 -8.843363E-02 1.960654E-01 6.954414E-02 + -6.483659E-01 9.897756E-01 -1.104376E+00 7.405583E-01 -1.795932E-01 5.275747E-02 -3.207244E-01 8.797052E-01 2.359194E-01 -2.160518E+00 + 3.079795E+00 -2.096698E+00 8.814941E-01 -2.152709E+00 2.052247E+00 -7.783751E-01 1.062597E+00 -1.125384E+00 -1.032324E+00 2.425718E+00 + -3.220482E+00 -3.899860E-01 1.638043E+01 -2.683526E+01 1.777019E+01 -1.727882E+01 -9.952654E+01 2.950475E+01 -1.055756E+00 -1.214492E+01 + 1.870364E+02 -1.528717E+02 3.341406E+02 -6.143015E+02 3.772142E+03 -1.248710E+03 -3.789426E+03 1.222846E+03 -2.276234E+03 2.739868E+02 + 2.970968E+04 4.531346E+03 2.210908E+03 1.798344E+03 -1.143164E+04 2.308800E+04 -3.173608E+04 4.386395E+03 -1.738702E+03 -9.360197E+02 + -1.634980E+09 + 80 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.243962E-01 8.556368E-01 -5.402573E-01 -1.191545E-01 2.814902E-01 -3.104605E+00 1.124014E+01 -2.335184E+01 2.757569E+01 + -1.709882E+01 6.488756E+00 -1.699290E+00 7.144288E-02 6.963749E-02 2.227934E-02 -6.402551E-02 3.914504E-02 2.794090E-02 -9.668924E-02 + 2.383416E-01 -3.126248E-01 5.553095E-01 -5.070790E-01 2.400714E-01 1.988735E-02 -1.808244E-02 -1.719005E-01 -4.242656E-01 1.464715E+00 + -1.868243E+00 1.546355E+00 -1.103229E+00 2.038366E+00 -1.242794E+00 -3.069713E-01 2.139844E-01 -4.169366E-02 2.423057E+00 1.194605E+00 + -4.412651E+00 6.900245E+00 -1.371532E+01 2.056494E+01 -1.928702E+01 1.299260E+01 8.698668E+01 -3.540788E+01 5.276734E+01 1.917612E+02 + -7.471504E+01 1.630133E+02 -9.382661E+01 -1.670328E+02 -1.413271E+02 -1.094974E+04 -3.380024E+01 -9.464201E+02 1.540427E+03 -3.336778E+02 + 8.158217E+03 -3.337784E+03 -1.244702E+02 -2.638486E+03 -2.356757E+03 -8.707586E+03 1.271375E+04 -2.837838E+03 1.172714E+03 2.516768E+02 + 1.233032E+09 + 81 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.615756E-02 2.177762E-02 2.156867E-01 -3.626723E-01 7.981541E-01 -3.820998E-01 -2.646828E+00 7.903291E+00 -1.709882E+01 + 2.285581E+01 -1.557900E+01 5.787847E+00 -1.306843E+00 1.042604E-01 5.396908E-02 -2.139077E-02 3.032201E-02 -5.298140E-02 4.532406E-03 + 2.210290E-01 -3.439320E-01 -4.871752E-02 2.771857E-01 -2.731982E-01 3.089464E-02 3.126618E-01 -6.757681E-01 1.271478E+00 -1.653768E+00 + 1.471073E+00 -1.025751E+00 2.032663E-01 1.546330E-01 -1.592562E+00 2.773412E+00 -2.583224E+00 -1.906867E-01 -3.480070E+00 1.532490E+00 + 5.457098E+00 -5.971793E+00 7.358425E+00 -2.166726E+01 9.476164E+00 2.588599E+00 -4.326368E+01 7.144614E+01 -1.280378E+02 -8.787338E+01 + -6.472532E+01 -1.072102E+02 -1.076341E+02 6.435095E+02 -4.628828E+03 1.890950E+04 6.849469E+02 2.326490E+01 -1.047334E+03 -4.225195E+02 + -1.787166E+04 5.588285E+02 -1.537897E+03 8.002300E+02 7.028261E+03 -2.241781E+04 1.600640E+03 -2.228396E+03 1.229275E+00 -1.838106E+02 + -2.380152E+09 + 82 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 6.112228E-02 3.438457E-02 -3.452321E-01 3.986995E-01 -7.701314E-01 4.060387E-01 8.211973E-01 -2.422646E+00 6.488756E+00 + -1.557900E+01 2.181473E+01 -1.468489E+01 5.178230E+00 -1.316718E+00 1.453182E-01 2.892661E-02 -1.414840E-02 -2.459430E-02 4.173850E-02 + -2.340283E-01 5.268598E-01 -2.285557E-01 -2.261174E-01 4.872430E-01 -3.148660E-01 3.737736E-02 3.569411E-01 -1.270774E+00 1.585132E+00 + -1.620532E+00 9.546860E-01 3.575261E-01 -1.227406E+00 2.295109E+00 -2.383268E+00 3.351786E+00 1.008337E+00 1.847585E+00 -2.669702E+00 + -2.158579E+00 2.538125E+00 -4.594258E+00 1.066606E+01 -4.820371E+00 1.607473E+01 1.746965E+01 3.859109E+01 8.893844E+01 -5.442955E+01 + -8.843206E+01 2.626234E+01 1.624092E+02 -4.751142E+02 4.030597E+03 -1.017130E+04 1.270380E+03 2.938871E+02 6.105586E+02 -5.344456E+02 + 1.401600E+04 -1.222052E+03 1.404298E+03 5.031056E+02 3.996066E+03 3.307626E+04 4.988240E+02 9.280939E+03 -9.754103E+02 5.106608E+01 + 1.687608E+09 + 83 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.492188E-01 1.831884E-01 1.779676E-01 -2.024837E-01 3.604274E-01 -1.841000E-01 -4.240553E-01 6.322655E-01 -1.699290E+00 + 5.787847E+00 -1.468489E+01 1.959572E+01 -1.273871E+01 4.723186E+00 -1.329778E+00 3.132612E-01 -9.607465E-02 1.451118E-01 -2.492792E-01 + 3.559816E-01 -5.471314E-01 4.283785E-01 -7.644018E-02 -2.704528E-01 3.765118E-01 -2.401522E-01 2.311523E-02 1.994177E-01 -2.401774E-01 + 3.677152E-01 4.752618E-01 -2.344946E+00 3.683395E+00 -3.160148E+00 2.045975E+00 -3.195313E+00 9.660869E-01 1.060056E-03 -6.257466E-01 + 1.222577E+00 -1.516956E+00 -1.907377E+00 -6.612488E-02 4.528456E+00 -9.716470E+00 1.929872E+01 -1.759902E+02 4.170823E+01 4.172413E+01 + 1.783435E+02 -9.876409E+01 -1.918570E+01 1.816097E+02 -7.780559E+02 3.005070E+03 -9.942502E+02 -6.108745E+02 -3.229900E+02 7.667866E+02 + 4.360266E+03 3.414251E+02 -9.475956E+01 -8.738278E+02 -6.656790E+03 -2.551530E+04 -7.252585E+03 -1.941335E+03 -3.132418E+02 1.501350E+02 + 5.801190E+08 + 84 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.110715E-01 1.710577E-01 -1.289020E-01 -1.332274E-01 -1.084414E-01 3.518690E-01 -1.997321E-02 -6.619843E-02 7.144288E-02 + -1.306843E+00 5.178230E+00 -1.273871E+01 1.725959E+01 -1.185716E+01 4.568530E+00 -1.233194E+00 1.576355E-01 -1.450778E-01 3.699631E-01 + -4.796052E-01 4.743246E-01 -5.780678E-01 6.210118E-01 -3.881153E-01 1.967126E-02 2.445482E-01 -4.936339E-01 5.605552E-01 -3.977282E-01 + 2.614304E-01 -1.121686E+00 2.540760E+00 -3.064710E+00 2.002740E+00 -1.037105E+00 2.354987E+00 -3.643742E+00 -1.238514E-01 2.262415E+00 + -1.138805E+00 -2.523607E+00 1.239100E+01 -8.214446E+00 -6.255482E+00 -1.097496E+01 -4.531552E+01 1.302714E+02 5.366412E+01 2.579762E+01 + -1.278148E+02 1.104136E+02 -2.543178E+01 -1.940322E+02 1.396882E+03 -1.189672E+04 8.038944E+02 9.882161E+02 -8.381996E+01 4.238434E+02 + -2.141545E+04 -2.178472E+02 -1.130880E+02 1.318379E+03 6.273862E+03 6.816353E+03 8.072711E+03 -1.848926E+03 1.176342E+03 -2.982344E+02 + 5.678270E+08 + 85 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.567997E-01 -3.303666E-01 1.783966E-01 1.636123E-01 7.457946E-02 -4.872232E-01 3.099270E-01 -1.220662E-01 6.963749E-02 + 1.042604E-01 -1.316718E+00 4.723186E+00 -1.185716E+01 1.680678E+01 -1.179075E+01 4.552729E+00 -1.225459E+00 4.339699E-01 -5.418920E-01 + 5.835208E-01 -3.789054E-01 2.752188E-01 -2.948130E-01 2.156252E-01 1.825186E-01 -2.572939E-01 1.944256E-01 -2.323086E-01 -1.347398E-02 + 4.955060E-01 -5.778592E-01 1.213741E-01 1.041657E-01 -1.719829E-01 4.571519E-01 -1.119524E+00 1.947940E+00 4.048853E-01 -5.012848E+00 + 1.979662E+00 1.215307E+01 -1.948561E+01 7.337500E+00 1.288553E+01 -1.215181E+01 5.913247E+01 -4.015745E+01 -6.714592E+01 -7.780202E+01 + 2.619959E+02 -5.880917E+01 4.991921E+01 -1.725820E+02 9.221130E+02 1.087202E+04 -2.817547E+03 -3.758754E+02 -1.633176E+02 -1.369666E+03 + 5.201526E+03 6.314663E+02 5.412367E+02 -1.119849E+03 -6.917432E+03 1.175862E+04 -8.209852E+03 -1.767241E+03 -5.866090E+02 2.910965E+02 + -2.489034E+09 + 86 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.572811E-01 1.899586E-01 5.449375E-02 -9.942162E-02 -3.245563E-01 5.457131E-01 -3.635170E-01 1.429121E-01 2.227934E-02 + 5.396908E-02 1.453182E-01 -1.329778E+00 4.568530E+00 -1.179075E+01 1.763618E+01 -1.356180E+01 6.106402E+00 -2.104142E+00 7.988299E-01 + -5.404710E-01 1.767547E-01 1.232038E-01 -1.272823E-01 7.461336E-02 -3.668098E-01 9.452296E-02 3.310067E-01 -3.749838E-01 7.674774E-01 + -9.726979E-01 1.104167E+00 -9.369902E-01 5.920478E-01 -1.100781E-01 -4.256646E-01 1.403408E-01 8.313618E-01 -2.002193E+00 6.054013E+00 + -1.404108E+00 -1.675832E+01 1.712978E+01 -1.947113E+00 -2.048324E+01 6.404360E+01 -8.440093E+01 1.117223E+02 -3.734380E+02 1.986700E+02 + -1.761980E+02 -4.623707E+00 -7.888906E+01 4.391287E+02 -2.533216E+03 -7.114921E+03 9.126406E+02 -2.586743E+02 -5.084050E+02 7.937531E+02 + 5.163750E+03 -9.460009E+02 -9.034876E+02 -8.235062E+02 2.896572E+03 -2.534900E+04 -3.199452E+03 5.234347E+03 -4.113584E+02 1.548642E+02 + 3.286930E+09 + 87 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.798762E-01 -1.229808E-01 -1.432228E-01 -2.630756E-01 5.838762E-01 -2.273263E-01 8.127517E-02 -7.845684E-02 -6.402551E-02 + -2.139077E-02 2.892661E-02 3.132612E-01 -1.233194E+00 4.552729E+00 -1.356180E+01 2.247475E+01 -1.907552E+01 9.665654E+00 -3.533851E+00 + 9.847076E-01 -2.233846E-01 -2.943122E-02 7.049342E-02 2.858495E-01 -3.585044E-01 5.976107E-01 -9.245839E-01 8.521558E-01 -1.144800E+00 + 9.368653E-01 -4.373192E-01 3.765863E-01 2.180642E-01 -8.083793E-01 1.454587E+00 -2.763730E+00 3.413254E+00 -2.014014E-01 -4.463190E+00 + -4.454228E-01 1.367210E+01 -1.394132E+01 5.160341E+00 2.397757E+01 -8.354912E+01 7.773148E+01 -1.928548E+02 4.859404E+02 -3.684509E+02 + 1.620329E+02 1.356925E+02 -4.538579E+01 -3.221260E+02 3.480793E+03 -1.008568E+03 -3.601223E+02 7.967981E+02 8.995150E+02 1.541533E+03 + -9.183887E+03 4.341874E+03 -1.705300E+01 2.617194E+03 1.957376E+03 2.326296E+04 1.400051E+04 -4.326143E+03 1.109074E+03 -1.352532E+02 + -2.584673E+09 + 88 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.368689E-01 -1.135199E-01 -2.249065E-01 4.854984E-01 -1.489337E-01 -7.051991E-01 5.239739E-01 -8.843363E-02 3.914504E-02 + 3.032201E-02 -1.414840E-02 -9.607465E-02 1.576355E-01 -1.225459E+00 6.106402E+00 -1.907552E+01 3.114528E+01 -2.704573E+01 1.426141E+01 + -5.202092E+00 1.683085E+00 -7.697234E-01 3.877681E-01 -7.383420E-01 8.931739E-01 -1.057207E+00 7.764332E-01 -5.293490E-01 1.363889E+00 + -1.939667E+00 1.455131E+00 -9.420829E-01 -1.430927E-01 8.404376E-01 -2.502593E+00 5.265928E+00 -4.712694E+00 -3.142730E-02 5.165212E+00 + -1.665202E+00 -4.136923E+00 9.631003E+00 -1.344280E+01 -1.969291E+00 7.065907E+01 -1.241798E+02 2.679607E+02 -2.658960E+02 2.369038E+02 + -1.549351E+02 -1.577786E+02 2.057444E+01 4.125940E+02 -5.785916E+03 2.205960E+03 -1.784593E+02 -9.384382E+02 -2.204258E+02 -2.739707E+02 + 1.232222E+04 -3.659124E+03 7.826650E+02 -2.939794E+03 -5.283192E+03 9.205764E+03 -1.201758E+04 1.473556E+03 -1.526167E+03 7.404533E+01 + 2.623279E+09 + 89 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.812640E-01 -2.903860E-01 6.358568E-01 -4.222750E-01 -3.340846E-01 1.194512E+00 -7.379154E-01 1.960654E-01 2.794090E-02 + -5.298140E-02 -2.459430E-02 1.451118E-01 -1.450778E-01 4.339699E-01 -2.104142E+00 9.665654E+00 -2.704573E+01 4.173820E+01 -3.598730E+01 + 1.961332E+01 -8.354443E+00 3.467282E+00 -1.520695E+00 8.196118E-01 -3.924434E-01 1.518788E-01 5.492281E-01 -5.287363E-01 -9.967295E-01 + 2.879306E+00 -2.807035E+00 1.294987E+00 4.643046E-01 -1.211886E+00 2.965930E+00 -6.812030E+00 6.818532E+00 -1.719074E+00 -7.426570E+00 + 7.941997E+00 -7.022544E+00 4.496766E+00 6.080430E+00 -2.701283E+01 -1.912812E+01 9.005819E+01 -3.374024E+02 2.762210E+02 -2.820773E+02 + 4.275198E+02 7.757200E+00 9.738178E+01 -3.050634E+02 3.105022E+03 7.226828E+03 1.138020E+03 5.561424E+02 1.967754E+02 -2.372656E+03 + -7.460296E+03 8.231108E+02 6.588412E+02 1.841638E+03 2.560322E+03 -1.868948E+04 2.234458E+03 1.534492E+03 1.139799E+03 -2.320717E+02 + 2.022808E+08 + 90 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -9.390823E-02 1.513640E+00 -1.831499E+00 1.027371E+00 -2.097409E-01 -8.638733E-01 4.352902E-01 6.954414E-02 -9.668924E-02 + 4.532406E-03 4.173850E-02 -2.492792E-01 3.699631E-01 -5.418920E-01 7.988299E-01 -3.533851E+00 1.426141E+01 -3.598730E+01 5.382907E+01 + -4.658198E+01 2.622512E+01 -1.138474E+01 4.260402E+00 -1.741100E+00 6.328870E-02 1.117408E+00 -2.030393E+00 2.005709E+00 -4.225636E-01 + -2.347426E+00 2.265876E+00 7.239198E-01 -2.967454E+00 2.067934E+00 -2.024476E+00 7.725208E+00 -1.395630E+01 9.702924E+00 8.188151E+00 + -1.044721E+01 3.713452E+00 -1.854452E+00 -9.048290E+00 5.535608E+01 -5.910355E+01 -2.119608E+01 2.421098E+02 -1.782673E+02 3.564970E+02 + -5.957591E+02 3.316424E+01 3.435403E+01 1.935661E+02 -3.132300E+02 -2.386186E+04 -6.173485E+02 1.548472E+03 -5.965264E+02 2.897386E+03 + 2.275210E+03 7.801686E+02 -1.366654E+03 -1.393757E+03 -2.745259E+02 1.458989E+04 8.322859E+03 -5.926037E+03 -8.034157E+01 6.033314E+02 + -4.001454E+09 + 91 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.536324E-01 -2.298638E+00 1.870378E+00 -8.800154E-01 5.104000E-01 2.562636E-01 2.296505E-01 -6.483659E-01 2.383416E-01 + 2.210290E-01 -2.340283E-01 3.559816E-01 -4.796052E-01 5.835208E-01 -5.404710E-01 9.847076E-01 -5.202092E+00 1.961332E+01 -4.658198E+01 + 6.613787E+01 -5.534947E+01 2.978262E+01 -1.211335E+01 4.716354E+00 -7.453259E-01 -1.523306E+00 2.167051E+00 -2.250049E+00 1.289240E+00 + 1.359036E+00 -9.856939E-01 -3.257664E+00 5.706895E+00 -2.843533E+00 1.756818E+00 -8.190679E+00 1.599803E+01 -1.151871E+01 -7.678424E+00 + 7.928579E+00 7.173742E+00 -5.558695E+00 1.545364E+01 -8.254252E+01 1.141918E+02 -1.111278E+01 -1.137972E+02 -7.863710E+01 -1.953395E+02 + 3.402851E+02 -4.274419E+01 6.432546E+01 4.044198E+01 1.199005E+03 2.551522E+04 -1.077261E+03 -1.989203E+03 3.490192E+02 -1.038182E+03 + -3.866104E+04 -7.258699E+02 -8.057092E+02 2.871460E+03 -3.656438E+03 -6.992394E+03 -1.371622E+04 1.048260E+04 -6.485856E+02 -6.333462E+02 + 2.492521E+09 + 92 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -9.286019E-01 2.052710E+00 -1.155593E+00 6.927103E-01 -2.347896E-01 -4.255178E-02 -7.781170E-01 9.897756E-01 -3.126248E-01 + -3.439320E-01 5.268598E-01 -5.471314E-01 4.743246E-01 -3.789054E-01 1.767547E-01 -2.233846E-01 1.683085E+00 -8.354443E+00 2.622512E+01 + -5.534947E+01 7.463563E+01 -5.999638E+01 3.143618E+01 -1.305792E+01 3.815671E+00 8.337200E-03 -1.442880E+00 1.956036E+00 -1.111334E+00 + -1.098954E+00 1.266082E+00 2.584711E+00 -5.519831E+00 3.208913E+00 -1.395772E+00 5.640535E+00 -1.108076E+01 4.821607E+00 1.331209E+01 + -7.872703E+00 -6.358489E+00 3.117822E+00 -1.516013E+01 9.244412E+01 -1.308596E+02 -1.666907E+01 1.050738E+02 1.354962E+02 2.154044E+02 + -3.763714E+01 1.320352E+00 -8.586524E+01 -3.531100E+01 -1.601524E+03 8.675862E+03 1.914792E+03 -2.105887E+02 -6.897391E+01 2.958956E+02 + 4.310150E+04 4.388912E+02 3.308988E+03 -1.730892E+03 -2.270890E+03 -2.122964E+04 2.617403E+04 -1.385790E+04 4.502551E+02 -7.385768E+01 + -8.192304E+08 + 93 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 8.801527E-01 -1.787143E+00 1.085792E+00 -8.375814E-01 7.530121E-02 1.230704E-01 9.627216E-01 -1.104376E+00 5.553095E-01 + -4.871752E-02 -2.285557E-01 4.283785E-01 -5.780678E-01 2.752188E-01 1.232038E-01 -2.943122E-02 -7.697234E-01 3.467282E+00 -1.138474E+01 + 2.978262E+01 -5.999638E+01 7.915248E+01 -6.377826E+01 3.462330E+01 -1.444010E+01 5.032288E+00 -7.933686E-01 -6.434131E-01 -3.784007E-01 + 1.402500E+00 -2.210040E+00 2.620744E-01 2.072738E+00 -1.070171E+00 -9.543020E-01 -5.093219E-01 5.983850E+00 -4.467827E+00 -1.286836E+01 + 3.089821E+00 7.691226E+00 -5.412077E-01 7.991160E+00 -5.338542E+01 9.113084E+01 1.235436E+02 -2.548116E+01 -3.056932E+02 5.491741E+01 + -1.641856E+02 8.979841E+01 -3.315910E+01 1.168782E+02 -9.875748E+02 -5.051220E+04 -8.197669E+02 2.888234E+03 -7.695368E+02 1.740138E+03 + -2.063600E+04 -1.369895E+03 -3.981366E+03 -2.020247E+02 -2.861364E+00 3.420736E+04 -3.808091E+04 1.921754E+04 -8.798842E+02 5.419627E+02 + 1.055342E+09 + 94 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -6.736888E-01 1.683982E+00 -1.367158E+00 7.323270E-01 -1.682328E-01 3.627265E-01 -8.912161E-01 7.405583E-01 -5.070790E-01 + 2.771857E-01 -2.261174E-01 -7.644018E-02 6.210118E-01 -2.948130E-01 -1.272823E-01 7.049342E-02 3.877681E-01 -1.520695E+00 4.260402E+00 + -1.211335E+01 3.143618E+01 -6.377826E+01 8.829779E+01 -7.603237E+01 4.467701E+01 -2.144720E+01 9.006487E+00 -4.021885E+00 3.045841E+00 + -2.204491E+00 1.863006E+00 -2.640536E-01 -1.302829E+00 8.015524E-01 -1.220812E+00 2.662290E+00 -3.896502E+00 5.206728E+00 1.230989E+01 + -2.823881E+00 -1.935197E+01 1.925404E+01 -1.902966E+01 1.306526E+01 -2.928014E+01 -1.314907E+02 -5.484108E+01 3.458206E+02 -5.507850E+02 + 2.875098E+02 -1.877299E+02 2.683428E+02 -1.798998E+02 5.246911E+03 4.819480E+04 -2.573317E+03 -4.552303E+03 3.421056E+02 -2.173702E+03 + 1.327436E+04 5.168130E+03 2.747692E+03 4.829861E+02 4.162664E+03 -5.480528E+04 3.479436E+04 -2.279603E+04 2.587543E+03 -2.376337E+02 + -1.302767E+09 + 95 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.415902E+00 -1.719630E+00 9.444736E-01 -6.942958E-01 2.673522E-01 -7.242451E-01 6.494207E-01 -1.795932E-01 2.400714E-01 + -2.731982E-01 4.872430E-01 -2.704528E-01 -3.881153E-01 2.156252E-01 7.461336E-02 2.858495E-01 -7.383420E-01 8.196118E-01 -1.741100E+00 + 4.716354E+00 -1.305792E+01 3.462330E+01 -7.603237E+01 1.141882E+02 -1.048888E+02 6.513323E+01 -3.250111E+01 1.460844E+01 -6.735196E+00 + 2.718294E+00 -9.745586E-01 -9.292020E-01 2.922395E+00 -1.550081E+00 2.173583E+00 -1.570711E+00 -3.222004E-01 -4.304165E+00 -1.173554E+01 + 1.022493E+01 2.591605E+01 -5.001637E+01 5.033392E+01 -1.075514E+00 -1.365619E+01 1.008995E+02 2.452648E+02 -4.420818E+02 4.881042E+02 + -2.075225E+02 2.268980E+02 -3.875204E+02 4.378378E+02 -1.064628E+04 -2.210946E+04 3.725293E+03 3.228233E+03 -9.065192E+01 1.604938E+03 + -1.275012E+03 -5.069796E+03 -1.332248E+03 -2.474086E+02 -5.235412E+03 7.028994E+04 -2.394964E+04 2.137452E+04 -3.849498E+03 -8.661925E+02 + 2.857732E+09 + 96 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.741692E+00 1.080813E+00 4.515220E-01 -3.549863E-01 5.582414E-01 3.346322E-01 -6.131389E-01 5.275747E-02 1.988735E-02 + 3.089464E-02 -3.148660E-01 3.765118E-01 1.967126E-02 1.825186E-01 -3.668098E-01 -3.585044E-01 8.931739E-01 -3.924434E-01 6.328870E-02 + -7.453259E-01 3.815671E+00 -1.444010E+01 4.467701E+01 -1.048888E+02 1.575544E+02 -1.422084E+02 8.697845E+01 -4.211203E+01 1.755935E+01 + -7.436885E+00 3.623784E+00 -1.980541E+00 -3.405810E+00 3.353636E+00 -9.991754E-01 -1.915668E+00 1.589369E+00 2.241602E+00 1.900559E+01 + -2.364898E+01 -6.437292E+00 5.809936E+01 -7.214668E+01 1.211168E+00 -3.043495E+01 9.591743E+01 -1.734722E+02 2.646349E+01 -2.375694E+02 + 7.978937E+00 -2.089212E+02 5.133332E+02 -3.592846E+02 9.678644E+03 7.233295E+03 -4.246828E+03 -2.200414E+03 7.644006E+02 -6.303488E+03 + 4.132396E+04 9.248749E+02 3.027524E+03 -1.706040E+03 9.550564E+03 -9.396283E+04 1.416444E+04 -8.752602E+03 3.297346E+03 8.544160E+02 + -6.347081E+08 + 97 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 7.082784E-01 3.414610E-01 -1.269220E+00 7.149928E-01 -6.464197E-01 -1.144921E-01 5.010700E-01 -3.207244E-01 -1.808244E-02 + 3.126618E-01 3.737736E-02 -2.401522E-01 2.445482E-01 -2.572939E-01 9.452296E-02 5.976107E-01 -1.057207E+00 1.518788E-01 1.117408E+00 + -1.523306E+00 8.337200E-03 5.032288E+00 -2.144720E+01 6.513323E+01 -1.422084E+02 2.016037E+02 -1.767595E+02 1.045906E+02 -4.739994E+01 + 2.116840E+01 -1.204668E+01 7.472174E+00 2.043427E+00 -7.646568E+00 9.198205E+00 -9.975410E+00 9.579497E+00 -9.137078E+00 -2.318938E+01 + 3.231019E+01 -1.658446E+01 -3.509668E+01 7.008977E+01 -3.146646E+01 5.013560E+01 -2.329613E+02 -2.217036E+00 7.457045E+02 7.484442E+01 + -4.193614E+01 2.157942E+02 -3.006779E+02 -1.119733E+02 -2.192310E+02 -6.500788E+03 4.085419E+03 3.166502E+03 -1.828357E+03 1.314820E+04 + -6.437989E+04 4.988434E+03 -3.348736E+03 3.017706E+03 -1.876322E+04 1.350289E+05 -2.449548E+04 6.944166E+03 -3.371575E+03 -7.601798E+02 + -1.682832E+09 + 98 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 6.407344E-01 -3.480126E+00 3.500189E+00 -1.064817E+00 5.892924E-01 -1.462151E-01 -4.543488E-01 8.797052E-01 -1.719005E-01 + -6.757681E-01 3.569411E-01 2.311523E-02 -4.936339E-01 1.944256E-01 3.310067E-01 -9.245839E-01 7.764332E-01 5.492281E-01 -2.030393E+00 + 2.167051E+00 -1.442880E+00 -7.933686E-01 9.006487E+00 -3.250111E+01 8.697845E+01 -1.767595E+02 2.440678E+02 -2.072560E+02 1.164944E+02 + -5.639656E+01 2.867699E+01 -1.532634E+01 2.515322E+00 6.821299E+00 -1.420559E+01 2.406720E+01 -3.059400E+01 2.430348E+01 5.730954E+00 + -3.024718E+01 2.309110E+01 2.736382E+01 -4.844930E+01 7.621522E+01 -1.300012E+02 2.115109E+02 1.171640E+02 -3.430135E+02 4.104059E+02 + -2.957022E+02 -3.482603E+02 4.204802E+02 6.561464E+02 -5.805595E+03 -4.525372E+03 -3.569524E+03 -2.790424E+03 1.552073E+03 -9.580256E+03 + 1.541362E+04 -3.007810E+03 -2.666495E+02 -8.284652E+02 3.857663E+04 -1.717468E+05 3.037097E+04 -1.253034E+04 3.630215E+03 2.033368E+03 + 5.610983E+09 + 99 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.362493E+00 5.613787E+00 -4.679650E+00 2.039658E+00 -1.943570E+00 2.128264E+00 -1.351349E+00 2.359194E-01 -4.242656E-01 + 1.271478E+00 -1.270774E+00 1.994177E-01 5.605552E-01 -2.323086E-01 -3.749838E-01 8.521558E-01 -5.293490E-01 -5.287363E-01 2.005709E+00 + -2.250049E+00 1.956036E+00 -6.434131E-01 -4.021885E+00 1.460844E+01 -4.211203E+01 1.045906E+02 -2.072560E+02 2.793662E+02 -2.302376E+02 + 1.365206E+02 -7.055869E+01 3.306834E+01 -1.441480E+01 2.027764E+00 6.791560E+00 -2.769431E+01 4.085236E+01 -2.305991E+01 -1.262041E+00 + 1.986799E+01 -1.206731E+01 -2.666813E+01 7.264574E+00 -2.272039E+01 2.471144E+01 -1.096358E+02 -2.775125E+02 -3.446243E+02 -2.947843E+02 + 4.103668E+02 3.998567E+02 -6.070619E+02 2.714363E+02 2.971980E+03 3.176772E+04 9.446075E+02 1.319542E+02 -7.192400E+02 -4.102157E+02 + 8.094716E+04 -3.961748E+03 8.918110E+03 -3.363930E+03 -4.707364E+04 1.560707E+05 -3.804127E+04 -3.913906E+03 -2.141855E+03 -2.478026E+03 + -1.912656E+09 + 100 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.889708E+00 -4.044814E+00 1.899341E+00 -2.189629E+00 3.107794E+00 -3.125692E+00 3.039935E+00 -2.160518E+00 1.464715E+00 + -1.653768E+00 1.585132E+00 -2.401774E-01 -3.977282E-01 -1.347398E-02 7.674774E-01 -1.144800E+00 1.363889E+00 -9.967295E-01 -4.225636E-01 + 1.289240E+00 -1.111334E+00 -3.784007E-01 3.045841E+00 -6.735196E+00 1.755935E+01 -4.739994E+01 1.164944E+02 -2.302376E+02 3.167620E+02 + -2.766776E+02 1.651254E+02 -7.850900E+01 3.715115E+01 -1.546430E+01 1.144652E+00 1.707656E+01 -3.019534E+01 8.797008E+00 5.782331E+00 + 1.423925E+00 -1.261268E+01 2.160356E+01 2.878760E+01 -5.950956E+01 1.527028E+01 2.861723E+02 1.045303E+02 1.863788E+01 3.454069E+02 + -3.871956E+02 -1.509761E+02 4.160111E+02 -1.804279E+02 -1.896938E+03 -3.853930E+04 -8.576314E+02 3.000708E+03 1.532603E+03 1.408134E+03 + -1.041651E+05 3.994613E+03 -1.106298E+04 1.829244E+03 6.056218E+04 -5.903035E+04 4.093561E+04 1.827667E+04 2.330750E+03 1.804070E+03 + -9.846052E+09 + 101 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -2.851800E+00 4.361568E+00 -1.333804E+00 5.327458E-01 -2.482184E+00 3.280249E+00 -3.512426E+00 3.079795E+00 -1.868243E+00 + 1.471073E+00 -1.620532E+00 3.677152E-01 2.614304E-01 4.955060E-01 -9.726979E-01 9.368653E-01 -1.939667E+00 2.879306E+00 -2.347426E+00 + 1.359036E+00 -1.098954E+00 1.402500E+00 -2.204491E+00 2.718294E+00 -7.436885E+00 2.116840E+01 -5.639656E+01 1.365206E+02 -2.766776E+02 + 3.991638E+02 -3.450469E+02 2.014540E+02 -1.071913E+02 4.774249E+01 -1.502900E+01 -6.037603E+00 4.704924E+00 9.504658E-01 -6.209425E+00 + -3.641904E+01 7.016954E+01 -5.903142E+01 -6.069709E+01 3.786157E+01 1.736153E+02 -4.779726E+02 5.893388E+01 9.929862E+01 -8.238240E+02 + -1.388146E+02 4.255090E+01 -6.997927E+01 1.377563E+02 1.245133E+03 1.417652E+04 3.296527E+03 -9.734806E+02 -1.350187E+03 -3.681512E+03 + 6.484304E+04 -4.778579E+03 4.256756E+03 -1.745047E+03 -6.210130E+03 -8.447996E+03 -9.823108E+03 -2.594617E+04 -3.635932E+03 -5.444135E+02 + 1.347476E+10 + 102 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 3.590311E+00 -5.975894E+00 1.664284E+00 7.827930E-01 1.894061E+00 -3.297016E+00 2.514935E+00 -2.096698E+00 1.546355E+00 + -1.025751E+00 9.546860E-01 4.752618E-01 -1.121686E+00 -5.778592E-01 1.104167E+00 -4.373192E-01 1.455131E+00 -2.807035E+00 2.265876E+00 + -9.856939E-01 1.266082E+00 -2.210040E+00 1.863006E+00 -9.745586E-01 3.623784E+00 -1.204668E+01 2.867699E+01 -7.055869E+01 1.651254E+02 + -3.450469E+02 5.137229E+02 -4.625364E+02 2.947674E+02 -1.575941E+02 7.093127E+01 -2.083715E+01 1.425389E+01 1.087626E+01 -1.394005E+01 + 4.907977E+01 -8.712778E+01 4.831517E+01 1.328880E+02 -9.674652E+00 -2.796144E+02 6.166880E+02 -5.694269E+02 5.702437E+02 4.031230E+02 + 7.832587E+02 -1.082322E+01 -1.654170E+02 2.351152E+02 -1.627492E+03 1.049986E+04 -9.536459E+03 -8.955780E+02 -1.517952E+03 1.756340E+02 + -5.552252E+04 7.331452E+03 -6.685792E+02 8.850188E+02 -6.425507E+04 -2.656841E+04 -2.699273E+04 2.161546E+04 1.744814E+03 3.748915E+02 + -2.700950E+09 + 103 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -5.300153E+00 9.512309E+00 -5.713790E+00 2.673552E+00 -3.453058E+00 2.868235E+00 -9.967034E-01 8.814941E-01 -1.103229E+00 + 2.032663E-01 3.575261E-01 -2.344946E+00 2.540760E+00 1.213741E-01 -9.369902E-01 3.765863E-01 -9.420829E-01 1.294987E+00 7.239198E-01 + -3.257664E+00 2.584711E+00 2.620744E-01 -2.640536E-01 -9.292020E-01 -1.980541E+00 7.472174E+00 -1.532634E+01 3.306834E+01 -7.850900E+01 + 2.014540E+02 -4.625364E+02 7.269151E+02 -6.673002E+02 4.222006E+02 -2.323202E+02 1.077343E+02 -6.412790E+01 3.610996E+00 1.529172E+01 + -2.086651E+01 -2.107922E+00 5.252248E+01 -1.470078E+02 -1.584725E+02 3.783136E+02 -7.610144E+02 1.937198E+02 -2.789851E+02 -3.029180E+02 + -1.702694E+03 3.299540E+01 4.336502E+02 1.217942E+03 -1.221607E+04 -1.080284E+04 2.175221E+04 3.509317E+03 2.649269E+03 1.362041E+04 + -4.185124E+04 -5.780777E+03 2.179865E+03 -5.771348E+02 4.616076E+04 1.042937E+05 3.082986E+04 -5.663342E+03 2.246437E+02 -4.400167E+02 + 1.847328E+09 + 104 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.519256E+00 -1.084049E+01 5.574121E+00 -1.859327E+00 2.973487E+00 -9.069520E-01 8.687322E-04 -2.152709E+00 2.038366E+00 + 1.546330E-01 -1.227406E+00 3.683395E+00 -3.064710E+00 1.041657E-01 5.920478E-01 2.180642E-01 -1.430927E-01 4.643046E-01 -2.967454E+00 + 5.706895E+00 -5.519831E+00 2.072738E+00 -1.302829E+00 2.922395E+00 -3.405810E+00 2.043427E+00 2.515322E+00 -1.441480E+01 3.715115E+01 + -1.071913E+02 2.947674E+02 -6.673002E+02 1.047450E+03 -9.484975E+02 5.994794E+02 -3.336361E+02 1.763347E+02 -5.332878E+01 -1.132156E+00 + 4.960922E+01 1.893174E+01 -9.060892E+01 1.198113E+02 2.720706E+02 -5.979361E+02 1.319834E+03 -4.610896E+02 -1.068009E+02 1.078423E+03 + 1.346629E+03 -5.509681E+01 -3.087377E+01 -4.178231E+02 3.394097E+04 6.510151E+04 -3.070746E+04 -6.618977E+03 -4.971896E+02 -8.403299E+03 + 1.937597E+04 2.058916E+04 -1.500361E+04 3.064194E+03 -9.662258E+03 -1.084352E+05 -2.392207E+04 2.135388E+04 -1.906566E+03 1.086874E+03 + -3.982927E+09 + 105 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -5.594707E+00 9.846266E+00 -1.295855E-01 -5.205138E+00 3.848749E-01 -4.756834E-01 1.368216E+00 2.052247E+00 -1.242794E+00 + -1.592562E+00 2.295109E+00 -3.160148E+00 2.002740E+00 -1.719829E-01 -1.100781E-01 -8.083793E-01 8.404376E-01 -1.211886E+00 2.067934E+00 + -2.843533E+00 3.208913E+00 -1.070171E+00 8.015524E-01 -1.550081E+00 3.353636E+00 -7.646568E+00 6.821299E+00 2.027764E+00 -1.546430E+01 + 4.774249E+01 -1.575941E+02 4.222006E+02 -9.484975E+02 1.439071E+03 -1.277578E+03 8.203547E+02 -4.758598E+02 1.694137E+02 -3.738581E+01 + -5.956544E+01 -1.619844E+01 9.979108E+01 -5.542591E+01 -4.738105E+02 1.212378E+03 -1.847200E+03 9.779069E+02 2.715820E+02 -1.845348E+03 + -1.906643E+02 -5.710465E+02 -3.020143E+02 7.331526E+02 -3.148178E+04 -9.913414E+04 2.237396E+04 5.932526E+03 -3.571042E+03 -9.099882E+03 + 1.037695E+04 -2.836817E+04 1.991000E+04 -8.430865E+03 -9.273308E+03 1.238098E+05 7.606331E+03 -5.265430E+04 9.850241E+03 -5.338632E+02 + 1.081086E+10 + 106 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.591497E+00 -6.917186E+00 -5.362100E+00 8.970454E+00 -3.924179E+00 3.808312E+00 -3.071044E+00 -7.783751E-01 -3.069713E-01 + 2.773412E+00 -2.383268E+00 2.045975E+00 -1.037105E+00 4.571519E-01 -4.256646E-01 1.454587E+00 -2.502593E+00 2.965930E+00 -2.024476E+00 + 1.756818E+00 -1.395772E+00 -9.543020E-01 -1.220812E+00 2.173583E+00 -9.991754E-01 9.198205E+00 -1.420559E+01 6.791560E+00 1.144652E+00 + -1.502900E+01 7.093127E+01 -2.323202E+02 5.994794E+02 -1.277578E+03 1.988638E+03 -1.895315E+03 1.250015E+03 -6.119610E+02 2.262035E+02 + -4.244542E+01 3.758298E+01 -1.966193E+01 -1.111113E+02 3.515588E+02 -1.596200E+03 2.380312E+03 -1.276498E+03 -1.803595E+03 1.424406E+03 + -2.037035E+03 9.432233E+02 4.508485E+02 5.222879E+02 2.903330E+04 1.536625E+05 -1.272986E+04 -6.175376E+03 5.841044E+03 8.638331E+03 + 7.573069E+04 7.320523E+03 -8.708366E+03 -4.986270E+03 1.324122E+04 6.240925E+04 9.358219E+03 9.755494E+04 -1.592101E+04 2.592584E+03 + -2.681108E+09 + 107 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -6.699553E+00 4.564477E+00 9.862980E+00 -1.475968E+01 7.530721E+00 -3.463534E+00 2.601257E+00 1.062597E+00 2.139844E-01 + -2.583224E+00 3.351786E+00 -3.195313E+00 2.354987E+00 -1.119524E+00 1.403408E-01 -2.763730E+00 5.265928E+00 -6.812030E+00 7.725208E+00 + -8.190679E+00 5.640535E+00 -5.093219E-01 2.662290E+00 -1.570711E+00 -1.915668E+00 -9.975410E+00 2.406720E+01 -2.769431E+01 1.707656E+01 + -6.037603E+00 -2.083715E+01 1.077343E+02 -3.336361E+02 8.203547E+02 -1.895315E+03 3.290730E+03 -3.178028E+03 1.896052E+03 -7.905415E+02 + 2.123558E+02 -1.460814E+02 1.971976E+01 2.643631E+02 -3.714884E+02 1.364911E+03 -2.249885E+03 1.651244E+03 2.114626E+03 2.352372E+02 + 2.105628E+03 -9.541981E+02 -5.295426E+02 1.893550E+03 -4.804015E+04 -1.388794E+05 -2.053464E+04 6.162784E+03 -5.219576E+03 -1.643761E+03 + -2.490521E+04 8.012056E+03 3.498073E+02 8.859534E+03 8.254992E+04 1.831417E+03 -1.258878E+05 -1.887335E+04 -1.648762E+03 -4.377881E+03 + 2.273263E+09 + 108 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.473265E+01 -1.726626E+01 8.154522E+00 -9.516916E-01 8.450729E-01 -4.812410E+00 1.974122E+00 -1.125384E+00 -4.169366E-02 + -1.906867E-01 1.008337E+00 9.660869E-01 -3.643742E+00 1.947940E+00 8.313618E-01 3.413254E+00 -4.712694E+00 6.818532E+00 -1.395630E+01 + 1.599803E+01 -1.108076E+01 5.983850E+00 -3.896502E+00 -3.222004E-01 1.589369E+00 9.579497E+00 -3.059400E+01 4.085236E+01 -3.019534E+01 + 4.704924E+00 1.425389E+01 -6.412790E+01 1.763347E+02 -4.758598E+02 1.250015E+03 -3.178028E+03 5.596393E+03 -4.986022E+03 2.607497E+03 + -1.024909E+03 4.624049E+02 -2.816063E+02 -4.003051E+02 1.051129E+03 -1.594784E+03 2.224115E+03 -1.374546E+03 -3.424242E+02 3.191424E+02 + 2.922654E+02 6.453343E+02 -7.867163E+01 7.060501E+02 1.017195E+04 2.657832E+05 3.956424E+04 2.516594E+02 -4.528704E+03 3.676688E+04 + -4.960879E+04 1.286698E+04 4.061519E+03 1.103768E+04 -1.542612E+05 -7.926240E+04 1.472546E+05 -1.021047E+05 1.393661E+04 4.718591E+03 + -4.890574E+09 + 109 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.359472E+01 1.199410E+01 -8.701416E+00 7.793588E+00 -2.544829E+00 4.729261E+00 -3.277762E+00 -1.032324E+00 2.423057E+00 + -3.480070E+00 1.847585E+00 1.060056E-03 -1.238514E-01 4.048853E-01 -2.002193E+00 -2.014014E-01 -3.142730E-02 -1.719074E+00 9.702924E+00 + -1.151871E+01 4.821607E+00 -4.467827E+00 5.206728E+00 -4.304165E+00 2.241602E+00 -9.137078E+00 2.430348E+01 -2.305991E+01 8.797008E+00 + 9.504658E-01 1.087626E+01 3.610996E+00 -5.332878E+01 1.694137E+02 -6.119610E+02 1.896052E+03 -4.986022E+03 8.288123E+03 -6.782318E+03 + 3.320443E+03 -1.568591E+03 6.878888E+02 2.780198E+02 -1.359007E+03 1.892916E+03 1.408362E+02 -2.075906E+03 5.255369E+01 1.609289E+03 + -9.229468E+02 -1.393031E+03 1.463678E+03 7.373624E+02 3.469571E+03 -1.265000E+05 2.567980E+04 -1.841794E+04 2.654065E+04 -4.621177E+04 + -1.704740E+05 -1.087612E+03 -2.924317E+04 -4.267412E+03 1.489596E+05 4.082989E+05 -1.434047E+05 1.243994E+05 -2.008351E+04 -8.740382E+03 + 4.331904E+09 + 110 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.403214E+00 6.659039E+00 -7.902574E+00 1.571011E+00 -9.160170E+00 9.575884E+00 -5.410748E+00 2.425718E+00 1.194605E+00 + 1.532490E+00 -2.669702E+00 -6.257466E-01 2.262415E+00 -5.012848E+00 6.054013E+00 -4.463190E+00 5.165212E+00 -7.426570E+00 8.188151E+00 + -7.678424E+00 1.331209E+01 -1.286836E+01 1.230989E+01 -1.173554E+01 1.900559E+01 -2.318938E+01 5.730954E+00 -1.262041E+00 5.782331E+00 + -6.209425E+00 -1.394005E+01 1.529172E+01 -1.132156E+00 -3.738581E+01 2.262035E+02 -7.905415E+02 2.607497E+03 -6.782318E+03 1.090031E+04 + -8.442558E+03 4.338236E+03 -1.566721E+03 -1.866990E+01 1.425964E+03 -1.321691E+03 -1.402470E+03 4.926115E+03 -6.576266E+03 1.067776E+03 + -3.556283E+02 1.290522E+02 -5.882231E+02 2.095565E+03 -4.119700E+04 -2.480852E+04 -3.981109E+04 1.777133E+04 -1.460012E+04 3.808632E+04 + 4.324108E+05 -3.906816E+04 2.320426E+04 -4.665629E+03 9.866315E+04 -6.203682E+05 1.564103E+05 3.444726E+04 9.698153E+03 1.246628E+04 + -3.308641E+10 + 111 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.083065E+00 -2.245394E+01 3.100598E+01 -2.263699E+01 1.815334E+01 -1.204950E+01 7.173906E+00 -3.220482E+00 -4.412651E+00 + 5.457098E+00 -2.158579E+00 1.222577E+00 -1.138805E+00 1.979662E+00 -1.404108E+00 -4.454228E-01 -1.665202E+00 7.941997E+00 -1.044721E+01 + 7.928579E+00 -7.872703E+00 3.089821E+00 -2.823881E+00 1.022493E+01 -2.364898E+01 3.231019E+01 -3.024718E+01 1.986799E+01 1.423925E+00 + -3.641904E+01 4.907977E+01 -2.086651E+01 4.960922E+01 -5.956544E+01 -4.244542E+01 2.123558E+02 -1.024909E+03 3.320443E+03 -8.442558E+03 + 1.465014E+04 -1.286772E+04 5.088320E+03 -1.563554E+03 9.740500E+02 6.206046E+01 1.308716E+03 -7.898050E+03 9.197065E+03 1.348746E+03 + 1.544831E+03 1.110091E+03 -1.907537E+03 2.284452E+03 -1.742297E+04 1.953588E+05 3.818460E+04 -1.065487E+04 -1.662024E+04 1.544020E+04 + -4.088869E+05 1.882536E+04 -1.829486E+04 -8.295041E+03 1.486799E+05 2.115581E+05 2.808116E+04 -7.437905E+04 6.121624E+02 -9.953999E+03 + 1.470977E+10 + 112 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 9.539215E+00 5.118478E+00 -2.592302E+01 3.509755E+01 -1.700810E+01 -9.554021E-01 -4.196525E+00 -3.899860E-01 6.900245E+00 + -5.971793E+00 2.538125E+00 -1.516956E+00 -2.523607E+00 1.215307E+01 -1.675832E+01 1.367210E+01 -4.136923E+00 -7.022544E+00 3.713452E+00 + 7.173742E+00 -6.358489E+00 7.691226E+00 -1.935197E+01 2.591605E+01 -6.437292E+00 -1.658446E+01 2.309110E+01 -1.206731E+01 -1.261268E+01 + 7.016954E+01 -8.712778E+01 -2.107922E+00 1.893174E+01 -1.619844E+01 3.758298E+01 -1.460814E+02 4.624049E+02 -1.568591E+03 4.338236E+03 + -1.286772E+04 2.604052E+04 -1.928682E+04 7.530732E+03 -6.072264E+03 3.394589E+03 -1.940260E+03 1.089992E+04 -7.521836E+03 2.913572E+03 + 3.130854E+03 -2.493931E+03 5.785720E+03 -1.376393E+04 1.586160E+05 -4.415572E+05 -7.091855E+04 1.165212E+03 2.533722E+04 -3.720512E+04 + 5.533454E+05 2.699028E+04 5.034268E+04 1.993825E+04 -3.400682E+05 2.096216E+05 5.772460E+04 -1.098534E+05 -4.654542E+03 4.246566E+03 + -3.977980E+09 + 113 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.016561E+01 1.213092E+01 -1.872338E+01 1.014422E+01 -7.592798E+00 1.378721E+00 4.697492E+00 1.638043E+01 -1.371532E+01 + 7.358425E+00 -4.594258E+00 -1.907377E+00 1.239100E+01 -1.948561E+01 1.712978E+01 -1.394132E+01 9.631003E+00 4.496766E+00 -1.854452E+00 + -5.558695E+00 3.117822E+00 -5.412077E-01 1.925404E+01 -5.001637E+01 5.809936E+01 -3.509668E+01 2.736382E+01 -2.666813E+01 2.160356E+01 + -5.903142E+01 4.831517E+01 5.252248E+01 -9.060892E+01 9.979108E+01 -1.966193E+01 1.971976E+01 -2.816063E+02 6.878888E+02 -1.566721E+03 + 5.088320E+03 -1.928682E+04 4.106466E+04 -2.997421E+04 1.493225E+04 -9.052282E+03 1.775456E+03 -2.239064E+03 3.639743E+03 -2.827100E+03 + 8.282204E+02 2.119918E+02 -6.487409E+03 1.608710E+04 -1.335516E+05 4.747339E+05 9.450544E+04 7.491935E+04 -3.694427E+04 2.257939E+05 + -8.161811E+05 7.822465E+04 -1.480308E+05 5.300044E+03 1.231217E+05 -8.220311E+05 -1.614718E+05 1.501792E+05 2.032313E+04 3.781362E+03 + -9.531373E+10 + 114 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.232596E+01 -2.608280E+01 1.518818E+01 -2.478277E+01 1.959658E+01 4.926406E+00 4.640563E+00 -2.683526E+01 2.056494E+01 + -2.166726E+01 1.066606E+01 -6.612488E-02 -8.214446E+00 7.337500E+00 -1.947113E+00 5.160341E+00 -1.344280E+01 6.080430E+00 -9.048290E+00 + 1.545364E+01 -1.516013E+01 7.991160E+00 -1.902966E+01 5.033392E+01 -7.214668E+01 7.008977E+01 -4.844930E+01 7.264574E+00 2.878760E+01 + -6.069709E+01 1.328880E+02 -1.470078E+02 1.198113E+02 -5.542591E+01 -1.111113E+02 2.643631E+02 -4.003051E+02 2.780198E+02 -1.866990E+01 + -1.563554E+03 7.530732E+03 -2.997421E+04 6.505996E+04 -5.727616E+04 3.021932E+04 -6.807133E+03 -3.259211E+03 -1.532315E+03 1.468568E+03 + 1.603058E+03 -6.474331E+02 -5.897789E+02 1.690439E+04 6.106403E+04 -1.735202E+04 -1.816055E+05 -4.927276E+03 2.307512E+04 -2.270594E+05 + 1.076868E+06 -1.537734E+05 6.642176E+04 9.716737E+02 1.380979E+05 7.004082E+05 1.343689E+05 -1.430746E+05 -1.174548E+03 1.234368E+04 + 1.500482E+11 + 115 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 7.307891E+00 -1.058222E+02 1.643158E+02 -9.389696E+01 2.597159E+01 -1.312607E+01 2.718056E-02 1.777019E+01 -1.928702E+01 + 9.476164E+00 -4.820371E+00 4.528456E+00 -6.255482E+00 1.288553E+01 -2.048324E+01 2.397757E+01 -1.969291E+00 -2.701283E+01 5.535608E+01 + -8.254252E+01 9.244412E+01 -5.338542E+01 1.306526E+01 -1.075514E+00 1.211168E+00 -3.146646E+01 7.621522E+01 -2.272039E+01 -5.950956E+01 + 3.786157E+01 -9.674652E+00 -1.584725E+02 2.720706E+02 -4.738105E+02 3.515588E+02 -3.714884E+02 1.051129E+03 -1.359007E+03 1.425964E+03 + 9.740500E+02 -6.072264E+03 1.493225E+04 -5.727616E+04 1.802302E+05 -1.795588E+05 6.510649E+04 -2.122284E+04 5.171198E+04 1.933207E+04 + -8.564838E+03 1.279786E+03 1.024719E+04 2.907584E+02 -1.047661E+05 6.323083E+05 2.778223E+05 1.069648E+04 -3.259531E+04 2.940672E+05 + -1.752870E+06 1.235912E+05 4.633500E+04 1.071234E+05 8.737466E+03 -6.117024E+05 1.147976E+05 -2.342603E+05 8.215990E+04 -5.739302E+03 + -2.380336E+11 + 116 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 3.665296E+01 8.774615E+01 -2.313598E+02 2.242806E+02 -1.188872E+02 2.360821E+01 -2.665036E+00 -1.727882E+01 1.299260E+01 + 2.588599E+00 1.607473E+01 -9.716470E+00 -1.097496E+01 -1.215181E+01 6.404360E+01 -8.354912E+01 7.065907E+01 -1.912812E+01 -5.910355E+01 + 1.141918E+02 -1.308596E+02 9.113084E+01 -2.928014E+01 -1.365619E+01 -3.043495E+01 5.013560E+01 -1.300012E+02 2.471144E+01 1.527028E+01 + 1.736153E+02 -2.796144E+02 3.783136E+02 -5.979361E+02 1.212378E+03 -1.596200E+03 1.364911E+03 -1.594784E+03 1.892916E+03 -1.321691E+03 + 6.206046E+01 3.394589E+03 -9.052282E+03 3.021932E+04 -1.795588E+05 6.045134E+05 -4.746878E+05 1.738268E+05 -1.922035E+05 -6.222349E+04 + -2.659902E+04 -2.570118E+04 -6.498991E+03 4.071402E+04 -2.048542E+05 -1.566050E+06 -3.365112E+05 4.649400E+04 1.460078E+05 -1.341326E+05 + 5.780233E+06 -4.149109E+05 3.387264E+04 -2.186786E+05 -9.565380E+05 4.415939E+06 -1.340890E+06 7.363073E+05 -1.694051E+05 3.103474E+04 + 3.298146E+11 + 117 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.518565E+01 -1.823254E+02 3.356966E+02 -1.131105E+02 -8.678461E+01 1.731121E+01 4.928548E+01 -9.952654E+01 8.698668E+01 + -4.326368E+01 1.746965E+01 1.929872E+01 -4.531552E+01 5.913247E+01 -8.440093E+01 7.773148E+01 -1.241798E+02 9.005819E+01 -2.119608E+01 + -1.111278E+01 -1.666907E+01 1.235436E+02 -1.314907E+02 1.008995E+02 9.591743E+01 -2.329613E+02 2.115109E+02 -1.096358E+02 2.861723E+02 + -4.779726E+02 6.166880E+02 -7.610144E+02 1.319834E+03 -1.847200E+03 2.380312E+03 -2.249885E+03 2.224115E+03 1.408362E+02 -1.402470E+03 + 1.308716E+03 -1.940260E+03 1.775456E+03 -6.807133E+03 6.510649E+04 -4.746878E+05 1.961692E+06 -1.766532E+06 1.195745E+06 -4.403240E+04 + -9.257646E+04 9.096062E+04 1.096524E+05 1.527942E+05 -3.024750E+05 1.439886E+06 5.305061E+05 4.717110E+05 -2.440994E+05 -4.359072E+05 + -5.696915E+06 5.693790E+05 2.422350E+05 4.806313E+05 -1.532426E+06 -2.961359E+06 8.674103E+05 -2.752154E+05 1.686052E+05 5.023135E+04 + 3.361380E+10 + 118 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 7.337356E+01 -2.093618E+02 -1.441459E+01 5.274466E+01 2.644518E+02 -2.962084E+02 8.676544E+01 2.950475E+01 -3.540788E+01 + 7.144614E+01 3.859109E+01 -1.759902E+02 1.302714E+02 -4.015745E+01 1.117223E+02 -1.928548E+02 2.679607E+02 -3.374024E+02 2.421098E+02 + -1.137972E+02 1.050738E+02 -2.548116E+01 -5.484108E+01 2.452648E+02 -1.734722E+02 -2.217036E+00 1.171640E+02 -2.775125E+02 1.045303E+02 + 5.893388E+01 -5.694269E+02 1.937198E+02 -4.610896E+02 9.779069E+02 -1.276498E+03 1.651244E+03 -1.374546E+03 -2.075906E+03 4.926115E+03 + -7.898050E+03 1.089992E+04 -2.239064E+03 -3.259211E+03 -2.122284E+04 1.738268E+05 -1.766532E+06 8.808296E+06 -6.726932E+06 1.409099E+06 + -7.499856E+05 -1.634105E+05 -4.022754E+05 1.120810E+06 -2.061758E+06 1.214678E+07 5.845712E+05 -3.766001E+05 2.588542E+05 1.704817E+06 + 2.888708E+06 -4.818178E+05 5.320601E+04 2.370119E+05 3.385872E+06 5.378734E+06 -2.696572E+05 5.109406E+05 -5.378785E+05 -3.553270E+05 + -1.396714E+12 + 119 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.000328E+02 3.697249E+01 -5.467423E+02 9.268111E+02 -8.059355E+02 4.753085E+02 -1.289345E+02 -1.055756E+00 5.276734E+01 + -1.280378E+02 8.893844E+01 4.170823E+01 5.366412E+01 -6.714592E+01 -3.734380E+02 4.859404E+02 -2.658960E+02 2.762210E+02 -1.782673E+02 + -7.863710E+01 1.354962E+02 -3.056932E+02 3.458206E+02 -4.420818E+02 2.646349E+01 7.457045E+02 -3.430135E+02 -3.446243E+02 1.863788E+01 + 9.929862E+01 5.702437E+02 -2.789851E+02 -1.068009E+02 2.715820E+02 -1.803595E+03 2.114626E+03 -3.424242E+02 5.255369E+01 -6.576266E+03 + 9.197065E+03 -7.521836E+03 3.639743E+03 -1.532315E+03 5.171198E+04 -1.922035E+05 1.195745E+06 -6.726932E+06 2.293172E+07 -5.639918E+06 + 1.981716E+05 -2.283982E+05 9.596920E+04 -5.513488E+05 -1.205600E+06 -3.499760E+07 -9.031445E+05 1.971731E+06 5.943514E+05 2.932715E+04 + -3.525799E+07 6.202588E+05 -1.695734E+06 1.476643E+06 -7.893853E+06 -1.031977E+07 -1.214858E+07 -1.687122E+06 6.997242E+05 3.960724E+05 + 1.011583E+12 + 120 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 9.493193E+01 1.351757E+02 1.317463E+00 -3.618302E+02 2.802296E+02 1.781068E+01 -1.273469E+02 -1.214492E+01 1.917612E+02 + -8.787338E+01 -5.442955E+01 4.172413E+01 2.579762E+01 -7.780202E+01 1.986700E+02 -3.684509E+02 2.369038E+02 -2.820773E+02 3.564970E+02 + -1.953395E+02 2.154044E+02 5.491741E+01 -5.507850E+02 4.881042E+02 -2.375694E+02 7.484442E+01 4.104059E+02 -2.947843E+02 3.454069E+02 + -8.238240E+02 4.031230E+02 -3.029180E+02 1.078423E+03 -1.845348E+03 1.424406E+03 2.352372E+02 3.191424E+02 1.609289E+03 1.067776E+03 + 1.348746E+03 2.913572E+03 -2.827100E+03 1.468568E+03 1.933207E+04 -6.222349E+04 -4.403240E+04 1.409099E+06 -5.639918E+06 1.571905E+07 + -6.053250E+06 1.187656E+06 1.407487E+05 4.250018E+06 -5.871151E+06 7.217269E+06 1.471576E+06 1.540848E+05 5.366644E+05 2.030119E+06 + 1.271171E+07 -5.399207E+05 -2.141250E+05 1.715902E+05 -3.027986E+06 -2.752652E+07 -1.758505E+06 4.162108E+06 -5.722210E+05 -1.772294E+05 + -1.040400E+12 + 121 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 9.498857E+01 -2.840224E+02 5.830362E+02 -2.115372E+02 3.149788E+02 -2.979692E+02 8.952377E+01 1.870364E+02 -7.471504E+01 + -6.472532E+01 -8.843206E+01 1.783435E+02 -1.278148E+02 2.619959E+02 -1.761980E+02 1.620329E+02 -1.549351E+02 4.275198E+02 -5.957591E+02 + 3.402851E+02 -3.763714E+01 -1.641856E+02 2.875098E+02 -2.075225E+02 7.978937E+00 -4.193614E+01 -2.957022E+02 4.103668E+02 -3.871956E+02 + -1.388146E+02 7.832587E+02 -1.702694E+03 1.346629E+03 -1.906643E+02 -2.037035E+03 2.105628E+03 2.922654E+02 -9.229468E+02 -3.556283E+02 + 1.544831E+03 3.130854E+03 8.282204E+02 1.603058E+03 -8.564838E+03 -2.659902E+04 -9.257646E+04 -7.499856E+05 1.981716E+05 -6.053250E+06 + 3.929908E+07 -7.793428E+06 2.784212E+06 -1.149788E+07 1.991444E+07 -1.722509E+07 -8.998423E+05 -1.243492E+06 2.912682E+05 -8.607728E+05 + 1.336705E+07 -1.246895E+06 1.634312E+06 -1.186183E+06 4.106430E+06 7.610645E+05 -3.002345E+06 -7.674368E+06 1.514790E+06 -1.162551E+05 + 2.185426E+12 + 122 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -3.709506E+02 2.454277E+02 5.830626E+01 -4.041853E+02 6.255397E+02 -4.559089E+02 2.451967E+02 -1.528717E+02 1.630133E+02 + -1.072102E+02 2.626234E+01 -9.876409E+01 1.104136E+02 -5.880917E+01 -4.623707E+00 1.356925E+02 -1.577786E+02 7.757200E+00 3.316424E+01 + -4.274419E+01 1.320352E+00 8.979841E+01 -1.877299E+02 2.268980E+02 -2.089212E+02 2.157942E+02 -3.482603E+02 3.998567E+02 -1.509761E+02 + 4.255090E+01 -1.082322E+01 3.299540E+01 -5.509681E+01 -5.710465E+02 9.432233E+02 -9.541981E+02 6.453343E+02 -1.393031E+03 1.290522E+02 + 1.110091E+03 -2.493931E+03 2.119918E+02 -6.474331E+02 1.279786E+03 -2.570118E+04 9.096062E+04 -1.634105E+05 -2.283982E+05 1.187656E+06 + -7.793428E+06 2.009940E+07 -1.870795E+07 4.917337E+06 3.517345E+06 -1.340567E+07 1.494337E+06 -2.265180E+03 -1.048255E+06 -6.575396E+05 + 9.206850E+06 -3.282151E+06 2.120652E+06 -7.827223E+04 -1.121863E+07 1.281150E+07 -4.905031E+06 3.916428E+06 1.058513E+05 -1.601605E+05 + 8.334480E+11 + 123 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 6.115176E+02 -4.717429E+02 1.945957E+02 5.217144E+02 -1.200644E+03 1.204189E+03 -7.656313E+02 3.341406E+02 -9.382661E+01 + -1.076341E+02 1.624092E+02 -1.918570E+01 -2.543178E+01 4.991921E+01 -7.888906E+01 -4.538579E+01 2.057444E+01 9.738178E+01 3.435403E+01 + 6.432546E+01 -8.586524E+01 -3.315910E+01 2.683428E+02 -3.875204E+02 5.133332E+02 -3.006779E+02 4.204802E+02 -6.070619E+02 4.160111E+02 + -6.997927E+01 -1.654170E+02 4.336502E+02 -3.087377E+01 -3.020143E+02 4.508485E+02 -5.295426E+02 -7.867163E+01 1.463678E+03 -5.882231E+02 + -1.907537E+03 5.785720E+03 -6.487409E+03 -5.897789E+02 1.024719E+04 -6.498991E+03 1.096524E+05 -4.022754E+05 9.596920E+04 1.407487E+05 + 2.784212E+06 -1.870795E+07 5.542109E+07 -5.309011E+07 6.494180E+07 -2.957132E+07 -1.034514E+07 -3.728036E+06 -3.617263E+05 -3.610619E+06 + 6.570818E+07 1.716878E+06 -1.107064E+06 1.050020E+06 3.225826E+07 -6.191851E+06 1.191279E+07 5.061736E+06 -5.426642E+05 1.863187E+05 + -1.748562E+12 + 124 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.363901E+03 -1.683214E+03 4.913783E+02 -8.448280E+02 1.157734E+03 -8.007334E+02 5.990844E+02 -6.143015E+02 -1.670328E+02 + 6.435095E+02 -4.751142E+02 1.816097E+02 -1.940322E+02 -1.725820E+02 4.391287E+02 -3.221260E+02 4.125940E+02 -3.050634E+02 1.935661E+02 + 4.044198E+01 -3.531100E+01 1.168782E+02 -1.798998E+02 4.378378E+02 -3.592846E+02 -1.119733E+02 6.561464E+02 2.714363E+02 -1.804279E+02 + 1.377563E+02 2.351152E+02 1.217942E+03 -4.178231E+02 7.331526E+02 5.222879E+02 1.893550E+03 7.060501E+02 7.373624E+02 2.095565E+03 + 2.284452E+03 -1.376393E+04 1.608710E+04 1.690439E+04 2.907584E+02 4.071402E+04 1.527942E+05 1.120810E+06 -5.513488E+05 4.250018E+06 + -1.149788E+07 4.917337E+06 -5.309011E+07 1.330046E+08 -2.431120E+08 6.271954E+08 3.110880E+07 5.103911E+05 4.179406E+06 2.780030E+06 + -1.368764E+08 -2.357339E+06 9.217025E+05 3.790016E+05 -6.907843E+06 -1.793524E+07 -4.190840E+06 3.117666E+06 1.832194E+05 2.654273E+05 + -3.729922E+11 + 125 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.297799E+03 -1.580732E+03 6.497093E+03 -4.422236E+03 9.857669E+03 -7.601918E+03 -7.841562E+02 3.772142E+03 -1.413271E+02 + -4.628828E+03 4.030597E+03 -7.780559E+02 1.396882E+03 9.221130E+02 -2.533216E+03 3.480793E+03 -5.785916E+03 3.105022E+03 -3.132300E+02 + 1.199005E+03 -1.601524E+03 -9.875748E+02 5.246911E+03 -1.064628E+04 9.678644E+03 -2.192310E+02 -5.805595E+03 2.971980E+03 -1.896938E+03 + 1.245133E+03 -1.627492E+03 -1.221607E+04 3.394097E+04 -3.148178E+04 2.903330E+04 -4.804015E+04 1.017195E+04 3.469571E+03 -4.119700E+04 + -1.742297E+04 1.586160E+05 -1.335516E+05 6.106403E+04 -1.047661E+05 -2.048542E+05 -3.024750E+05 -2.061758E+06 -1.205600E+06 -5.871151E+06 + 1.991444E+07 3.517345E+06 6.494180E+07 -2.431120E+08 8.621888E+09 -6.290140E+09 -1.928362E+09 -4.909974E+08 -4.407948E+08 -7.457232E+08 + 2.895378E+09 -3.166085E+08 9.262790E+07 -1.156871E+08 -3.008419E+08 4.076503E+08 -7.878276E+07 4.911256E+07 -2.559013E+07 1.856626E+07 + 1.512698E+13 + 126 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.379822E+04 -6.859132E+04 5.494776E+04 -8.695468E+03 -1.325695E+03 6.088795E+03 2.557742E+02 -1.248710E+03 -1.094974E+04 + 1.890950E+04 -1.017130E+04 3.005070E+03 -1.189672E+04 1.087202E+04 -7.114921E+03 -1.008568E+03 2.205960E+03 7.226828E+03 -2.386186E+04 + 2.551522E+04 8.675862E+03 -5.051220E+04 4.819480E+04 -2.210946E+04 7.233295E+03 -6.500788E+03 -4.525372E+03 3.176772E+04 -3.853930E+04 + 1.417652E+04 1.049986E+04 -1.080284E+04 6.510151E+04 -9.913414E+04 1.536625E+05 -1.388794E+05 2.657832E+05 -1.265000E+05 -2.480852E+04 + 1.953588E+05 -4.415572E+05 4.747339E+05 -1.735202E+04 6.323083E+05 -1.566050E+06 1.439886E+06 1.214678E+07 -3.499760E+07 7.217269E+06 + -1.722509E+07 -1.340567E+07 -2.957132E+07 6.271954E+08 -6.290140E+09 9.573854E+10 1.087339E+09 -1.832911E+09 -1.528517E+08 -1.478870E+09 + 4.357939E+09 -5.720422E+08 2.340407E+08 -1.127716E+08 -4.108907E+08 2.568479E+09 7.937546E+07 -6.393310E+08 -2.619539E+07 -1.046318E+07 + 1.424720E+13 + 127 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.015685E+04 1.421956E+04 -8.300154E+03 3.017220E+03 -1.701427E+03 4.326530E+02 3.866084E+03 -3.789426E+03 -3.380024E+01 + 6.849469E+02 1.270380E+03 -9.942502E+02 8.038944E+02 -2.817547E+03 9.126406E+02 -3.601223E+02 -1.784593E+02 1.138020E+03 -6.173485E+02 + -1.077261E+03 1.914792E+03 -8.197669E+02 -2.573317E+03 3.725293E+03 -4.246828E+03 4.085419E+03 -3.569524E+03 9.446075E+02 -8.576314E+02 + 3.296527E+03 -9.536459E+03 2.175221E+04 -3.070746E+04 2.237396E+04 -1.272986E+04 -2.053464E+04 3.956424E+04 2.567980E+04 -3.981109E+04 + 3.818460E+04 -7.091855E+04 9.450544E+04 -1.816055E+05 2.778223E+05 -3.365112E+05 5.305061E+05 5.845712E+05 -9.031445E+05 1.471576E+06 + -8.998423E+05 1.494337E+06 -1.034514E+07 3.110880E+07 -1.928362E+09 1.087339E+09 3.632250E+09 -1.671762E+08 8.426395E+08 7.428751E+08 + -3.522750E+09 3.566665E+08 -3.481172E+07 1.151165E+08 8.125198E+08 -4.532692E+08 4.335439E+07 9.256990E+07 2.832442E+07 -1.561771E+07 + 1.114252E+13 + 128 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -9.603727E+01 -1.575947E+02 -6.932156E+02 7.145837E+02 -6.038124E+02 4.725524E+02 -3.041980E+02 1.222846E+03 -9.464201E+02 + 2.326490E+01 2.938871E+02 -6.108745E+02 9.882161E+02 -3.758754E+02 -2.586743E+02 7.967981E+02 -9.384382E+02 5.561424E+02 1.548472E+03 + -1.989203E+03 -2.105887E+02 2.888234E+03 -4.552303E+03 3.228233E+03 -2.200414E+03 3.166502E+03 -2.790424E+03 1.319542E+02 3.000708E+03 + -9.734806E+02 -8.955780E+02 3.509317E+03 -6.618977E+03 5.932526E+03 -6.175376E+03 6.162784E+03 2.516594E+02 -1.841794E+04 1.777133E+04 + -1.065487E+04 1.165212E+03 7.491935E+04 -4.927276E+03 1.069648E+04 4.649400E+04 4.717110E+05 -3.766001E+05 1.971731E+06 1.540848E+05 + -1.243492E+06 -2.265180E+03 -3.728036E+06 5.103911E+05 -4.909974E+08 -1.832911E+09 -1.671762E+08 1.308187E+09 -1.622816E+08 9.116448E+08 + -2.422398E+09 3.427572E+08 -8.488510E+07 7.196628E+07 2.733953E+08 -5.707024E+07 -1.400609E+08 5.413553E+07 -6.247219E+06 4.823832E+06 + -1.238872E+13 + 129 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 2.682455E+03 2.747194E+03 -9.326368E+03 4.227808E+03 6.634030E+02 7.889897E+01 7.824008E+02 -2.276234E+03 1.540427E+03 + -1.047334E+03 6.105586E+02 -3.229900E+02 -8.381996E+01 -1.633176E+02 -5.084050E+02 8.995150E+02 -2.204258E+02 1.967754E+02 -5.965264E+02 + 3.490192E+02 -6.897391E+01 -7.695368E+02 3.421056E+02 -9.065192E+01 7.644006E+02 -1.828357E+03 1.552073E+03 -7.192400E+02 1.532603E+03 + -1.350187E+03 -1.517952E+03 2.649269E+03 -4.971896E+02 -3.571042E+03 5.841044E+03 -5.219576E+03 -4.528704E+03 2.654065E+04 -1.460012E+04 + -1.662024E+04 2.533722E+04 -3.694427E+04 2.307512E+04 -3.259531E+04 1.460078E+05 -2.440994E+05 2.588542E+05 5.943514E+05 5.366644E+05 + 2.912682E+05 -1.048255E+06 -3.617263E+05 4.179406E+06 -4.407948E+08 -1.528517E+08 8.426395E+08 -1.622816E+08 1.008178E+09 1.534366E+08 + -2.418659E+09 3.578525E+08 -3.196534E+07 1.640528E+07 2.788003E+08 -8.727587E+07 -1.518050E+08 2.322656E+07 1.441081E+07 -2.137042E+06 + -2.673967E+12 + 130 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.703380E+03 3.813890E+03 2.483858E+03 -6.058046E+03 1.332368E+02 -1.489918E+03 6.445081E+02 2.739868E+02 -3.336778E+02 + -4.225195E+02 -5.344456E+02 7.667866E+02 4.238434E+02 -1.369666E+03 7.937531E+02 1.541533E+03 -2.739707E+02 -2.372656E+03 2.897386E+03 + -1.038182E+03 2.958956E+02 1.740138E+03 -2.173702E+03 1.604938E+03 -6.303488E+03 1.314820E+04 -9.580256E+03 -4.102157E+02 1.408134E+03 + -3.681512E+03 1.756340E+02 1.362041E+04 -8.403299E+03 -9.099882E+03 8.638331E+03 -1.643761E+03 3.676688E+04 -4.621177E+04 3.808632E+04 + 1.544020E+04 -3.720512E+04 2.257939E+05 -2.270594E+05 2.940672E+05 -1.341326E+05 -4.359072E+05 1.704817E+06 2.932715E+04 2.030119E+06 + -8.607728E+05 -6.575396E+05 -3.610619E+06 2.780030E+06 -7.457232E+08 -1.478870E+09 7.428751E+08 9.116448E+08 1.534366E+08 4.274620E+09 + -1.223555E+10 2.334862E+09 -6.180888E+08 3.757291E+08 2.074666E+09 -1.058727E+09 1.857014E+08 4.240602E+08 4.074006E+06 -7.863066E+06 + 7.580179E+12 + 131 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.232404E+04 1.140713E+03 -1.064864E+04 4.757627E+04 -6.498640E+04 4.331207E+04 -3.866014E+04 2.970968E+04 8.158217E+03 + -1.787166E+04 1.401600E+04 4.360266E+03 -2.141545E+04 5.201526E+03 5.163750E+03 -9.183887E+03 1.232222E+04 -7.460296E+03 2.275210E+03 + -3.866104E+04 4.310150E+04 -2.063600E+04 1.327436E+04 -1.275012E+03 4.132396E+04 -6.437989E+04 1.541362E+04 8.094716E+04 -1.041651E+05 + 6.484304E+04 -5.552252E+04 -4.185124E+04 1.937597E+04 1.037695E+04 7.573069E+04 -2.490521E+04 -4.960879E+04 -1.704740E+05 4.324108E+05 + -4.088869E+05 5.533454E+05 -8.161811E+05 1.076868E+06 -1.752870E+06 5.780233E+06 -5.696915E+06 2.888708E+06 -3.525799E+07 1.271171E+07 + 1.336705E+07 9.206850E+06 6.570818E+07 -1.368764E+08 2.895378E+09 4.357939E+09 -3.522750E+09 -2.422398E+09 -2.418659E+09 -1.223555E+10 + 3.533370E+11 -1.005617E+10 7.758088E+09 -2.357945E+09 -1.255470E+10 5.131416E+09 -1.091736E+09 -2.681768E+09 -2.165741E+08 8.365345E+07 + 2.861435E+14 + 132 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 1.389880E+03 -2.849021E+03 -1.145550E+03 -4.150261E+02 3.949788E+03 -2.351620E+03 -2.930666E+03 4.531346E+03 -3.337784E+03 + 5.588285E+02 -1.222052E+03 3.414251E+02 -2.178472E+02 6.314663E+02 -9.460009E+02 4.341874E+03 -3.659124E+03 8.231108E+02 7.801686E+02 + -7.258699E+02 4.388912E+02 -1.369895E+03 5.168130E+03 -5.069796E+03 9.248749E+02 4.988434E+03 -3.007810E+03 -3.961748E+03 3.994613E+03 + -4.778579E+03 7.331452E+03 -5.780777E+03 2.058916E+04 -2.836817E+04 7.320523E+03 8.012056E+03 1.286698E+04 -1.087612E+03 -3.906816E+04 + 1.882536E+04 2.699028E+04 7.822465E+04 -1.537734E+05 1.235912E+05 -4.149109E+05 5.693790E+05 -4.818178E+05 6.202588E+05 -5.399207E+05 + -1.246895E+06 -3.282151E+06 1.716878E+06 -2.357339E+06 -3.166085E+08 -5.720422E+08 3.566665E+08 3.427572E+08 3.578525E+08 2.334862E+09 + -1.005617E+10 6.773738E+09 -8.664659E+08 1.266310E+09 4.699328E+09 -2.073443E+09 1.137779E+09 6.702360E+08 6.509657E+07 4.641715E+06 + -5.573845E+13 + 133 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 4.807345E+03 -4.815780E+03 -1.180215E+03 2.351170E+03 -1.372720E+03 2.481499E+03 -2.943263E+03 2.210908E+03 -1.244702E+02 + -1.537897E+03 1.404298E+03 -9.475956E+01 -1.130880E+02 5.412367E+02 -9.034876E+02 -1.705300E+01 7.826650E+02 6.588412E+02 -1.366654E+03 + -8.057092E+02 3.308988E+03 -3.981366E+03 2.747692E+03 -1.332248E+03 3.027524E+03 -3.348736E+03 -2.666495E+02 8.918110E+03 -1.106298E+04 + 4.256756E+03 -6.685792E+02 2.179865E+03 -1.500361E+04 1.991000E+04 -8.708366E+03 3.498073E+02 4.061519E+03 -2.924317E+04 2.320426E+04 + -1.829486E+04 5.034268E+04 -1.480308E+05 6.642176E+04 4.633500E+04 3.387264E+04 2.422350E+05 5.320601E+04 -1.695734E+06 -2.141250E+05 + 1.634312E+06 2.120652E+06 -1.107064E+06 9.217025E+05 9.262790E+07 2.340407E+08 -3.481172E+07 -8.488510E+07 -3.196534E+07 -6.180888E+08 + 7.758088E+09 -8.664659E+08 2.583221E+09 -3.629426E+08 -6.021227E+09 2.805703E+09 -1.098607E+09 -5.994466E+08 -3.358237E+07 -8.301872E+05 + 3.657506E+13 + 134 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.163300E+03 3.222601E+03 -8.485626E+03 6.279030E+03 -4.494017E+02 1.018263E+03 -1.303440E+03 1.798344E+03 -2.638486E+03 + 8.002300E+02 5.031056E+02 -8.738278E+02 1.318379E+03 -1.119849E+03 -8.235062E+02 2.617194E+03 -2.939794E+03 1.841638E+03 -1.393757E+03 + 2.871460E+03 -1.730892E+03 -2.020247E+02 4.829861E+02 -2.474086E+02 -1.706040E+03 3.017706E+03 -8.284652E+02 -3.363930E+03 1.829244E+03 + -1.745047E+03 8.850188E+02 -5.771348E+02 3.064194E+03 -8.430865E+03 -4.986270E+03 8.859534E+03 1.103768E+04 -4.267412E+03 -4.665629E+03 + -8.295041E+03 1.993825E+04 5.300044E+03 9.716737E+02 1.071234E+05 -2.186786E+05 4.806313E+05 2.370119E+05 1.476643E+06 1.715902E+05 + -1.186183E+06 -7.827223E+04 1.050020E+06 3.790016E+05 -1.156871E+08 -1.127716E+08 1.151165E+08 7.196628E+07 1.640528E+07 3.757291E+08 + -2.357945E+09 1.266310E+09 -3.629426E+08 1.834692E+09 -1.330600E+09 1.352238E+09 2.046366E+09 -4.065042E+08 6.084676E+06 -1.369468E+07 + 8.310638E+12 + 135 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -4.052916E+04 6.305515E+04 -1.061320E+04 3.387038E+03 -2.603023E+04 -5.119010E+03 1.644163E+04 -1.143164E+04 -2.356757E+03 + 7.028261E+03 3.996066E+03 -6.656790E+03 6.273862E+03 -6.917432E+03 2.896572E+03 1.957376E+03 -5.283192E+03 2.560322E+03 -2.745259E+02 + -3.656438E+03 -2.270890E+03 -2.861363E+00 4.162664E+03 -5.235412E+03 9.550564E+03 -1.876322E+04 3.857663E+04 -4.707364E+04 6.056218E+04 + -6.210130E+03 -6.425507E+04 4.616076E+04 -9.662258E+03 -9.273308E+03 1.324122E+04 8.254992E+04 -1.542612E+05 1.489596E+05 9.866315E+04 + 1.486799E+05 -3.400682E+05 1.231217E+05 1.380979E+05 8.737466E+03 -9.565380E+05 -1.532426E+06 3.385872E+06 -7.893853E+06 -3.027986E+06 + 4.106430E+06 -1.121863E+07 3.225826E+07 -6.907843E+06 -3.008419E+08 -4.108907E+08 8.125198E+08 2.733953E+08 2.788003E+08 2.074666E+09 + -1.255470E+10 4.699328E+09 -6.021227E+09 -1.330600E+09 1.672444E+11 -4.309531E+10 3.593738E+10 1.319491E+10 5.051802E+08 6.751608E+07 + -4.912546E+14 + 136 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -3.044333E+04 8.929571E+04 -1.267357E+05 2.394571E+04 2.506794E+04 3.684536E+04 -2.369345E+04 2.308800E+04 -8.707586E+03 + -2.241781E+04 3.307626E+04 -2.551530E+04 6.816353E+03 1.175862E+04 -2.534900E+04 2.326296E+04 9.205764E+03 -1.868948E+04 1.458989E+04 + -6.992394E+03 -2.122964E+04 3.420736E+04 -5.480528E+04 7.028994E+04 -9.396283E+04 1.350289E+05 -1.717468E+05 1.560707E+05 -5.903035E+04 + -8.447996E+03 -2.656841E+04 1.042937E+05 -1.084352E+05 1.238098E+05 6.240925E+04 1.831417E+03 -7.926240E+04 4.082989E+05 -6.203682E+05 + 2.115581E+05 2.096216E+05 -8.220311E+05 7.004082E+05 -6.117024E+05 4.415939E+06 -2.961359E+06 5.378734E+06 -1.031977E+07 -2.752652E+07 + 7.610645E+05 1.281150E+07 -6.191851E+06 -1.793524E+07 4.076503E+08 2.568479E+09 -4.532692E+08 -5.707024E+07 -8.727587E+07 -1.058727E+09 + 5.131416E+09 -2.073443E+09 2.805703E+09 1.352238E+09 -4.309531E+10 7.024751E+11 -2.500442E+10 5.494332E+09 -3.134126E+09 -5.373704E+08 + 2.070722E+14 + 137 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -1.193229E+04 2.155777E+04 -1.801831E+04 1.478654E+04 -1.447811E+04 -1.149118E+04 3.544210E+04 -3.173608E+04 1.271375E+04 + 1.600640E+03 4.988240E+02 -7.252585E+03 8.072711E+03 -8.209852E+03 -3.199452E+03 1.400051E+04 -1.201758E+04 2.234458E+03 8.322859E+03 + -1.371622E+04 2.617403E+04 -3.808091E+04 3.479436E+04 -2.394964E+04 1.416444E+04 -2.449548E+04 3.037097E+04 -3.804127E+04 4.093561E+04 + -9.823108E+03 -2.699273E+04 3.082986E+04 -2.392207E+04 7.606331E+03 9.358219E+03 -1.258878E+05 1.472546E+05 -1.434047E+05 1.564103E+05 + 2.808116E+04 5.772460E+04 -1.614718E+05 1.343689E+05 1.147976E+05 -1.340890E+06 8.674103E+05 -2.696572E+05 -1.214858E+07 -1.758505E+06 + -3.002345E+06 -4.905031E+06 1.191279E+07 -4.190840E+06 -7.878276E+07 7.937546E+07 4.335439E+07 -1.400609E+08 -1.518050E+08 1.857014E+08 + -1.091736E+09 1.137779E+09 -1.098607E+09 2.046366E+09 3.593738E+10 -2.500442E+10 8.258081E+10 -2.042257E+10 3.746731E+09 3.515908E+08 + -4.386839E+14 + 138 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 3.074538E+03 -3.096672E+03 -4.633398E+03 1.307666E+04 -9.924108E+03 3.869566E+03 -5.960999E+03 4.386395E+03 -2.837838E+03 + -2.228396E+03 9.280939E+03 -1.941335E+03 -1.848926E+03 -1.767241E+03 5.234347E+03 -4.326143E+03 1.473556E+03 1.534492E+03 -5.926037E+03 + 1.048260E+04 -1.385790E+04 1.921754E+04 -2.279603E+04 2.137452E+04 -8.752602E+03 6.944166E+03 -1.253034E+04 -3.913906E+03 1.827667E+04 + -2.594617E+04 2.161546E+04 -5.663342E+03 2.135388E+04 -5.265430E+04 9.755494E+04 -1.887335E+04 -1.021047E+05 1.243994E+05 3.444726E+04 + -7.437905E+04 -1.098534E+05 1.501792E+05 -1.430746E+05 -2.342603E+05 7.363073E+05 -2.752154E+05 5.109406E+05 -1.687122E+06 4.162108E+06 + -7.674368E+06 3.916428E+06 5.061736E+06 3.117666E+06 4.911256E+07 -6.393310E+08 9.256990E+07 5.413553E+07 2.322656E+07 4.240602E+08 + -2.681768E+09 6.702360E+08 -5.994466E+08 -4.065042E+08 1.319491E+10 5.494332E+09 -2.042257E+10 3.073145E+10 -3.299410E+09 -2.259949E+08 + -2.450842E+14 + 139 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 -8.567786E+02 4.513991E+03 -3.720700E+03 1.502254E+03 -1.750081E+03 5.943839E+02 9.651323E+02 -1.738702E+03 1.172714E+03 + 1.229275E+00 -9.754103E+02 -3.132418E+02 1.176342E+03 -5.866090E+02 -4.113584E+02 1.109074E+03 -1.526167E+03 1.139799E+03 -8.034157E+01 + -6.485856E+02 4.502551E+02 -8.798842E+02 2.587543E+03 -3.849498E+03 3.297346E+03 -3.371575E+03 3.630215E+03 -2.141855E+03 2.330750E+03 + -3.635932E+03 1.744814E+03 2.246437E+02 -1.906566E+03 9.850241E+03 -1.592101E+04 -1.648762E+03 1.393661E+04 -2.008351E+04 9.698153E+03 + 6.121624E+02 -4.654542E+03 2.032313E+04 -1.174548E+03 8.215990E+04 -1.694051E+05 1.686052E+05 -5.378785E+05 6.997242E+05 -5.722210E+05 + 1.514790E+06 1.058513E+05 -5.426642E+05 1.832194E+05 -2.559013E+07 -2.619539E+07 2.832442E+07 -6.247219E+06 1.441081E+07 4.074006E+06 + -2.165741E+08 6.509657E+07 -3.358237E+07 6.084676E+06 5.051802E+08 -3.134126E+09 3.746731E+09 -3.299410E+09 9.299398E+08 1.078182E+08 + -2.611421E+13 + 140 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.661757E+02 -5.312590E+01 3.467785E+02 2.594557E+02 -1.213013E+03 8.086835E+02 4.028573E+02 -9.360197E+02 2.516768E+02 + -1.838106E+02 5.106608E+01 1.501350E+02 -2.982344E+02 2.910965E+02 1.548642E+02 -1.352532E+02 7.404533E+01 -2.320717E+02 6.033314E+02 + -6.333462E+02 -7.385768E+01 5.419627E+02 -2.376337E+02 -8.661925E+02 8.544160E+02 -7.601798E+02 2.033368E+03 -2.478026E+03 1.804070E+03 + -5.444135E+02 3.748915E+02 -4.400167E+02 1.086874E+03 -5.338632E+02 2.592584E+03 -4.377881E+03 4.718591E+03 -8.740382E+03 1.246628E+04 + -9.953999E+03 4.246566E+03 3.781362E+03 1.234368E+04 -5.739302E+03 3.103474E+04 5.023135E+04 -3.553270E+05 3.960724E+05 -1.772294E+05 + -1.162551E+05 -1.601605E+05 1.863187E+05 2.654273E+05 1.856626E+07 -1.046318E+07 -1.561771E+07 4.823832E+06 -2.137042E+06 -7.863066E+06 + 8.365345E+07 4.641715E+06 -8.301872E+05 -1.369468E+07 6.751608E+07 -5.373704E+08 3.515908E+08 -2.259949E+08 1.078182E+08 1.656743E+08 + 4.934824E+13 + 141 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 + 0.000000E+00 5.890859E+09 -7.725383E+09 -2.678551E+09 3.594968E+09 1.227773E+09 -3.428461E+09 3.890406E+09 -1.634980E+09 1.233032E+09 + -2.380152E+09 1.687608E+09 5.801190E+08 5.678270E+08 -2.489034E+09 3.286930E+09 -2.584673E+09 2.623279E+09 2.022808E+08 -4.001454E+09 + 2.492521E+09 -8.192304E+08 1.055342E+09 -1.302767E+09 2.857732E+09 -6.347081E+08 -1.682832E+09 5.610983E+09 -1.912656E+09 -9.846052E+09 + 1.347476E+10 -2.700950E+09 1.847328E+09 -3.982927E+09 1.081086E+10 -2.681108E+09 2.273263E+09 -4.890574E+09 4.331904E+09 -3.308641E+10 + 1.470977E+10 -3.977980E+09 -9.531373E+10 1.500482E+11 -2.380336E+11 3.298146E+11 3.361380E+10 -1.396714E+12 1.011583E+12 -1.040400E+12 + 2.185426E+12 8.334480E+11 -1.748562E+12 -3.729922E+11 1.512698E+13 1.424720E+13 1.114252E+13 -1.238872E+13 -2.673967E+12 7.580179E+12 + 2.861435E+14 -5.573845E+13 3.657506E+13 8.310638E+12 -4.912546E+14 2.070722E+14 -4.386839E+14 -2.450842E+14 -2.611421E+13 4.934824E+13 + 5.042066E+21 diff --git a/resources/bmatrix/rttov/amsr_bmatrix_70_test.dat b/resources/bmatrix/rttov/amsr_bmatrix_70_test.dat new file mode 100644 index 000000000..0a14d5c4b --- /dev/null +++ b/resources/bmatrix/rttov/amsr_bmatrix_70_test.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3df51e9b03e7c9efb77ba970b9927db90cce24c79d6036602d305e491ffd4e60 +size 1022063 diff --git a/resources/bmatrix/rttov/atms_bmatrix_70_test.dat b/resources/bmatrix/rttov/atms_bmatrix_70_test.dat new file mode 100644 index 000000000..92a286561 --- /dev/null +++ b/resources/bmatrix/rttov/atms_bmatrix_70_test.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e919cdd89e4ae40e73c41071244ca6fdc955941ec9ab77967166f058dc7accf1 +size 1008087 diff --git a/resources/rmatrix/gnssro/gnssro_ba_rmatrix.nl b/resources/rmatrix/gnssro/gnssro_ba_rmatrix.nl new file mode 100644 index 000000000..bb477036c --- /dev/null +++ b/resources/rmatrix/gnssro/gnssro_ba_rmatrix.nl @@ -0,0 +1,1972 @@ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 3 (known as Metop-B), taken from processing centre 94 (DMI) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 4 (known as Metop-A), taken from processing centre 94 (DMI) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 5 (known as Metop-C), taken from processing centre 94 (DMI) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 3 (known as Metop-B), taken from processing centre 254 (EUMETSAT) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 4 (known as Metop-A), taken from processing centre 254 (EUMETSAT) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 5 (known as Metop-C), taken from processing centre 254 (EUMETSAT) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 41 (known as CHAMP), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 41 (known as CHAMP), taken from processing centre 78 (GFZ) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 42 (known as TerraSAR-X), taken from processing centre 78 (GFZ) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 43 (known as TanDEM-X), taken from processing centre 78 (GFZ) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 44 (known as PAZ), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 722 (known as Grace-A), taken from processing centre 78 (GFZ) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 740 (known as COSMIC-1), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 741 (known as COSMIC-2), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 742 (known as COSMIC-3), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 743 (known as COSMIC-4), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 744 (known as COSMIC-5), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 745 (known as COSMIC-6), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 786 (known as C/NOFS), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 522 (known as FY-3C), taken from processing centre 38 +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.200E-01, 0.200E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 523 (known as FY-3D), taken from processing centre 38 +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.200E-01, 0.200E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 825 (known as KOMPSAT-5), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 750 (known as COSMIC-2 E1), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 751 (known as COSMIC-2 E2), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 752 (known as COSMIC-2 E3), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 753 (known as COSMIC-2 E4), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 754 (known as COSMIC-2 E5), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 755 (known as COSMIC-2 E6), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 269 (known as Spire), taken from processing centre 178 (Spire) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ diff --git a/resources/rmatrix/gnssro/gnssro_ba_rmatrix_avtemp.nl b/resources/rmatrix/gnssro/gnssro_ba_rmatrix_avtemp.nl new file mode 100644 index 000000000..3dca3b248 --- /dev/null +++ b/resources/rmatrix/gnssro/gnssro_ba_rmatrix_avtemp.nl @@ -0,0 +1,2125 @@ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 3 (known as Metop-B (DMI)), taken from processing centre 94 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 4 (known as Metop-A (DMI)), taken from processing centre 94 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 5 (known as Metop-C (DMI)), taken from processing centre 94 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 3 (known as Metop-B (EUMETSAT)), taken from processing centre 254 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 4 (known as Metop-A (EUMETSAT)), taken from processing centre 254 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 5 (known as Metop-C (EUMETSAT)), taken from processing centre 254 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 42 (known as TerraSAR-X (GFZ)), taken from processing centre 78 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 43 (known as TanDEM-X (GFZ)), taken from processing centre 78 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 522 (known as FY-3C (CMA)), taken from processing centre 38 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 523 (known as FY-3D (CMA)), taken from processing centre 38 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 740 (known as COSMIC-1 FM1 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 741 (known as COSMIC-1 FM2(UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 742 (known as COSMIC-1 FM3 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 743 (known as COSMIC-1 FM4 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 744 (known as COSMIC-1 FM5 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 745 (known as COSMIC-1 FM6 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 825 (known as KOMPSAT-5 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 269 (known as Spire constellation), taken from processing centre 178 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 750 (known as COSMIC-2 E1 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 751 (known as COSMIC-2 E2 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 752 (known as COSMIC-2 E3 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 753 (known as COSMIC-2 E4 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 754 (known as COSMIC-2 E5 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 755 (known as COSMIC-2 E6 (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ +! +! Observation error namelist for GPSRO observations +! This entry is for satellite 44 (known as PAZ (UCAR)), taken from processing centre 60 +! +! Each entry is for a given range of average troposphere temperatures, which will be interpolated between. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background bending angle). +! +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3e-06 + av_temp = 225 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.048, 0.020, 0.008, 0.016, 0.300 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3e-06 + av_temp = 230 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.058, 0.014, 0.010, 0.037, 0.280 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3e-06 + av_temp = 235 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.064, 0.029, 0.012, 0.015, 0.220 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3e-06 + av_temp = 240 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.075, 0.033, 0.014, 0.014, 0.175 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3e-06 + av_temp = 245 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.081, 0.036, 0.012, 0.017, 0.146 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3e-06 + av_temp = 250 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.111, 0.048, 0.012, 0.020, 0.160 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3e-06 + av_temp = 255 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.132, 0.061, 0.012, 0.023, 0.152 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3e-06 + av_temp = 260 + max_height = 20000 + heights = 3000.0, 10000.0, 20000.0, 40000.0, 59900.0 + obs_errors = 0.128, 0.072, 0.010, 0.019, 0.179 +/ diff --git a/resources/rmatrix/gnssro/gnssro_ba_rmatrix_latitude.nl b/resources/rmatrix/gnssro/gnssro_ba_rmatrix_latitude.nl new file mode 100644 index 000000000..bb477036c --- /dev/null +++ b/resources/rmatrix/gnssro/gnssro_ba_rmatrix_latitude.nl @@ -0,0 +1,1972 @@ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 3 (known as Metop-B), taken from processing centre 94 (DMI) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 94 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 4 (known as Metop-A), taken from processing centre 94 (DMI) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 94 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 5 (known as Metop-C), taken from processing centre 94 (DMI) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 94 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 3 (known as Metop-B), taken from processing centre 254 (EUMETSAT) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 3 + origc = 254 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 4 (known as Metop-A), taken from processing centre 254 (EUMETSAT) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 4 + origc = 254 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 5 (known as Metop-C), taken from processing centre 254 (EUMETSAT) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 5 + origc = 254 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 41 (known as CHAMP), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 41 (known as CHAMP), taken from processing centre 78 (GFZ) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 41 + origc = 78 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 42 (known as TerraSAR-X), taken from processing centre 78 (GFZ) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 42 + origc = 78 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 43 (known as TanDEM-X), taken from processing centre 78 (GFZ) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 43 + origc = 78 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 44 (known as PAZ), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 44 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 722 (known as Grace-A), taken from processing centre 78 (GFZ) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 722 + origc = 78 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 740 (known as COSMIC-1), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 740 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 741 (known as COSMIC-2), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 741 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 742 (known as COSMIC-3), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 742 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 743 (known as COSMIC-4), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 743 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 744 (known as COSMIC-5), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 744 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 745 (known as COSMIC-6), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 745 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 786 (known as C/NOFS), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 786 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 522 (known as FY-3C), taken from processing centre 38 +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 522 + origc = 38 + min_error = 6.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.200E-01, 0.200E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 523 (known as FY-3D), taken from processing centre 38 +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.200E-01, 0.200E-01 +/ +&GPSRO_ob_error + satid = 523 + origc = 38 + min_error = 6.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.200E-01, 0.200E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 825 (known as KOMPSAT-5), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 825 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 750 (known as COSMIC-2 E1), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 750 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 751 (known as COSMIC-2 E2), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 751 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 752 (known as COSMIC-2 E3), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 752 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 753 (known as COSMIC-2 E4), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 753 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 754 (known as COSMIC-2 E5), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 754 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 755 (known as COSMIC-2 E6), taken from processing centre 60 (UCAR) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 755 + origc = 60 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +! +! Observation error namelist for GPSRO refractivity observations +! This entry is for satellite 269 (known as Spire), taken from processing centre 178 (Spire) +! +! Each entry is for a given range of latitudes, and the nearest matrix to each +! observation will be chosen. +! The observation errors are given for a set of heights. +! If an observation is present on a height outside this range, then the value +! will be taken to be the value at the relevant end. +! If an observation is at a height between the levels indicated, then the code +! will linearly interpolate between those heights. +! The entries are given as relative errors (percentage error of the observation +! divided by the background refractivity). +! +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = 75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = 45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = 15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = -15 + clen = 1.000E+10 + heights = 0.000E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = -45 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.110E+00, 0.150E-01, 0.150E-01 +/ +&GPSRO_ob_error + satid = 269 + origc = 178 + min_error = 3.000E-06 + latitude = -75 + clen = 1.000E+10 + heights = 0.000E+00, 0.025E+00, 0.100E+05, 0.700E+05 + obs_errors = 0.200E+00, 0.800E-01, 0.150E-01, 0.150E-01 +/ diff --git a/resources/rmatrix/rttov/amsr_gcomw1_rmatrix_test.nc4 b/resources/rmatrix/rttov/amsr_gcomw1_rmatrix_test.nc4 new file mode 100644 index 000000000..c1b7f234a --- /dev/null +++ b/resources/rmatrix/rttov/amsr_gcomw1_rmatrix_test.nc4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:593372037ea656861c78ef1badb74e126e02fb1ddae5fd384c85fccc03d082bb +size 9378 diff --git a/resources/rmatrix/rttov/atms_noaa_20_rmatrix_test.nc4 b/resources/rmatrix/rttov/atms_noaa_20_rmatrix_test.nc4 new file mode 100644 index 000000000..db41c54d8 --- /dev/null +++ b/resources/rmatrix/rttov/atms_noaa_20_rmatrix_test.nc4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6df4bb221b0cf822ee56e8594adfa7340dcc90d1a8d9d97332dea83575ca0e3e +size 9378 diff --git a/src/mains/RunCRTM.h b/src/mains/RunCRTM.h index a0df47b51..045e1b7d3 100644 --- a/src/mains/RunCRTM.h +++ b/src/mains/RunCRTM.h @@ -63,12 +63,13 @@ template class RunCRTM : public oops::Application { const eckit::LocalConfiguration gconf(conf[jj], "geovals"); const GeoVaLs_ gval(gconf, obsdb[jj], hop.requiredVars()); - const ObsAuxCtrl_ ybias(obsdb[jj], conf[jj]); + eckit::LocalConfiguration biasconf = conf[jj].getSubConfiguration("obs bias"); + typename ObsAuxCtrl_::Parameters_ biasparams; + biasparams.validateAndDeserialize(biasconf); + const ObsAuxCtrl_ ybias(obsdb[jj], biasparams); ObsVector_ hofx(obsdb[jj]); - ObsDiags_ diag(obsdb[jj], - hop.locations(obsdb[jj].windowStart(), obsdb[jj].windowEnd()), - diagvars); + ObsDiags_ diag(obsdb[jj], hop.locations(), diagvars); hop.simulateObs(gval, hofx, ybias, diag); diff --git a/src/ufo/AnalyticInit.cc b/src/ufo/AnalyticInit.cc index 986760cc8..d5a3dac09 100644 --- a/src/ufo/AnalyticInit.cc +++ b/src/ufo/AnalyticInit.cc @@ -37,7 +37,7 @@ void AnalyticInit::fillGeoVaLs(const Locations & locs, GeoVaLs & geovals) const { oops::Log::trace() << "AnalyticInit::analytic_init starting" << std::endl; if (config_.has("analytic_init")) { - ufo_geovals_analytic_init_f90(geovals.toFortran(), locs.toFortran(), config_); + ufo_geovals_analytic_init_f90(geovals.toFortran(), locs, config_); } oops::Log::trace() << "AnalyticInit::analytic_init done" << std::endl; } diff --git a/src/ufo/CMakeLists.txt b/src/ufo/CMakeLists.txt index 3309da4e6..497e4f961 100644 --- a/src/ufo/CMakeLists.txt +++ b/src/ufo/CMakeLists.txt @@ -12,20 +12,24 @@ list( APPEND ufo_src_files GeoVaLs.interface.F90 GeoVaLs.interface.h instantiateObsFilterFactory.h + instantiateObsLocFactory.h + LinearObsBiasOperator.cc + LinearObsBiasOperator.h LinearObsOperator.cc LinearObsOperator.h LinearObsOperatorBase.cc LinearObsOperatorBase.h Locations.cc Locations.h - Locations.interface.F90 - Locations.interface.h ObsBias.cc ObsBias.h ObsBiasCovariance.cc ObsBiasCovariance.h ObsBiasIncrement.cc ObsBiasIncrement.h + ObsBiasOperator.cc + ObsBiasOperator.h + ObsBiasParameters.h ObsDiagnostics.cc ObsDiagnostics.h ObsOperator.cc @@ -33,8 +37,10 @@ list( APPEND ufo_src_files ObsOperatorBase.cc ObsOperatorBase.h ObsTraits.h + locations_f.cc + locations_f.h ufo_geovals_mod.F90 - ufo_locs_mod.F90 + ufo_locations_mod.F90 ufo_variables_mod.F90 ufo_constants_mod.F90 ) @@ -53,34 +59,41 @@ add_subdirectory( utils ) add_subdirectory( basis ) add_subdirectory( predictors ) add_subdirectory( filters ) +add_subdirectory( obslocalization ) add_subdirectory( identity ) add_subdirectory( atmvertinterp ) add_subdirectory( atmvertinterplay ) add_subdirectory( atmsfcinterp ) add_subdirectory( avgkernel ) -if( ${CRTM_FOUND} ) +if( crtm_FOUND ) add_subdirectory( crtm ) -endif( ${CRTM_FOUND} ) -add_subdirectory( radarreflectivity ) +endif() add_subdirectory( radarradialvelocity ) -if( ${RTTOV_FOUND} ) +if( rttov_FOUND ) add_subdirectory( rttov ) -endif( ${RTTOV_FOUND} ) + add_subdirectory( rttovcpp ) +endif() add_subdirectory( gnssro ) add_subdirectory( sfcpcorrected ) add_subdirectory( marine ) -if( ${GEOS-AERO_FOUND} ) - add_subdirectory( geos_aero ) -endif( ${GEOS-AERO_FOUND} ) +add_subdirectory( aerosols ) add_subdirectory( timeoper ) add_subdirectory( profile ) add_subdirectory( groundgnss ) +add_subdirectory( variabletransforms ) +add_subdirectory( scatwind ) +add_subdirectory( sattcwv ) +add_subdirectory( compositeoper ) +add_subdirectory( backgrounderrorvertinterp ) +add_subdirectory( backgrounderroridentity ) +add_subdirectory( categoricaloper ) list( APPEND ufo_src_files ${utils_src_files} ${basis_src_files} ${predictor_src_files} ${filters_src_files} + ${obslocalization_src_files} ${identity_src_files} ${atmvertinterp_src_files} ${atmvertinterplay_src_files} @@ -88,40 +101,75 @@ list( APPEND ufo_src_files ${avgkernel_src_files} ${crtm_src_files} ${rttov_src_files} + ${rttovcpp_src_files} ${gnssro_src_files} - ${geosaod_src_files} + ${aerosols_src_files} ${sfcpcorrected_src_files} - ${radarreflectivity_src_files} ${radarradialvelocity_src_files} ${timeoper_src_files} ${profile_src_files} ${groundgnss_src_files} + ${variabletransforms_src_files} + ${scatwind_src_files} + ${sattcwv_src_files} + ${compositeoper_src_files} + ${backgrounderrorvertinterp_src_files} + ${backgrounderroridentity_src_files} + ${categoricaloper_src_files} ) list( APPEND ufo_src_files ${marine_src_files} ) -list( APPEND UFO_LIBS_DEP oops ioda ${LAPACK_LIBRARIES} ${NETCDF_LIBRARIES} ) -if( ${CRTM_FOUND} ) - list( APPEND UFO_LIBS_DEP crtm ) -endif( ${CRTM_FOUND} ) -if( ${GSW_FOUND} ) - list( APPEND UFO_LIBS_DEP gsw ) -endif( ${GSW_FOUND} ) -if( ${RTTOV_FOUND} ) - list( APPEND UFO_LIBS_DEP rttov ) -endif( ${RTTOV_FOUND} ) - -if( ${ROPP-UFO_FOUND} ) - list( APPEND UFO_LIBS_DEP ropp-ufo ) -endif( ${ROPP-UFO_FOUND} ) - -if( ${GEOS-AERO_FOUND} ) - list( APPEND UFO_LIBS_DEP geos-aero ) -endif( ${GEOS-AERO_FOUND} ) - +## Create Library target ecbuild_add_library( TARGET ufo SOURCES ${ufo_src_files} - LIBS ${UFO_LIBS_DEP} INSTALL_HEADERS LISTED - HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/ufo - LINKER_LANGUAGE ${OOPS_LINKER_LANGUAGE} + HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/${PROJECT_NAME} ) + +## Link dependencies +target_link_libraries(ufo PUBLIC MPI::MPI_C MPI::MPI_CXX MPI::MPI_Fortran) +target_link_libraries(ufo PUBLIC Boost::boost) +target_link_libraries(ufo PUBLIC NetCDF::NetCDF_C NetCDF::NetCDF_Fortran) +target_link_libraries(ufo PUBLIC Eigen3::Eigen) +target_link_libraries(ufo PUBLIC gsl::gsl-lite) +target_link_libraries(ufo PUBLIC eckit) +target_link_libraries(ufo PUBLIC fckit) +target_link_libraries(ufo PUBLIC ioda) +target_link_libraries(ufo PUBLIC oops) + +# Optional dependencies +if(crtm_FOUND) + target_link_libraries(ufo PUBLIC crtm) +endif() + +if(gsw_FOUND) + target_link_libraries(ufo PUBLIC gsw) +endif() + +if(rttov_FOUND) + target_link_libraries(ufo PUBLIC rttov) + target_compile_definitions(ufo PUBLIC RTTOV_FOUND) +endif() + +if(ropp-ufo_FOUND) + target_link_libraries(ufo PUBLIC ropp-ufo) +endif() + +if(geos-aero_FOUND) + target_link_libraries(ufo PUBLIC geos-aero) +endif() + + +## Include paths +target_include_directories(ufo PUBLIC $ + $) + + +## Fortran modules +set(MODULE_DIR ${PROJECT_NAME}/module) +set_target_properties(${PROJECT_NAME} PROPERTIES Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/${MODULE_DIR}) +install(DIRECTORY ${CMAKE_BINARY_DIR}/${MODULE_DIR}/ DESTINATION ${CMAKE_INSTALL_LIBDIR}/${MODULE_DIR}) +target_include_directories(${PROJECT_NAME} INTERFACE + $ + $) + diff --git a/src/ufo/Fortran.h b/src/ufo/Fortran.h index c0a07eb79..a30e30535 100644 --- a/src/ufo/Fortran.h +++ b/src/ufo/Fortran.h @@ -18,6 +18,8 @@ typedef int F90goms; typedef int F90hop; // Observation bias key type typedef int F90obias; +// Observation filter key type +typedef int F90obfilter; } // namespace ufo #endif // UFO_FORTRAN_H_ diff --git a/src/ufo/GeoVaLs.cc b/src/ufo/GeoVaLs.cc index d31d5eaad..b636b697b 100644 --- a/src/ufo/GeoVaLs.cc +++ b/src/ufo/GeoVaLs.cc @@ -1,5 +1,5 @@ /* - * (C) Copyright 2017-2019 UCAR + * (C) Copyright 2017-2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -7,12 +7,17 @@ #include "ufo/GeoVaLs.h" +#include #include #include #include "eckit/config/Configuration.h" #include "eckit/exception/Exceptions.h" +#include "ioda/distribution/Accumulator.h" +#include "ioda/distribution/Distribution.h" +#include "ioda/ObsSpace.h" + #include "oops/base/Variables.h" #include "oops/util/Logger.h" @@ -24,8 +29,9 @@ namespace ufo { // ----------------------------------------------------------------------------- /*! \brief Default constructor - does not allocate fields */ -GeoVaLs::GeoVaLs(const eckit::mpi::Comm & comm) - : keyGVL_(-1), comm_(comm) +GeoVaLs::GeoVaLs(const std::shared_ptr dist, + const oops::Variables & vars) + : keyGVL_(-1), vars_(vars), dist_(dist) { oops::Log::trace() << "GeoVaLs default constructor starting" << std::endl; ufo_geovals_default_constr_f90(keyGVL_); @@ -44,10 +50,10 @@ GeoVaLs::GeoVaLs(const eckit::mpi::Comm & comm) * */ GeoVaLs::GeoVaLs(const Locations & locs, const oops::Variables & vars) - : keyGVL_(-1), vars_(vars), comm_(locs.getComm()) + : keyGVL_(-1), vars_(vars), dist_(locs.distribution()) { oops::Log::trace() << "GeoVaLs contructor starting" << std::endl; - ufo_geovals_setup_f90(keyGVL_, locs.nobs(), vars_); + ufo_geovals_setup_f90(keyGVL_, locs.size(), vars_); oops::Log::trace() << "GeoVaLs contructor key = " << keyGVL_ << std::endl; } @@ -60,7 +66,7 @@ GeoVaLs::GeoVaLs(const Locations & locs, const oops::Variables & vars) GeoVaLs::GeoVaLs(const eckit::Configuration & config, const ioda::ObsSpace & obspace, const oops::Variables & vars) - : keyGVL_(-1), vars_(vars), comm_(obspace.comm()) + : keyGVL_(-1), vars_(vars), dist_(obspace.distribution()) { oops::Log::trace() << "GeoVaLs constructor config starting" << std::endl; ufo_geovals_setup_f90(keyGVL_, 0, vars_); @@ -75,7 +81,7 @@ GeoVaLs::GeoVaLs(const eckit::Configuration & config, * create a new GeoVaLs with just one location */ GeoVaLs::GeoVaLs(const GeoVaLs & other, const int & index) - : keyGVL_(-1), vars_(other.vars_), comm_(other.comm_) + : keyGVL_(-1), vars_(other.vars_), dist_(other.dist_) { oops::Log::trace() << "GeoVaLs copy one GeoVaLs constructor starting" << std::endl; ufo_geovals_setup_f90(keyGVL_, 1, vars_); @@ -87,7 +93,7 @@ GeoVaLs::GeoVaLs(const GeoVaLs & other, const int & index) /*! \brief Copy constructor */ GeoVaLs::GeoVaLs(const GeoVaLs & other) - : keyGVL_(-1), vars_(other.vars_), comm_(other.comm_) + : keyGVL_(-1), vars_(other.vars_), dist_(other.dist_) { oops::Log::trace() << "GeoVaLs copy constructor starting" << std::endl; ufo_geovals_setup_f90(keyGVL_, 0, vars_); @@ -102,6 +108,15 @@ GeoVaLs::~GeoVaLs() { oops::Log::trace() << "GeoVaLs destructor done" << std::endl; } // ----------------------------------------------------------------------------- +/*! \brief Allocate GeoVaLs for \p vars variables to have \p nlevels levels (number of + * locations is defined in the constructor) */ +void GeoVaLs::allocate(const int & nlevels, const oops::Variables & vars) +{ + oops::Log::trace() << "GeoVaLs::allocate starting" << std::endl; + ufo_geovals_allocate_f90(keyGVL_, nlevels, vars); + oops::Log::trace() << "GeoVaLs::allocate done" << std::endl; +} +// ----------------------------------------------------------------------------- /*! \brief Zero out the GeoVaLs */ void GeoVaLs::zero() { oops::Log::trace() << "GeoVaLs::zero starting" << std::endl; @@ -198,10 +213,31 @@ GeoVaLs & GeoVaLs::operator*=(const GeoVaLs & other) { /*! \brief Scalar product of two GeoVaLs */ double GeoVaLs::dot_product_with(const GeoVaLs & other) const { oops::Log::trace() << "GeoVaLs::dot_product_with starting" << std::endl; - double zz; - ufo_geovals_dotprod_f90(keyGVL_, other.keyGVL_, zz, comm_); + const size_t nlocs = this->nlocs(); + assert(nlocs == other.nlocs()); + assert(vars_ == other.vars_); + auto accumulator = dist_->createAccumulator(); + std::vector this_values(nlocs), other_values(nlocs); + double missing = util::missingValue(missing); + // loop over all variables in geovals + for (size_t jvar = 0; jvar < vars_.size(); ++jvar) { + const size_t nlevs = this->nlevs(vars_[jvar]); + assert(nlevs == other.nlevs(vars_[jvar])); + // loop over all levels for this variable + for (size_t jlev = 0; jlev < nlevs; ++jlev) { + this->get(this_values, vars_[jvar], jlev+1); + other.get(other_values, vars_[jvar], jlev+1); + // loop over all locations + for (size_t jloc = 0; jloc < nlocs; ++jloc) { + if ((this_values[jloc] != missing) && (other_values[jloc] != missing)) { + accumulator->addTerm(jloc, this_values[jloc]*other_values[jloc]); + } + } + } + } + const double dotprod = accumulator->computeResult(); oops::Log::trace() << "GeoVaLs::dot_product_with done" << std::endl; - return zz; + return dotprod; } // ----------------------------------------------------------------------------- /*! \brief Split two GeoVaLs */ @@ -267,10 +303,11 @@ size_t GeoVaLs::nlevs(const std::string & var) const { /*! \brief Return all values for a specific 2D variable */ void GeoVaLs::get(std::vector & vals, const std::string & var) const { oops::Log::trace() << "GeoVaLs::get 2D starting" << std::endl; - size_t nlocs; - ufo_geovals_nlocs_f90(keyGVL_, nlocs); - ASSERT(vals.size() == nlocs); - ufo_geovals_get2d_f90(keyGVL_, var.size(), var.c_str(), nlocs, vals[0]); + /// Call method to get double values (Fortran data structure stores data in double) + /// and convert to floats + std::vector doublevals(vals.size()); + this->get(doublevals, var); + vals.assign(doublevals.begin(), doublevals.end()); oops::Log::trace() << "GeoVaLs::get 2D done" << std::endl; } // ----------------------------------------------------------------------------- @@ -294,14 +331,126 @@ void GeoVaLs::get(std::vector & vals, const std::string & var, const int oops::Log::trace() << "GeoVaLs::get done" << std::endl; } // ----------------------------------------------------------------------------- -/*! \brief Put values for a specific variable and level */ +/*! \brief Return all values for a specific 2D variable */ +void GeoVaLs::get(std::vector & vals, const std::string & var) const { + oops::Log::trace() << "GeoVaLs::get 2D starting" << std::endl; + size_t nlocs; + ufo_geovals_nlocs_f90(keyGVL_, nlocs); + ASSERT(vals.size() == nlocs); + ufo_geovals_get2d_f90(keyGVL_, var.size(), var.c_str(), nlocs, vals[0]); + oops::Log::trace() << "GeoVaLs::get 2D done" << std::endl; +} +// ----------------------------------------------------------------------------- +/*! \brief Return all values for a specific 2D variable */ +void GeoVaLs::get(std::vector & vals, const std::string & var) const { + oops::Log::trace() << "GeoVaLs::get 2D starting" << std::endl; + /// Call method to get double values (Fortran data structure stores data in double) + /// and convert to ints + std::vector doublevals(vals.size()); + this->get(doublevals, var); + vals.assign(doublevals.begin(), doublevals.end()); + oops::Log::trace() << "GeoVaLs::get 2D done" << std::endl; +} +// ----------------------------------------------------------------------------- +/*! \brief Return all values for a specific variable and location */ +void GeoVaLs::getAtLocation(std::vector & vals, + const std::string & var, + const int loc) const { + oops::Log::trace() << "GeoVaLs::getAtLocation starting" << std::endl; + const size_t nlevs = this->nlevs(var); + ASSERT(vals.size() == nlevs); + ASSERT(loc >= 0 && loc < this->nlocs()); + ufo_geovals_get_loc_f90(keyGVL_, var.size(), var.c_str(), loc, nlevs, vals[0]); + oops::Log::trace() << "GeoVaLs::getAtLocation done" << std::endl; +} +// ----------------------------------------------------------------------------- +/*! \brief Return all values for a specific variable and location and convert to float */ +void GeoVaLs::getAtLocation(std::vector & vals, + const std::string & var, + const int loc) const { + oops::Log::trace() << "GeoVaLs::getAtLocation starting" << std::endl; + std::vector doublevals(vals.size()); + this->getAtLocation(doublevals, var, loc); + vals.assign(doublevals.begin(), doublevals.end()); + oops::Log::trace() << "GeoVaLs::getAtLocation done" << std::endl; +} +// ----------------------------------------------------------------------------- +/*! \brief Return all values for a specific variable and location and convert to int */ +void GeoVaLs::getAtLocation(std::vector & vals, + const std::string & var, + const int loc) const { + oops::Log::trace() << "GeoVaLs::getAtLocation starting" << std::endl; + std::vector doublevals(vals.size()); + this->getAtLocation(doublevals, var, loc); + vals.assign(doublevals.begin(), doublevals.end()); + oops::Log::trace() << "GeoVaLs::getAtLocation done" << std::endl; +} +// ----------------------------------------------------------------------------- +/*! \brief Put double values for a specific variable and level */ void GeoVaLs::put(const std::vector & vals, const std::string & var, const int lev) const { oops::Log::trace() << "GeoVaLs::put starting" << std::endl; size_t nlocs; ufo_geovals_nlocs_f90(keyGVL_, nlocs); ASSERT(vals.size() == nlocs); ufo_geovals_putdouble_f90(keyGVL_, var.size(), var.c_str(), lev, nlocs, vals[0]); - oops::Log::trace() << "GeoVaLs::get done" << std::endl; + oops::Log::trace() << "GeoVaLs::put done" << std::endl; +} +// ----------------------------------------------------------------------------- +/*! \brief Put float values for a specific variable and level */ +void GeoVaLs::put(const std::vector & vals, const std::string & var, const int lev) const { + oops::Log::trace() << "GeoVaLs::put starting" << std::endl; + size_t nlocs; + ufo_geovals_nlocs_f90(keyGVL_, nlocs); + ASSERT(vals.size() == nlocs); + std::vector doublevals(vals.begin(), vals.end()); + ufo_geovals_putdouble_f90(keyGVL_, var.size(), var.c_str(), lev, nlocs, doublevals[0]); + oops::Log::trace() << "GeoVaLs::put done" << std::endl; +} +// ----------------------------------------------------------------------------- +/*! \brief Put int values for a specific variable and level */ +void GeoVaLs::put(const std::vector & vals, const std::string & var, const int lev) const { + oops::Log::trace() << "GeoVaLs::put starting" << std::endl; + size_t nlocs; + ufo_geovals_nlocs_f90(keyGVL_, nlocs); + ASSERT(vals.size() == nlocs); + std::vector doublevals(vals.begin(), vals.end()); + ufo_geovals_putdouble_f90(keyGVL_, var.size(), var.c_str(), lev, nlocs, doublevals[0]); + oops::Log::trace() << "GeoVaLs::put done" << std::endl; +} +/*! \brief Put double values for a specific variable and location */ +void GeoVaLs::putAtLocation(const std::vector & vals, + const std::string & var, + const int loc) const { + oops::Log::trace() << "GeoVaLs::putAtLocation starting" << std::endl; + const size_t nlevs = this->nlevs(var); + ASSERT(vals.size() == nlevs); + ASSERT(loc >= 0 && loc < this->nlocs()); + ufo_geovals_put_loc_f90(keyGVL_, var.size(), var.c_str(), loc, nlevs, vals[0]); + oops::Log::trace() << "GeoVaLs::putAtLocation done" << std::endl; +} +/*! \brief Put float values for a specific variable and location */ +void GeoVaLs::putAtLocation(const std::vector & vals, + const std::string & var, + const int loc) const { + oops::Log::trace() << "GeoVaLs::putAtLocation starting" << std::endl; + const size_t nlevs = this->nlevs(var); + ASSERT(vals.size() == nlevs); + ASSERT(loc >= 0 && loc < this->nlocs()); + std::vector doublevals(vals.begin(), vals.end()); + ufo_geovals_put_loc_f90(keyGVL_, var.size(), var.c_str(), loc, nlevs, doublevals[0]); + oops::Log::trace() << "GeoVaLs::putAtLocation done" << std::endl; +} +/*! \brief Put int values for a specific variable and location */ +void GeoVaLs::putAtLocation(const std::vector & vals, + const std::string & var, + const int loc) const { + oops::Log::trace() << "GeoVaLs::putAtLocation starting" << std::endl; + const size_t nlevs = this->nlevs(var); + ASSERT(vals.size() == nlevs); + ASSERT(loc >= 0 && loc < this->nlocs()); + std::vector doublevals(vals.begin(), vals.end()); + ufo_geovals_put_loc_f90(keyGVL_, var.size(), var.c_str(), loc, nlevs, doublevals[0]); + oops::Log::trace() << "GeoVaLs::putAtLocation done" << std::endl; } // ----------------------------------------------------------------------------- /*! \brief Read GeoVaLs from the file */ @@ -315,8 +464,17 @@ void GeoVaLs::read(const eckit::Configuration & config, /*! \brief Write GeoVaLs to the file */ void GeoVaLs::write(const eckit::Configuration & config) const { oops::Log::trace() << "GeoVaLs::write starting" << std::endl; - ufo_geovals_write_file_f90(keyGVL_, config, comm_); + ufo_geovals_write_file_f90(keyGVL_, config, dist_->rank()); oops::Log::trace() << "GeoVaLs::write done" << std::endl; } // ----------------------------------------------------------------------------- +/*! \brief Return the number of geovals */ +size_t GeoVaLs::nlocs() const { + oops::Log::trace() << "GeoVaLs::nlocs starting" << std::endl; + size_t nlocs; + ufo_geovals_nlocs_f90(keyGVL_, nlocs); + oops::Log::trace() << "GeoVaLs::nlocs done" << std::endl; + return nlocs; +} +// ----------------------------------------------------------------------------- } // namespace ufo diff --git a/src/ufo/GeoVaLs.h b/src/ufo/GeoVaLs.h index 86e2f1ef0..57e59278f 100644 --- a/src/ufo/GeoVaLs.h +++ b/src/ufo/GeoVaLs.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2017-2018 UCAR + * (C) Copyright 2017-2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -8,12 +8,11 @@ #ifndef UFO_GEOVALS_H_ #define UFO_GEOVALS_H_ +#include #include #include #include -#include "eckit/mpi/Comm.h" - #include "oops/base/Variables.h" #include "oops/util/ObjectCounter.h" #include "oops/util/Printable.h" @@ -26,6 +25,7 @@ namespace eckit { namespace ioda { class ObsSpace; + class Distribution; } namespace ufo { @@ -40,7 +40,7 @@ class GeoVaLs : public util::Printable, public: static const std::string classname() {return "ufo::GeoVaLs";} - explicit GeoVaLs(const eckit::mpi::Comm &); + GeoVaLs(const std::shared_ptr, const oops::Variables &); GeoVaLs(const Locations &, const oops::Variables &); GeoVaLs(const eckit::Configuration &, const ioda::ObsSpace &, const oops::Variables &); @@ -59,6 +59,14 @@ class GeoVaLs : public util::Printable, void split(GeoVaLs &, GeoVaLs &) const; void merge(const GeoVaLs &, const GeoVaLs &); + /// \brief Allocate GeoVaLs for \p vars variables with \p nlev number of levels + /// \details Fails if at least one of the \p vars doesn't exist in GeoVaLs. + /// Only allocates variables that haven't been allocated before. + /// Fails if one of \p vars is already allocated with number of levels + /// different than \p nlev; doesn't reallocate variables that are already + /// allocated with \p nlev. + void allocate(const int & nlev, const oops::Variables & vars); + void zero(); void reorderzdir(const std::string &, const std::string &); void random(); @@ -69,13 +77,36 @@ class GeoVaLs : public util::Printable, const oops::Variables & getVars() const {return vars_;} size_t nlevs(const std::string & var) const; - void get(std::vector &, const std::string &) const; void get(std::vector &, const std::string &, const int) const; void get(std::vector &, const std::string &, const int) const; - void put(const std::vector &, const std::string &, const int) const; - + /// Get 2D GeoVaLs for variable \p var (fails for 3D GeoVaLs) + void get(std::vector &, const std::string & var) const; + /// Get 2D GeoVaLs for variable \p var (fails for 3D GeoVaLs), and convert to float + void get(std::vector &, const std::string & var) const; + /// Get 2D GeoVaLs for variable \p var (fails for 3D GeoVaLs), and convert to int + void get(std::vector &, const std::string & var) const; + /// Get GeoVaLs at a specified location + void getAtLocation(std::vector &, const std::string &, const int) const; + /// Get GeoVaLs at a specified location and convert to float + void getAtLocation(std::vector &, const std::string &, const int) const; + /// Get GeoVaLs at a specified location and convert to int + void getAtLocation(std::vector &, const std::string &, const int) const; + /// Put GeoVaLs for double variable \p var at level \p lev. + void put(const std::vector & vals, const std::string & var, const int lev) const; + /// Put GeoVaLs for float variable \p var at level \p lev. + void put(const std::vector & vals, const std::string & var, const int lev) const; + /// Put GeoVaLs for int variable \p var at level \p lev. + void put(const std::vector & vals, const std::string & var, const int lev) const; + /// Put GeoVaLs for double variable \p var at location \p loc. + void putAtLocation(const std::vector & vals, const std::string & var, + const int loc) const; + /// Put GeoVaLs for float variable \p var at location \p loc. + void putAtLocation(const std::vector & vals, const std::string & var, const int loc) const; + /// Put GeoVaLs for int variable \p var at location \p loc. + void putAtLocation(const std::vector & vals, const std::string & var, const int loc) const; void read(const eckit::Configuration &, const ioda::ObsSpace &); void write(const eckit::Configuration &) const; + size_t nlocs() const; int & toFortran() {return keyGVL_;} const int & toFortran() const {return keyGVL_;} @@ -85,7 +116,7 @@ class GeoVaLs : public util::Printable, F90goms keyGVL_; oops::Variables vars_; - const eckit::mpi::Comm & comm_; + std::shared_ptr dist_; /// observations MPI distribution }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/GeoVaLs.interface.F90 b/src/ufo/GeoVaLs.interface.F90 index ebf7baf6c..2c2f7ee0d 100644 --- a/src/ufo/GeoVaLs.interface.F90 +++ b/src/ufo/GeoVaLs.interface.F90 @@ -7,11 +7,8 @@ module ufo_geovals_mod_c use fckit_configuration_module, only: fckit_configuration -use fckit_mpi_module, only: fckit_mpi_comm use iso_c_binding use ufo_geovals_mod -use ufo_locs_mod -use ufo_locs_mod_c, only : ufo_locs_registry use kinds implicit none @@ -68,6 +65,24 @@ subroutine ufo_geovals_setup_c(c_key_self, c_nlocs, c_vars) bind(c,name='ufo_geo end subroutine ufo_geovals_setup_c +!> Allocate GeoVaLs +subroutine ufo_geovals_allocate_c(c_key_self, c_nlevels, c_vars) bind(c,name='ufo_geovals_allocate_f90') +use oops_variables_mod +implicit none +integer(c_int), intent(inout) :: c_key_self +integer(c_int), intent(in) :: c_nlevels +type(c_ptr), value, intent(in) :: c_vars + +type(ufo_geovals), pointer :: self +type(oops_variables) :: vars + +call ufo_geovals_registry%get(c_key_self, self) + +vars = oops_variables(c_vars) +call ufo_geovals_allocate(self, vars, c_nlevels) + +end subroutine ufo_geovals_allocate_c + ! ------------------------------------------------------------------------------ !> Copy one GeoVaLs object into another @@ -106,25 +121,25 @@ end subroutine ufo_geovals_copy_one_c ! ------------------------------------------------------------------------------ !> Analytic init -subroutine ufo_geovals_analytic_init_c(c_key_self, c_key_locs, c_conf) bind(c,name='ufo_geovals_analytic_init_f90') - +subroutine ufo_geovals_analytic_init_c(c_key_self, c_locs, c_conf) bind(c,name='ufo_geovals_analytic_init_f90') +use ufo_locations_mod implicit none integer(c_int), intent(in) :: c_key_self -integer(c_int), intent(in) :: c_key_locs +type(c_ptr), value, intent(in) :: c_locs type(c_ptr), value, intent(in) :: c_conf type(ufo_geovals), pointer :: self -type(ufo_locs), pointer :: locs +type(ufo_locations) ::locs character(len=30) :: ic character(len=:), allocatable :: str type(fckit_configuration) :: f_conf call ufo_geovals_registry%get(c_key_self, self) -call ufo_locs_registry%get(c_key_locs,locs) f_conf = fckit_configuration(c_conf) call f_conf%get_or_die("analytic_init",str) ic = str +locs = ufo_locations(c_locs) call ufo_geovals_analytic_init(self,locs,ic) end subroutine ufo_geovals_analytic_init_c @@ -333,26 +348,6 @@ end subroutine ufo_geovals_normalize_c ! ------------------------------------------------------------------------------ -subroutine ufo_geovals_dotprod_c(c_key_self, c_key_other, prod, c_comm) bind(c,name='ufo_geovals_dotprod_f90') -implicit none -integer(c_int), intent(in) :: c_key_self, c_key_other -real(c_double), intent(inout) :: prod -type(c_ptr), value, intent(in) :: c_comm - -type(ufo_geovals), pointer :: self, other -type(fckit_mpi_comm) :: f_comm - -call ufo_geovals_registry%get(c_key_self, self) -call ufo_geovals_registry%get(c_key_other, other) - -f_comm = fckit_mpi_comm(c_comm) - -call ufo_geovals_dotprod(self, other, prod, f_comm) - -end subroutine ufo_geovals_dotprod_c - -! ------------------------------------------------------------------------------ - subroutine ufo_geovals_split_c(c_key_self, c_key_other1, c_key_other2) bind(c,name='ufo_geovals_split_f90') implicit none integer(c_int), intent(in) :: c_key_self, c_key_other1, c_key_other2 @@ -430,7 +425,7 @@ subroutine ufo_geovals_nlevs_c(c_key_self, lvar, c_var, nlevs) bind(c, name='ufo call ufo_geovals_get_var(self, varname, geoval) -nlevs = size(geoval%vals,1) +nlevs = geoval%nval end subroutine ufo_geovals_nlevs_c @@ -440,11 +435,11 @@ subroutine ufo_geovals_get2d_c(c_key_self, lvar, c_var, nlocs, values) bind(c, n use ufo_vars_mod, only: MAXVARLEN use string_f_c_mod implicit none -integer(c_int), intent(in) :: c_key_self -integer(c_int), intent(in) :: lvar +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: lvar character(kind=c_char, len=1), intent(in) :: c_var(lvar+1) -integer(c_int), intent(in) :: nlocs -real(c_float), intent(inout) :: values(nlocs) +integer(c_int), intent(in) :: nlocs +real(c_double), intent(inout) :: values(nlocs) character(max_string) :: err_msg type(ufo_geoval), pointer :: geoval @@ -493,11 +488,13 @@ subroutine ufo_geovals_get_c(c_key_self, lvar, c_var, lev, nlocs, values) bind(c call ufo_geovals_get_var(self, varname, geoval) if (lev<1 .or. lev>size(geoval%vals,1)) then - write(err_msg,*)'ufo_geovals_get_f90',trim(varname),'level out of range:',lev,size(geoval%vals,1) + write(err_msg,*)'ufo_geovals_get_f90 "',trim(varname),'" level out of range: 1~', & + size(geoval%vals,1), ', lev=', lev call abor1_ftn(err_msg) endif if (nlocs /= size(geoval%vals,2)) then - write(err_msg,*)'ufo_geovals_get_f90',trim(varname),'error locs number:',nlocs,size(geoval%vals,2) + write(err_msg,*)'ufo_geovals_get_f90 "',trim(varname),'" error locs number:',nlocs,& + ' /= ',size(geoval%vals,2) call abor1_ftn(err_msg) endif @@ -507,6 +504,46 @@ end subroutine ufo_geovals_get_c ! ------------------------------------------------------------------------------ +subroutine ufo_geovals_get_loc_c(c_key_self, lvar, c_var, c_loc, nlevs, values) bind(c, name='ufo_geovals_get_loc_f90') +use ufo_vars_mod, only: MAXVARLEN +use string_f_c_mod +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: lvar +character(kind=c_char, len=1), intent(in) :: c_var(lvar+1) +integer(c_int), intent(in) :: c_loc +integer(c_int), intent(in) :: nlevs +real(c_double), intent(inout) :: values(nlevs) + +character(max_string) :: err_msg +type(ufo_geoval), pointer :: geoval +character(len=MAXVARLEN) :: varname +type(ufo_geovals), pointer :: self +integer(c_int) :: loc + +call c_f_string(c_var, varname) +call ufo_geovals_registry%get(c_key_self, self) + +call ufo_geovals_get_var(self, varname, geoval) + +! Convert location index from the C++ to the Fortran convention. +loc = c_loc + 1 + +if (loc<1 .or. loc>size(geoval%vals,2)) then + write(err_msg,*)'ufo_geovals_get_loc_f90',trim(varname),'location out of range:',loc,size(geoval%vals,2) + call abor1_ftn(err_msg) +endif +if (nlevs /= size(geoval%vals,1)) then + write(err_msg,*)'ufo_geovals_get_loc_f90',trim(varname),'incorrect number of levels:',nlevs,size(geoval%vals,1) + call abor1_ftn(err_msg) +endif + +values(:) = geoval%vals(:,loc) + +end subroutine ufo_geovals_get_loc_c + +! ------------------------------------------------------------------------------ + subroutine ufo_geovals_getdouble_c(c_key_self, lvar, c_var, lev, nlocs, values)& bind(c, name='ufo_geovals_getdouble_f90') use ufo_vars_mod, only: MAXVARLEN @@ -534,32 +571,64 @@ end subroutine ufo_geovals_getdouble_c subroutine ufo_geovals_putdouble_c(c_key_self, lvar, c_var, lev, nlocs, values) bind(c, name='ufo_geovals_putdouble_f90') use ufo_vars_mod, only: MAXVARLEN -use oops_variables_mod use string_f_c_mod integer(c_int), intent(in) :: c_key_self integer(c_int), intent(in) :: lvar character(kind=c_char, len=1), intent(in) :: c_var(lvar+1) integer(c_int), intent(in) :: lev integer(c_int), intent(in) :: nlocs -real(c_double), intent(inout) :: values(nlocs) +real(c_double), intent(in) :: values(nlocs) -type(ufo_geoval) :: geoval -character(len=MAXVARLEN) :: varname +type(ufo_geoval), pointer :: geoval +character(len=MAXVARLEN) :: varname type(ufo_geovals), pointer :: self -type(oops_variables) :: var -integer :: nlev call c_f_string(c_var, varname) call ufo_geovals_registry%get(c_key_self, self) -self%geovals(1)%nval=5 -geoval%nval=self%geovals(1)%nval -geoval%nlocs=nlocs -allocate(geoval%vals(geoval%nval,geoval%nlocs)) +call ufo_geovals_get_var(self, varname, geoval) geoval%vals(lev,:) = values(:) -call ufo_geovals_put_var(self, varname, geoval, lev) end subroutine ufo_geovals_putdouble_c +! ------------------------------------------------------------------------------ +subroutine ufo_geovals_put_loc_c(c_key_self, lvar, c_var, c_loc, nlevs, values) bind(c, name='ufo_geovals_put_loc_f90') +use ufo_vars_mod, only: MAXVARLEN +use string_f_c_mod + +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: lvar +character(kind=c_char, len=1), intent(in) :: c_var(lvar+1) +integer(c_int), intent(in) :: c_loc +integer(c_int), intent(in) :: nlevs +real(c_double), intent(in) :: values(nlevs) + +character(max_string) :: err_msg +type(ufo_geoval), pointer :: geoval +character(len=MAXVARLEN) :: varname +type(ufo_geovals), pointer :: self +integer(c_int) :: loc + +call c_f_string(c_var, varname) +call ufo_geovals_registry%get(c_key_self, self) +call ufo_geovals_get_var(self, varname, geoval) + +! Convert location index from the C++ to the Fortran convention. +loc = c_loc + 1 + +if (loc<1 .or. loc>size(geoval%vals,2)) then + write(err_msg,*)'ufo_geovals_put_loc_f90',trim(varname),'location out of range:',loc,size(geoval%vals,2) + call abor1_ftn(err_msg) +endif +if (nlevs /= size(geoval%vals,1)) then + write(err_msg,*)'ufo_geovals_put_loc_f90',trim(varname),'incorrect number of levels:',nlevs,size(geoval%vals,1) + call abor1_ftn(err_msg) +endif + +geoval%vals(:,loc) = values(:) + +end subroutine ufo_geovals_put_loc_c + ! ------------------------------------------------------------------------------ subroutine ufo_geovals_maxloc_c(c_key_self, mxval, iloc, ivar) bind(c,name='ufo_geovals_maxloc_f90') @@ -617,16 +686,15 @@ end subroutine ufo_geovals_read_file_c ! ------------------------------------------------------------------------------ -subroutine ufo_geovals_write_file_c(c_key_self, c_conf, c_comm) bind(c,name='ufo_geovals_write_file_f90') +subroutine ufo_geovals_write_file_c(c_key_self, c_conf, c_rank) bind(c,name='ufo_geovals_write_file_f90') implicit none integer(c_int), intent(in) :: c_key_self type(c_ptr), value, intent(in) :: c_conf -type(c_ptr), value, intent(in) :: c_comm +integer(c_size_t), intent(in) :: c_rank ! mpi rank (to be added to filename) type(ufo_geovals), pointer :: self character(max_string) :: fout, filename -type(fckit_mpi_comm) :: comm character(len=10) :: cproc integer :: ppos character(len=:), allocatable :: str @@ -637,10 +705,7 @@ subroutine ufo_geovals_write_file_c(c_key_self, c_conf, c_comm) bind(c,name='ufo call f_conf%get_or_die("filename",str) filename = str -! get the process rank number -comm = fckit_mpi_comm(c_comm) - -write(cproc,fmt='(i4.4)') comm%rank() +write(cproc,fmt='(i4.4)') c_rank ! Find the left-most dot in the file name, and use that to pick off the file name ! and file extension. diff --git a/src/ufo/GeoVaLs.interface.h b/src/ufo/GeoVaLs.interface.h index 72ab87728..41f50e581 100644 --- a/src/ufo/GeoVaLs.interface.h +++ b/src/ufo/GeoVaLs.interface.h @@ -8,7 +8,6 @@ #ifndef UFO_GEOVALS_INTERFACE_H_ #define UFO_GEOVALS_INTERFACE_H_ -#include "eckit/mpi/Comm.h" #include "Fortran.h" // Forward declarations @@ -20,7 +19,13 @@ namespace ioda { class ObsSpace; } +namespace oops { + class Variables; +} + + namespace ufo { + class Locations; /// Interface to Fortran UFO GeoVals routines /*! @@ -30,7 +35,8 @@ namespace ufo { extern "C" { void ufo_geovals_default_constr_f90(F90goms &); - void ufo_geovals_setup_f90(F90goms &, const F90locs &, const oops::Variables &); + void ufo_geovals_setup_f90(F90goms &, const size_t &, const oops::Variables &); + void ufo_geovals_allocate_f90(F90goms &, const size_t &, const oops::Variables &); void ufo_geovals_delete_f90(F90goms &); void ufo_geovals_copy_f90(const F90goms &, F90goms &); void ufo_geovals_copy_one_f90(F90goms &, const F90goms &, int &); @@ -39,7 +45,7 @@ extern "C" { const int &, const char *); void ufo_geovals_abs_f90(const F90goms &); void ufo_geovals_rms_f90(const F90goms &, double &); - void ufo_geovals_analytic_init_f90(F90goms &, const F90locs &, + void ufo_geovals_analytic_init_f90(F90goms &, const Locations &, const eckit::Configuration &); void ufo_geovals_random_f90(const F90goms &); void ufo_geovals_scalmult_f90(const F90goms &, const double &); @@ -49,8 +55,6 @@ extern "C" { void ufo_geovals_diff_f90(const F90goms &, const F90goms &); void ufo_geovals_schurmult_f90(const F90goms &, const F90goms &); void ufo_geovals_normalize_f90(const F90goms &, const F90goms &); - void ufo_geovals_dotprod_f90(const F90goms &, const F90goms &, double &, - const eckit::mpi::Comm &); void ufo_geovals_split_f90(const F90goms &, const F90goms &, const F90goms &); void ufo_geovals_merge_f90(const F90goms &, const F90goms &, const F90goms &); void ufo_geovals_minmaxavg_f90(const F90goms &, int &, int &, double &, double &, double &); @@ -58,18 +62,21 @@ extern "C" { void ufo_geovals_nlocs_f90(const F90goms &, size_t &); void ufo_geovals_nlevs_f90(const F90goms &, const int &, const char *, int &); void ufo_geovals_get2d_f90(const F90goms &, const int &, const char *, const int &, - float &); + double &); void ufo_geovals_get_f90(const F90goms &, const int &, const char *, const int &, const int &, float &); + void ufo_geovals_get_loc_f90(const F90goms &, const int &, const char *, const int &, + const int &, double &); void ufo_geovals_getdouble_f90(const F90goms &, const int &, const char *, const int &, const int &, double &); void ufo_geovals_putdouble_f90(const F90goms &, const int &, const char *, const int &, const int &, const double &); + void ufo_geovals_put_loc_f90(const F90goms &, const int &, const char *, const int &, + const int &, const double &); void ufo_geovals_read_file_f90(const F90goms &, const eckit::Configuration &, const ioda::ObsSpace &, const oops::Variables &); - void ufo_geovals_write_file_f90(const F90goms &, const eckit::Configuration &, - const eckit::mpi::Comm &); + void ufo_geovals_write_file_f90(const F90goms &, const eckit::Configuration &, const size_t &); } // extern C } // namespace ufo diff --git a/src/ufo/LinearObsBiasOperator.cc b/src/ufo/LinearObsBiasOperator.cc new file mode 100644 index 000000000..a921ba1e2 --- /dev/null +++ b/src/ufo/LinearObsBiasOperator.cc @@ -0,0 +1,89 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/LinearObsBiasOperator.h" + +#include +#include + +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" + +#include "ufo/ObsBias.h" +#include "ufo/ObsBiasIncrement.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- + +LinearObsBiasOperator::LinearObsBiasOperator(ioda::ObsSpace & odb) + : odb_(odb) { + oops::Log::trace() << "LinearObsBiasOperator::create done." << std::endl; +} + +// ----------------------------------------------------------------------------- + +void LinearObsBiasOperator::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, + ObsDiagnostics & ydiags) { + oops::Log::trace() << "LinearObsBiasOperator::setTrajectory starts." << std::endl; + const std::vector> variablePredictors = + bias.variablePredictors(); + const std::size_t npreds = variablePredictors.size(); + + predData_.resize(npreds, ioda::ObsVector(odb_)); + for (std::size_t p = 0; p < npreds; ++p) { + variablePredictors[p]->compute(odb_, geovals, ydiags, predData_[p]); + } + + oops::Log::trace() << "LinearObsBiasOperator::setTrajectory done." << std::endl; +} + +// ----------------------------------------------------------------------------- + +void LinearObsBiasOperator::computeObsBiasTL(const GeoVaLs & geovals, + const ObsBiasIncrement & biascoeffinc, + ioda::ObsVector & ybiasinc) const { + oops::Log::trace() << "LinearObsBiasOperator::computeObsBiasTL starts." << std::endl; + + const size_t npreds = predData_.size(); + + ybiasinc.zero(); + for (size_t jpred = 0; jpred < npreds; ++jpred) { + ybiasinc.axpy(biascoeffinc.coefficients(jpred), predData_[jpred]); + } + + oops::Log::trace() << "LinearObsBiasOperator::computeObsBiasTL done." << std::endl; +} + +// ----------------------------------------------------------------------------- + +void LinearObsBiasOperator::computeObsBiasAD(GeoVaLs & geovals, + ObsBiasIncrement & biascoeffinc, + const ioda::ObsVector & ybiasinc) const { + oops::Log::trace() << "LinearObsBiasOperator::computeObsBiasAD starts." << std::endl; + + const size_t npreds = predData_.size(); + + for (std::size_t jpred = 0; jpred < npreds; ++jpred) { + biascoeffinc.updateCoeff(jpred, predData_[jpred].multivar_dot_product_with(ybiasinc)); + } + + oops::Log::trace() << "LinearObsBiasOperator::computeAD done." << std::endl; +} + +// ----------------------------------------------------------------------------- + +void LinearObsBiasOperator::print(std::ostream & os) const { + os << "TL/AD bias computed as linear combination of predictors."; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/LinearObsBiasOperator.h b/src/ufo/LinearObsBiasOperator.h new file mode 100644 index 000000000..facc56cc6 --- /dev/null +++ b/src/ufo/LinearObsBiasOperator.h @@ -0,0 +1,55 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_LINEAROBSBIASOPERATOR_H_ +#define UFO_LINEAROBSBIASOPERATOR_H_ + +#include + +#include "oops/util/Printable.h" + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsBias; + class ObsBiasIncrement; + class ObsDiagnostics; + +/// Tangent linear and adjoint of the linear combination bias correction operator +class LinearObsBiasOperator : public util::Printable { + public: + explicit LinearObsBiasOperator(ioda::ObsSpace &); + + /// Set trajectory (save predictors) + void setTrajectory(const GeoVaLs &, const ObsBias &, ObsDiagnostics &); + /// Compute TL of bias correction + void computeObsBiasTL(const GeoVaLs &, const ObsBiasIncrement &, + ioda::ObsVector &) const; + /// Compute adjoint of bias correction + void computeObsBiasAD(GeoVaLs &, ObsBiasIncrement &, + const ioda::ObsVector &) const; + + private: + /// Print used for logging + void print(std::ostream &) const override; + + /// ObsSpace used for this bias correction + ioda::ObsSpace & odb_; + + /// predictors values; set in setTrajectory + std::vector predData_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_LINEAROBSBIASOPERATOR_H_ diff --git a/src/ufo/LinearObsOperator.cc b/src/ufo/LinearObsOperator.cc index ed117156e..5199e0588 100644 --- a/src/ufo/LinearObsOperator.cc +++ b/src/ufo/LinearObsOperator.cc @@ -5,9 +5,11 @@ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#include "ioda/ObsVector.h" - #include "ufo/LinearObsOperator.h" + +#include + +#include "ioda/ObsVector.h" #include "ufo/LinearObsOperatorBase.h" #include "ufo/Locations.h" #include "ufo/ObsBias.h" @@ -19,22 +21,37 @@ namespace ufo { // ----------------------------------------------------------------------------- LinearObsOperator::LinearObsOperator(ioda::ObsSpace & os, const eckit::Configuration & conf) - : oper_(LinearObsOperatorFactory::create(os, conf)), odb_(os), biaspreds_() -{} - -// ----------------------------------------------------------------------------- - -LinearObsOperator::~LinearObsOperator() {} + : oper_(LinearObsOperatorFactory::create(os, conf)), odb_(os) +{ + // We use += rather than = to make sure the Variables objects contain no duplicate entries + // and the variables are sorted alphabetically. + oops::Variables operatorVars; + operatorVars += oper_->simulatedVars(); + oops::Variables obsSpaceVars; + obsSpaceVars += os.obsvariables(); + if (!(operatorVars == obsSpaceVars)) + throw eckit::UserError("The list of variables simulated by the obs operator differs from " + "the list of simulated variables in the obs space", + Here()); +} // ----------------------------------------------------------------------------- void LinearObsOperator::setTrajectory(const GeoVaLs & gvals, const ObsBias & bias) { oops::Variables vars; vars += bias.requiredHdiagnostics(); - ObsDiagnostics ydiags(odb_, Locations(odb_, odb_.windowStart(), odb_.windowEnd()), vars); + std::vector lons(odb_.nlocs()); + std::vector lats(odb_.nlocs()); + std::vector times(odb_.nlocs()); + odb_.get_db("MetaData", "latitude", lats); + odb_.get_db("MetaData", "longitude", lons); + odb_.get_db("MetaData", "datetime", times); + ObsDiagnostics ydiags(odb_, Locations(lons, lats, times, odb_.distribution()), vars); oper_->setTrajectory(gvals, bias, ydiags); - biaspreds_.clear(); - biaspreds_ = bias.computePredictors(gvals, ydiags); + if (bias) { + biasoper_.reset(new LinearObsBiasOperator(odb_)); + biasoper_->setTrajectory(gvals, bias, ydiags); + } } // ----------------------------------------------------------------------------- @@ -44,7 +61,7 @@ void LinearObsOperator::simulateObsTL(const GeoVaLs & gvals, ioda::ObsVector & y oper_->simulateObsTL(gvals, yy); if (bias) { ioda::ObsVector ybiasinc(odb_); - bias.computeObsBiasTL(gvals, biaspreds_, ybiasinc); + biasoper_->computeObsBiasTL(gvals, bias, ybiasinc); yy += ybiasinc; } } @@ -56,7 +73,7 @@ void LinearObsOperator::simulateObsAD(GeoVaLs & gvals, const ioda::ObsVector & y oper_->simulateObsAD(gvals, yy); if (bias) { ioda::ObsVector ybiasinc(yy); - bias.computeObsBiasAD(gvals, biaspreds_, ybiasinc); + biasoper_->computeObsBiasAD(gvals, bias, ybiasinc); } } diff --git a/src/ufo/LinearObsOperator.h b/src/ufo/LinearObsOperator.h index f12af2661..7557f1ef8 100644 --- a/src/ufo/LinearObsOperator.h +++ b/src/ufo/LinearObsOperator.h @@ -8,14 +8,13 @@ #ifndef UFO_LINEAROBSOPERATOR_H_ #define UFO_LINEAROBSOPERATOR_H_ -#include - #include -#include #include #include "oops/util/Printable.h" +#include "ufo/LinearObsBiasOperator.h" +#include "ufo/LinearObsOperatorBase.h" // Forward declarations namespace eckit { @@ -35,7 +34,6 @@ namespace ufo { class GeoVaLs; class ObsBias; class ObsBiasIncrement; - class LinearObsOperatorBase; // ----------------------------------------------------------------------------- @@ -43,7 +41,6 @@ class LinearObsOperator : public util::Printable, private boost::noncopyable { public: LinearObsOperator(ioda::ObsSpace &, const eckit::Configuration &); - ~LinearObsOperator(); /// Obs Operator void setTrajectory(const GeoVaLs &, const ObsBias &); @@ -56,8 +53,8 @@ class LinearObsOperator : public util::Printable, private: void print(std::ostream &) const; std::unique_ptr oper_; + std::unique_ptr biasoper_; ioda::ObsSpace & odb_; - std::vector biaspreds_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/LinearObsOperatorBase.cc b/src/ufo/LinearObsOperatorBase.cc index c44d8932d..ef9cad5db 100644 --- a/src/ufo/LinearObsOperatorBase.cc +++ b/src/ufo/LinearObsOperatorBase.cc @@ -16,6 +16,12 @@ namespace ufo { // ----------------------------------------------------------------------------- +oops::Variables LinearObsOperatorBase::simulatedVars() const { + return odb_.obsvariables(); +} + +// ----------------------------------------------------------------------------- + LinearObsOperatorFactory::LinearObsOperatorFactory(const std::string & name) { if (getMakers().find(name) != getMakers().end()) { oops::Log::error() << name << " already registered in ufo::LinearObsOperatorFactory." diff --git a/src/ufo/LinearObsOperatorBase.h b/src/ufo/LinearObsOperatorBase.h index 008a43e3e..ba0bd1b1e 100644 --- a/src/ufo/LinearObsOperatorBase.h +++ b/src/ufo/LinearObsOperatorBase.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2017-2018 UCAR + * (C) Copyright 2017-2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -34,7 +34,8 @@ class ObsDiagnostics; class LinearObsOperatorBase : public util::Printable, private boost::noncopyable { public: - LinearObsOperatorBase() {} + explicit LinearObsOperatorBase(const ioda::ObsSpace & odb) + : odb_(odb) {} virtual ~LinearObsOperatorBase() {} /// Obs Operator @@ -45,8 +46,19 @@ class LinearObsOperatorBase : public util::Printable, /// Operator input required from Model virtual const oops::Variables & requiredVars() const = 0; +/// \brief List of variables simulated by this operator. +/// +/// The default implementation returns the list of all simulated variables in the ObsSpace. + virtual oops::Variables simulatedVars() const; + +/// \brief The space containing the observations to be simulated by this operator. + const ioda::ObsSpace &obsspace() const { return odb_; } + private: virtual void print(std::ostream &) const = 0; + + private: + const ioda::ObsSpace & odb_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/Locations.cc b/src/ufo/Locations.cc index 5ecf0c2ae..8c98ec740 100644 --- a/src/ufo/Locations.cc +++ b/src/ufo/Locations.cc @@ -1,5 +1,5 @@ /* - * (C) Copyright 2017 UCAR + * (C) Copyright 2017-2020 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -7,33 +7,28 @@ #include "ufo/Locations.h" -#include #include #include "eckit/config/Configuration.h" #include "eckit/exception/Exceptions.h" +#include "ioda/ObsSpace.h" + #include "oops/mpi/mpi.h" #include "oops/util/DateTime.h" #include "oops/util/Logger.h" -#include "oops/util/Random.h" - -#include "ufo/Locations.interface.h" namespace ufo { // ------------------------------------------------------------------------------------------------- -Locations::Locations(const eckit::mpi::Comm & comm) : comm_(comm) { - int nobs = 0; - ufo_locs_setup_f90(keyLoc_, nobs); -} - -// ------------------------------------------------------------------------------------------------- - -Locations::Locations(const ioda::ObsSpace & odb, - const util::DateTime & t1, const util::DateTime & t2) : comm_(odb.comm()) { - ufo_locs_init_f90(keyLoc_, odb, t1, t2); +Locations::Locations(const std::vector & lons, const std::vector & lats, + const std::vector & times, + const std::shared_ptr dist) + : dist_(dist), lons_(lons), lats_(lats), times_(times) { + ASSERT(lons_.size() == lats_.size()); + ASSERT(lats_.size() == times_.size()); + oops::Log::trace() << "ufo::Locations::Locations done" << std::endl; } // ------------------------------------------------------------------------------------------------- @@ -57,69 +52,52 @@ Locations::Locations(const ioda::ObsSpace & odb, */ Locations::Locations(const eckit::Configuration & conf, - const eckit::mpi::Comm & comm) : comm_(comm) { + const eckit::mpi::Comm & comm) { const eckit::LocalConfiguration obsconf(conf, "obs space"); const util::DateTime bgn = util::DateTime(conf.getString("window begin")); const util::DateTime end = util::DateTime(conf.getString("window end")); ioda::ObsSpace obspace(obsconf, comm, bgn, end, oops::mpi::myself()); - const int nlocs = obspace.nlocs(); + const size_t nlocs = obspace.nlocs(); + dist_ = obspace.distribution(); - std::vector lats(nlocs); - std::vector lons(nlocs); - obspace.get_db("MetaData", "latitude", lats); - obspace.get_db("MetaData", "longitude", lons); + lats_.resize(nlocs); + lons_.resize(nlocs); + times_.resize(nlocs); - ufo_locs_create_f90(keyLoc_, nlocs, obspace, &lats[0], &lons[0]); -} - -// ------------------------------------------------------------------------------------------------- - -Locations::Locations(const Locations & other) : comm_(other.comm_) { - ufo_locs_copy_f90(keyLoc_, other.toFortran()); + obspace.get_db("MetaData", "latitude", lats_); + obspace.get_db("MetaData", "longitude", lons_); + obspace.get_db("MetaData", "datetime", times_); } // ------------------------------------------------------------------------------------------------- Locations & Locations::operator+=(const Locations & other) { - F90locs otherKeyLoc_ = other.toFortran(); - ufo_locs_concatenate_f90(keyLoc_, otherKeyLoc_); + lons_.insert(lons_.end(), other.lons_.begin(), other.lons_.end()); + lats_.insert(lats_.end(), other.lats_.begin(), other.lats_.end()); + times_.insert(times_.end(), other.times_.begin(), other.times_.end()); return *this; } // ------------------------------------------------------------------------------------------------- - -Locations::~Locations() { - ufo_locs_delete_f90(keyLoc_); +std::vector Locations::isInTimeWindow(const util::DateTime & t1, + const util::DateTime & t2) const { + std::vector isIn(times_.size(), false); + for (size_t ii = 0; ii < times_.size(); ++ii) { + if (t1 < times_[ii] && times_[ii] <= t2) isIn[ii] = true; + } + return isIn; } // ------------------------------------------------------------------------------------------------- -int Locations::nobs() const { - int nobs; - ufo_locs_nobs_f90(keyLoc_, nobs); - return nobs; +size_t Locations::size() const { + return lats_.size(); } // ------------------------------------------------------------------------------------------------- void Locations::print(std::ostream & os) const { - int nobs, indx, max_indx, i(0); - ufo_locs_nobs_f90(keyLoc_, nobs); - ufo_locs_indx_f90(keyLoc_, i, indx, max_indx); - os << "Locations: " << nobs << " locations: " - << max_indx << " maximum indx:"; - - // Write lat and lon to debug stream - double lat, lon; - - for (int i=0; i < nobs; ++i) { - ufo_locs_indx_f90(keyLoc_, i, indx, max_indx); - ufo_locs_coords_f90(keyLoc_, i, lat, lon); - - oops::Log::debug() << "obs " << i << ": " << "gv index = " << indx - << std::setprecision(2) << std::fixed - << " lat = " << lat << ", lon = " << lon << std::endl; - } + os << "Lat/lon/time locations: " << size() << " locations on this task " << std::endl; } // ------------------------------------------------------------------------------------------------- diff --git a/src/ufo/Locations.h b/src/ufo/Locations.h index 9bea7b651..f0c9bf118 100644 --- a/src/ufo/Locations.h +++ b/src/ufo/Locations.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2017 UCAR + * (C) Copyright 2017-2020 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -8,47 +8,62 @@ #ifndef UFO_LOCATIONS_H_ #define UFO_LOCATIONS_H_ +#include #include #include +#include #include "eckit/mpi/Comm.h" +#include "ioda/distribution/Distribution.h" #include "oops/util/DateTime.h" #include "oops/util/ObjectCounter.h" #include "oops/util/Printable.h" -#include "ioda/ObsSpace.h" - -#include "ufo/Locations.interface.h" - namespace eckit { class Configuration; } namespace ufo { -/// Locations class to handle locations for UFO. - +/// \brief Locations class to handle simple lat-lon-time locations class Locations : public util::Printable, private util::ObjectCounter { public: static const std::string classname() {return "ufo::Locations";} - explicit Locations(const eckit::mpi::Comm &); - Locations(const ioda::ObsSpace &, const util::DateTime &, const util::DateTime &); + /// \brief constructor from passed \p lons, \p lats, \p times + Locations(const std::vector & lons, const std::vector & lats, + const std::vector & times, + const std::shared_ptr); + /// \brief constructor used in oops tests Locations(const eckit::Configuration &, const eckit::mpi::Comm &); - explicit Locations(const ufo::Locations &); - ~Locations(); + /// append locations with more locations Locations & operator+=(const Locations &); - int nobs() const; - int toFortran() const {return keyLoc_;} - const eckit::mpi::Comm & getComm() const {return comm_;} + /// find which observations are in the (\p t1, \p t2] time window + std::vector isInTimeWindow(const util::DateTime & t1, const util::DateTime & t2) const; + + /// size of locations + size_t size() const; + + /// accessor to the observations MPI distribution + const std::shared_ptr & distribution() const {return dist_;} + /// accessor to observation longitudes (on current MPI task) + const std::vector & lons() const {return lons_;} + /// accessor to observation latitudes (on current MPI task) + const std::vector & lats() const {return lats_;} + /// accessor to DateTimes (on current MPI task) + const std::vector & times() const {return times_;} + private: - void print(std::ostream & os) const; - F90locs keyLoc_; - const eckit::mpi::Comm & comm_; + void print(std::ostream & os) const override; + + std::shared_ptr dist_; /// observations MPI distribution + std::vector lons_; /// longitudes on current MPI task + std::vector lats_; /// latitudes on current MPI task + std::vector times_; /// times of observations on current MPI task }; } // namespace ufo diff --git a/src/ufo/Locations.interface.F90 b/src/ufo/Locations.interface.F90 deleted file mode 100644 index 43ef88fcd..000000000 --- a/src/ufo/Locations.interface.F90 +++ /dev/null @@ -1,192 +0,0 @@ -! -! (C) Copyright 2017 UCAR -! -! This software is licensed under the terms of the Apache Licence Version 2.0 -! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -! - -module ufo_locs_mod_c - -use iso_c_binding -use ufo_locs_mod -use kinds - -implicit none - -public :: ufo_locs_registry - -private - -! ------------------------------------------------------------------------------ - -#define LISTED_TYPE ufo_locs - -!> Linked list interface - defines registry_t type -#include "oops/util/linkedList_i.f" - -!> Global registry -type(registry_t) :: ufo_locs_registry - -! ------------------------------------------------------------------------------ -contains -! ------------------------------------------------------------------------------ -!> Linked list implementation -#include "oops/util/linkedList_c.f" - -! ------------------------------------------------------------------------------ - -subroutine ufo_locs_create_c(key, klocs, c_obsspace, klats, klons) bind(c,name='ufo_locs_create_f90') - -implicit none -integer(c_int), intent(inout) :: key -integer(c_int), intent(in) :: klocs -type(c_ptr), value, intent(in) :: c_obsspace -real(c_double), intent(in) :: klats(klocs) -real(c_double), intent(in) :: klons(klocs) - -type(ufo_locs), pointer :: self -real(kind_real) :: lats(klocs) -real(kind_real) :: lons(klocs) - -call ufo_locs_registry%setup(key, self) - -lats(:) = klats(:) -lons(:) = klons(:) - -call ufo_locs_create(self, c_obsspace, klocs, lats, lons) - -end subroutine ufo_locs_create_c - -! ------------------------------------------------------------------------------ - -subroutine ufo_locs_setup_c(key, nlocs) bind(c,name='ufo_locs_setup_f90') - -implicit none -integer(c_int), intent(inout) :: key -integer(c_int), intent(in) :: nlocs -type(ufo_locs), pointer :: self - -call ufo_locs_registry%setup(key, self) - -call ufo_locs_setup(self, nlocs) - -end subroutine ufo_locs_setup_c - -!------------------------------------------------------------------------------ - -subroutine ufo_locs_copy_c(key, key2) bind(c,name='ufo_locs_copy_f90') - -implicit none -integer(c_int), intent(inout) :: key -integer(c_int), intent(in) :: key2 - -type(ufo_locs), pointer :: self -type(ufo_locs), pointer :: other - -call ufo_locs_registry%setup(key, self) -call ufo_locs_registry%get(key2, other) - -call ufo_locs_copy(self, other) - -end subroutine ufo_locs_copy_c - -! ------------------------------------------------------------------------------ - -subroutine ufo_locs_delete_c(key) bind(c,name='ufo_locs_delete_f90') - -implicit none -integer(c_int), intent(inout) :: key -type(ufo_locs), pointer :: self - -call ufo_locs_registry%get(key,self) -call ufo_locs_delete(self) -call ufo_locs_registry%remove(key) - -end subroutine ufo_locs_delete_c - -! ------------------------------------------------------------------------------ - -subroutine ufo_locs_nobs_c(key, kobs) bind(c,name='ufo_locs_nobs_f90') - -implicit none -integer(c_int), intent(in) :: key -integer(c_int), intent(inout) :: kobs -type(ufo_locs), pointer :: self - -call ufo_locs_registry%get(key,self) -kobs = self%nlocs - -end subroutine ufo_locs_nobs_c -! ------------------------------------------------------------------------------ - -subroutine ufo_locs_coords_c(key, idx, mylat, mylon) bind(c,name='ufo_locs_coords_f90') - -implicit none -integer(c_int), intent(in) :: key -integer(c_int), intent(in) :: idx -real(c_double), intent(inout) :: mylat,mylon - -type(ufo_locs), pointer :: self - -call ufo_locs_registry%get(key,self) -mylat = self%lat(idx+1) -mylon = self%lon(idx+1) - -end subroutine ufo_locs_coords_c -!--------------------------------------------------------------------------------- - -subroutine ufo_locs_indx_c(key, idx, indx, max_indx) bind(c,name='ufo_locs_indx_f90') - -implicit none -integer(c_int), intent(in) :: key -integer(c_int), intent(in) :: idx -integer(c_int), intent(inout) :: indx -integer(c_int), intent(inout) :: max_indx - -type(ufo_locs), pointer :: self - -call ufo_locs_registry%get(key, self) -max_indx = self%max_indx -if (max_indx > 0) indx = self%indx(idx+1) - 1 ! the minus to take account of C++ starting from 0 - - -end subroutine ufo_locs_indx_c -!------------------------------------------------------------------------------ -subroutine ufo_locs_concatenate_c(key, key2) bind(c,name='ufo_locs_concatenate_f90') - -implicit none -integer(c_int), intent(in) :: key -integer(c_int), intent(in) :: key2 - -type(ufo_locs), pointer :: self -type(ufo_locs), pointer :: other - -call ufo_locs_registry%get(key, self) -call ufo_locs_registry%get(key2, other) -call ufo_locs_concatenate(self, other) - -end subroutine ufo_locs_concatenate_c -! ------------------------------------------------------------------------------ - -subroutine ufo_locs_init_c(c_key_self, c_obsspace, c_t1, c_t2) bind(c,name='ufo_locs_init_f90') -use datetime_mod -implicit none -integer(c_int), intent(inout) :: c_key_self -type(c_ptr), value, intent(in) :: c_obsspace -type(c_ptr), value, intent(in) :: c_t1, c_t2 - -type(ufo_locs), pointer :: self - -type(datetime) :: t1, t2 - -call ufo_locs_registry%setup(c_key_self, self) - -call c_f_datetime(c_t1, t1) -call c_f_datetime(c_t2, t2) - -call ufo_locs_registry%get(c_key_self, self) -call ufo_locs_init(self, c_obsspace, t1, t2) - -end subroutine ufo_locs_init_c - -end module ufo_locs_mod_c diff --git a/src/ufo/Locations.interface.h b/src/ufo/Locations.interface.h deleted file mode 100644 index 6fe7f4053..000000000 --- a/src/ufo/Locations.interface.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * (C) Copyright 2017-2018 UCAR - * - * This software is licensed under the terms of the Apache Licence Version 2.0 - * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - */ - -#ifndef UFO_LOCATIONS_INTERFACE_H_ -#define UFO_LOCATIONS_INTERFACE_H_ - -#include "ioda/ObsSpace.h" - -#include "Fortran.h" - -namespace ufo { - -/// Interface to Fortran UFO Locations routines -/*! - * The core of the UFO Locations is coded in Fortran. - * Here we define the interfaces to the Fortran code. - */ - -extern "C" { - - void ufo_locs_init_f90(F90locs &, const ioda::ObsSpace &, - const util::DateTime &, const util::DateTime &); - void ufo_locs_create_f90(F90locs &, const int &, const ioda::ObsSpace &, - const double *, const double *); - void ufo_locs_copy_f90(F90locs &, const F90locs &); - void ufo_locs_setup_f90(F90locs &, const int &); - void ufo_locs_delete_f90(F90locs &); - void ufo_locs_nobs_f90(const F90locs &, int &); - void ufo_locs_coords_f90(const F90locs &, int &, double &, double &); - void ufo_locs_indx_f90(const F90locs &, int &, int &, int &); - void ufo_locs_concatenate_f90(const F90locs &, const F90locs &); - -// ----------------------------------------------------------------------------- - -} // extern C - -} // namespace ufo -#endif // UFO_LOCATIONS_INTERFACE_H_ diff --git a/src/ufo/ObsBias.cc b/src/ufo/ObsBias.cc index c450290d9..6443eddb3 100644 --- a/src/ufo/ObsBias.cc +++ b/src/ufo/ObsBias.cc @@ -1,5 +1,5 @@ /* - * (C) Copyright 2017-2019 UCAR + * (C) Copyright 2017-2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -7,11 +7,14 @@ #include "ufo/ObsBias.h" -#include +#include #include #include #include +#include "ioda/Engines/Factory.h" +#include "ioda/Layout.h" +#include "ioda/ObsGroup.h" #include "ioda/ObsVector.h" #include "oops/base/Variables.h" @@ -20,50 +23,32 @@ #include "ufo/ObsBiasIncrement.h" #include "ufo/ObsDiagnostics.h" +#include "ufo/utils/IodaGroupIndices.h" namespace ufo { // ----------------------------------------------------------------------------- -ObsBias::ObsBias(ioda::ObsSpace & odb, const eckit::Configuration & conf) - : predbases_(0), jobs_(0), odb_(odb), conf_(conf) { +ObsBias::ObsBias(ioda::ObsSpace & odb, const ObsBiasParameters & params) + : numStaticPredictors_(0), numVariablePredictors_(0), vars_(odb.obsvariables()) { oops::Log::trace() << "ObsBias::create starting." << std::endl; - // Get the jobs(channels) - if (conf_.has("obs bias.jobs")) { - const std::set jobs = oops::parseIntSet(conf_.getString("obs bias.jobs")); - jobs_.assign(jobs.begin(), jobs.end()); - } - // Predictor factory - if (conf_.has("obs bias.predictors")) { - std::vector confs; - conf_.get("obs bias.predictors", confs); - for (std::size_t j = 0; j < confs.size(); ++j) { - std::shared_ptr pred(PredictorFactory::create(confs[j], jobs_)); - predbases_.push_back(pred); - prednames_.push_back(pred->name()); - geovars_ += pred->requiredGeovars(); - hdiags_ += pred->requiredHdiagnostics(); - - // Reserve the space for ObsBiasTerm for predictor - if (jobs_.size() > 0) { - for (auto & job : jobs_) { - hdiags_ += oops::Variables({prednames_.back() + "_" + std::to_string(job)}); - } - } else { - hdiags_ += oops::Variables({prednames_.back()}); - } - } + for (const eckit::LocalConfiguration &conf : params.staticBC.value().predictors.value()) { + initPredictor(conf); + ++numStaticPredictors_; + } + for (const eckit::LocalConfiguration &conf : params.variationalBC.value().predictors.value()) { + initPredictor(conf); + ++numVariablePredictors_; } - if (prednames_.size() * jobs_.size() > 0) { - // Initialize the biascoeffs to ZERO - biascoeffs_.resize(prednames_.size() * jobs_.size()); - std::fill(biascoeffs_.begin(), biascoeffs_.end(), 0.0); - + if (prednames_.size() * vars_.size() > 0) { + // Initialize the coefficients of variable predictors to 0. (Coefficients of static predictors + // are not stored; they are always equal to 1.) + biascoeffs_ = Eigen::VectorXd::Zero(numVariablePredictors_ * vars_.size()); // Read or initialize bias coefficients - this->read(conf); + this->read(params); } oops::Log::trace() << "ObsBias::create done." << std::endl; @@ -72,14 +57,16 @@ ObsBias::ObsBias(ioda::ObsSpace & odb, const eckit::Configuration & conf) // ----------------------------------------------------------------------------- ObsBias::ObsBias(const ObsBias & other, const bool copy) - : odb_(other.odb_), conf_(other.conf_), predbases_(other.predbases_), - prednames_(other.prednames_), jobs_(other.jobs_), + : predictors_(other.predictors_), + prednames_(other.prednames_), + numStaticPredictors_(other.numStaticPredictors_), + numVariablePredictors_(other.numVariablePredictors_), + vars_(other.vars_), geovars_(other.geovars_), hdiags_(other.hdiags_) { oops::Log::trace() << "ObsBias::copy ctor starting." << std::endl; // Initialize the biascoeffs - biascoeffs_.resize(prednames_.size() * jobs_.size()); - std::fill(biascoeffs_.begin(), biascoeffs_.end(), 0.0); + biascoeffs_ = Eigen::VectorXd::Zero(numVariablePredictors_ * vars_.size()); // Copy the bias coeff data if (copy && biascoeffs_.size() > 0) *this = other; @@ -90,8 +77,7 @@ ObsBias::ObsBias(const ObsBias & other, const bool copy) // ----------------------------------------------------------------------------- ObsBias & ObsBias::operator+=(const ObsBiasIncrement & dx) { - for (std::size_t jj = 0; jj < biascoeffs_.size(); ++jj) - biascoeffs_[jj] += dx[jj]; + biascoeffs_ += dx.data(); return *this; } @@ -99,11 +85,12 @@ ObsBias & ObsBias::operator+=(const ObsBiasIncrement & dx) { ObsBias & ObsBias::operator=(const ObsBias & rhs) { if (rhs.size() > 0 && this->size() == rhs.size()) { - conf_ = rhs.conf_; biascoeffs_ = rhs.biascoeffs_; - predbases_ = rhs.predbases_; + predictors_ = rhs.predictors_; prednames_ = rhs.prednames_; - jobs_ = rhs.jobs_; + numStaticPredictors_ = rhs.numStaticPredictors_; + numVariablePredictors_ = rhs.numVariablePredictors_; + vars_ = rhs.vars_; geovars_ = rhs.geovars_; hdiags_ = rhs.hdiags_; } @@ -112,86 +99,47 @@ ObsBias & ObsBias::operator=(const ObsBias & rhs) { // ----------------------------------------------------------------------------- -void ObsBias::read(const eckit::Configuration & conf) { +void ObsBias::read(const Parameters_ & params) { oops::Log::trace() << "ObsBias::read and initialize from file, starting "<< std::endl; - // Bias coefficients input file name - std::string input_filename; - if (conf.has("obs bias.abias_in")) { - input_filename = conf.getString("obs bias.abias_in"); - oops::Log::debug() << "ObsBias::initialize coefficients from file: " - << input_filename << std::endl; - - // Default predictor names from GSI - // temporary solution, we should have a self-explanatory obsbias file - const std::vector gsi_predictors = {"constant", - "zenith_angle", - "cloud_liquid_water", - "lapse_rate_order_2", - "lapse_rate", - "cosine_of_latitude_times_orbit_node", - "sine_of_latitude", - "emissivity", - "scan_angle_order_4", - "scan_angle_order_3", - "scan_angle_order_2", - "scan_angle" - }; - std::ifstream infile(input_filename); - - // get the sensor id - std::string sensor = conf.getString("obs bias.sensor"); - oops::Log::debug() << "ObsBias::initialize coefficients for sensor: " - << sensor << std::endl; - - std::size_t ich; // sequential number - std::string nusis; // sensor/instrument/satellite - std::size_t nuchan; // channel number - float tlap, tsum; - std::size_t ntlapupdate; - - if (infile.is_open()) - { - oops::Log::debug() << "ObsBias:: prior file is opened" << std::endl; - float par; - while (infile >> ich) - { - infile >> nusis; - infile >> nuchan; - infile >> tlap; - infile >> tsum; - infile >> ntlapupdate; - if (nusis == sensor) { - auto ijob = std::find(jobs_.begin(), jobs_.end(), nuchan); - if (ijob != jobs_.end()) { - int j = std::distance(jobs_.begin(), ijob); - - for (auto & item : gsi_predictors) { - infile >> par; - auto ipred = std::find(prednames_.begin(), prednames_.end(), item); - if (ipred != prednames_.end()) { - int p = std::distance(prednames_.begin(), ipred); - biascoeffs_.at(j*prednames_.size() + p) = static_cast(par); - } - } - } else { - for (auto & item : gsi_predictors) - infile >> par; - } - } else { - for (auto & item : gsi_predictors) - infile >> par; - } + if (params.inputFile.value() != boost::none) { + // Open an hdf5 file with bias coefficients, read only + ioda::Engines::BackendNames backendName = ioda::Engines::BackendNames::Hdf5File; + ioda::Engines::BackendCreationParameters backendParams; + backendParams.fileName = *params.inputFile.value(); + backendParams.action = ioda::Engines::BackendFileActions::Open; + backendParams.openMode = ioda::Engines::BackendOpenModes::Read_Only; + + // Create the backend and attach it to an ObsGroup + // Use the None DataLyoutPolicy for now to accommodate the current file format + ioda::Group backend = constructBackend(backendName, backendParams); + ioda::ObsGroup obsgroup = ioda::ObsGroup(backend, + ioda::detail::DataLayoutPolicy::generate( + ioda::detail::DataLayoutPolicy::Policies::None)); + + // Read all coefficients into the Eigen array + ioda::Variable coeffvar = obsgroup.vars["bias_coefficients"]; + Eigen::ArrayXXf allbiascoeffs; + coeffvar.readWithEigenRegular(allbiascoeffs); + + // Find indices of predictors and variables/channels that we need in the data read from the file + const std::vector pred_idx = getRequiredVariableIndices(obsgroup, "predictors", + prednames_.begin() + numStaticPredictors_, prednames_.end()); + const std::vector var_idx = getRequiredVarOrChannelIndices(obsgroup, vars_); + + // Filter predictors and channels that we need + // FIXME: may be possible by indexing allbiascoeffs(pred_idx, chan_idx) when Eigen 3.4 + // is available + + for (size_t jpred = 0; jpred < pred_idx.size(); ++jpred) { + for (size_t jvar = 0; jvar < var_idx.size(); ++jvar) { + biascoeffs_[index(jpred, jvar)] = allbiascoeffs(pred_idx[jpred], var_idx[jvar]); } - infile.close(); - oops::Log::debug() << "ObsBias:: read prior from " << input_filename << std::endl; - } else { - oops::Log::error() << "Unable to open file : " << input_filename << std::endl; - ABORT("Unable to open bias prior file "); } } else { - oops::Log::warning() << "ObsBias::prior file is NOT available, starting from ZERO" - << std::endl; + if (numVariablePredictors_ > 0) + oops::Log::warning() << "ObsBias::prior file is NOT available, starting from ZERO" + << std::endl; } oops::Log::trace() << "ObsBias::read and initilization done " << std::endl; @@ -199,137 +147,59 @@ void ObsBias::read(const eckit::Configuration & conf) { // ----------------------------------------------------------------------------- -void ObsBias::write(const eckit::Configuration & conf) const { +void ObsBias::write(const Parameters_ & params) const { oops::Log::trace() << "ObsBias::write to file not implemented" << std::endl; - - // Bias coefficients output file name - std::string output_filename; - if (conf.has("ObsBias.abias_out")) { - output_filename = conf.getString("ObsBias.abias_out"); - } - - oops::Log::trace() << "ObsBias::write to file done" << std::endl; } // ----------------------------------------------------------------------------- -void ObsBias::computeObsBias(ioda::ObsVector & ybias, - ObsDiagnostics & ydiags, - const std::vector & predData) const { - oops::Log::trace() << "ObsBias::computeObsBias starting" << std::endl; - - if (this->size() > 0) { - const std::size_t nlocs = ybias.nlocs(); - const std::size_t npreds = prednames_.size(); - const std::size_t njobs = jobs_.size(); - - ASSERT(biascoeffs_.size() == npreds*njobs); - ASSERT(predData.size() == npreds); - ASSERT(ybias.nvars() == njobs); - - /* predData memory layout (npreds X nlocs X njobs) - * Loc 0 1 2 3 - * -------------------------- - * pred1 Chan1 | 0 3 6 9 - * Chan2 | 1 4 7 10 - * .... | 2 5 8 11 - * - * pred2 Chan1 |12 15 18 21 - * Chan2 |13 16 19 22 - * .... |14 17 20 23 - */ - - ybias.zero(); - - /* ybias memory layout (nlocs X njobs) - * ch1 ch2 ch3 ch4 - * Loc -------------------------- - * 0 | 0 1 2 3 - * 1 | 4 5 6 7 - * 2 | 8 9 10 11 - * 3 |12 13 14 15 - * 4 |16 17 18 19 - * ...| - */ - - /* map bias coeff to eigen matrix npreds X njobs (read only) - * bias coeff memory layout (npreds X njobs) - * ch1 ch2 ch3 ch4 - * -------------------------- - * pred1 | 0 1 2 3 - * pred2 | 4 5 6 7 - * pred3 | 8 9 10 11 - * .... | - */ - Eigen::Map coeffs(biascoeffs_.data(), npreds, njobs); - - std::vector biasTerm(nlocs, 0.0); - // For each channel: ( nlocs X 1 ) = ( nlocs X npreds ) * ( npreds X 1 ) - for (std::size_t jch = 0; jch < njobs; ++jch) { - for (std::size_t jp = 0; jp < npreds; ++jp) { - // axpy - const double beta = coeffs(jp, jch); - for (std::size_t jl = 0; jl < nlocs; ++jl) { - biasTerm[jl] = predData[jp][jl*njobs+jch] * beta; - ybias[jl*njobs+jch] += biasTerm[jl]; - } - // Save ObsBiasTerms (bias_coeff * predictor) for QC - const std::string varname = predbases_[jp]->name() + "_" + std::to_string(jobs_[jch]); - if (ydiags.has(varname)) { - ydiags.save(biasTerm, varname, 1); - } else { - oops::Log::error() << varname << " is not reserved in ydiags !" << std::endl; - ABORT("ObsBiasTerm variable is not reserved in ydiags"); - } - } - } - } - - oops::Log::trace() << "ObsBias::computeObsBias done." << std::endl; -} +double ObsBias::norm() const { + oops::Log::trace() << "ObsBias::norm starting." << std::endl; + double zz = 0.0; -// ----------------------------------------------------------------------------- -std::vector ObsBias::computePredictors(const GeoVaLs & geovals, - const ObsDiagnostics & ydiags) const { - const std::size_t npreds = predbases_.size(); + // Static predictors + const int numUnitCoeffs = numStaticPredictors_ * vars_.size(); + zz += numUnitCoeffs * numUnitCoeffs; - std::vector predData(npreds, ioda::ObsVector(odb_)); + // Variable predictors + zz += biascoeffs_.squaredNorm(); - for (std::size_t p = 0; p < npreds; ++p) { - predbases_[p]->compute(odb_, geovals, ydiags, predData[p]); - predData[p].save(predbases_[p]->name() + "Predictor"); - } + // Compute average and take square root + const int numCoeffs = numUnitCoeffs + biascoeffs_.size(); + if (numCoeffs > 0) zz = std::sqrt(zz/numCoeffs); - oops::Log::trace() << "ObsBias::computePredictors done." << std::endl; - return predData; + oops::Log::trace() << "ObsBias::norm done." << std::endl; + return zz; } // ----------------------------------------------------------------------------- -double ObsBias::norm() const { - oops::Log::trace() << "ObsBias::norm starting." << std::endl; - double zz = 0.0; - for (std::size_t jj = 0; jj < biascoeffs_.size(); ++jj) { - zz += biascoeffs_[jj] * biascoeffs_[jj]; - } - if (biascoeffs_.size() > 0) zz = std::sqrt(zz/biascoeffs_.size()); - oops::Log::trace() << "ObsBias::norm done." << std::endl; - return zz; +std::vector> ObsBias::variablePredictors() const { + return std::vector>( + predictors_.begin() + numStaticPredictors_, predictors_.end()); } // ----------------------------------------------------------------------------- void ObsBias::print(std::ostream & os) const { if (this->size() > 0) { - // map bias coeffs to eigen matrix (writable) + // map bias coeffs to eigen matrix Eigen::Map - coeffs(biascoeffs_.data(), prednames_.size(), jobs_.size()); - + coeffs(biascoeffs_.data(), numVariablePredictors_, vars_.size()); os << "ObsBias::print " << std::endl; os << "---------------------------------------------------------------" << std::endl; - auto njobs = jobs_.size(); - for (std::size_t p = 0; p < prednames_.size(); ++p) { + for (std::size_t p = 0; p < numStaticPredictors_; ++p) { os << std::fixed << std::setw(20) << prednames_[p] + << ": Min= " << std::setw(15) << std::setprecision(8) + << 1.0f + << ", Max= " << std::setw(15) << std::setprecision(8) + << 1.0f + << ", Norm= " << std::setw(15) << std::setprecision(8) + << std::sqrt(static_cast(vars_.size())) + << std::endl; + } + for (std::size_t p = 0; p < numVariablePredictors_; ++p) { + os << std::fixed << std::setw(20) << prednames_[numStaticPredictors_ + p] << ": Min= " << std::setw(15) << std::setprecision(8) << coeffs.row(p).minCoeff() << ", Max= " << std::setw(15) << std::setprecision(8) @@ -345,4 +215,27 @@ void ObsBias::print(std::ostream & os) const { // ----------------------------------------------------------------------------- +void ObsBias::initPredictor(const eckit::Configuration &predictorConf) { + std::shared_ptr pred(PredictorFactory::create(predictorConf, vars_)); + predictors_.push_back(pred); + prednames_.push_back(pred->name()); + geovars_ += pred->requiredGeovars(); + hdiags_ += pred->requiredHdiagnostics(); + + // Reserve the space for ObsBiasTerm for predictor + if (vars_.channels().size() > 0) { + // At present we can label predictors with either the channel number or the variable + // name, but not both. So make sure there's only one multi-channel variable. + ASSERT(vars_.size() == vars_.channels().size()); + for (auto & job : vars_.channels()) { + hdiags_ += oops::Variables({prednames_.back() + "_" + std::to_string(job)}); + } + } else { + for (const std::string & variable : vars_.variables()) + hdiags_ += oops::Variables({prednames_.back() + "_" + variable}); + } +} + +// ----------------------------------------------------------------------------- + } // namespace ufo diff --git a/src/ufo/ObsBias.h b/src/ufo/ObsBias.h index 962a3d689..5fa566ef4 100644 --- a/src/ufo/ObsBias.h +++ b/src/ufo/ObsBias.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2017-2018 UCAR + * (C) Copyright 2017-2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -20,6 +20,7 @@ #include "oops/util/ObjectCounter.h" #include "oops/util/Printable.h" +#include "ufo/ObsBiasParameters.h" #include "ufo/predictors/PredictorBase.h" namespace oops { @@ -36,57 +37,84 @@ namespace ufo { class ObsBiasIncrement; class ObsDiagnostics; -/// Class to handle observation bias parameters. - -// ----------------------------------------------------------------------------- - +/// Class to handle observation bias correction coefficients +/// \details contains information on what predictors are used for bias +/// correction application class ObsBias : public util::Printable, private util::ObjectCounter { public: + typedef ObsBiasParameters Parameters_; + static const std::string classname() {return "ufo::ObsBias";} - ObsBias(ioda::ObsSpace &, const eckit::Configuration &); + ObsBias(ioda::ObsSpace &, const Parameters_ &); ObsBias(const ObsBias &, const bool); - ~ObsBias() {} ObsBias & operator+=(const ObsBiasIncrement &); ObsBias & operator=(const ObsBias &); - // I/O and diagnostics - void read(const eckit::Configuration &); - void write(const eckit::Configuration &) const; + /// Read bias correction coefficients from file + void read(const Parameters_ &); + void write(const Parameters_ &) const; double norm() const; std::size_t size() const {return biascoeffs_.size();} - // Bias parameters interface - const double & operator[](const unsigned int ii) const {return biascoeffs_[ii];} - double & operator[](const unsigned int ii) {return biascoeffs_[ii];} - - // Obs bias model - void computeObsBias(ioda::ObsVector &, ObsDiagnostics &, - const std::vector &) const; + /// Return the coefficient of predictor \p jpred for variable \p jvar. + /// + /// Note: \p jpred may be the index of a static or a variable predictor. + double operator()(size_t jpred, size_t jvar) const { + return jpred < numStaticPredictors_ ? + 1.0 : biascoeffs_[index(jpred - numStaticPredictors_, jvar)]; + } - // Obs Bias Predictors - std::vector computePredictors(const GeoVaLs &, const ObsDiagnostics &) const; + /// Return bias correction coefficients (for *variable* predictors) + const Eigen::VectorXd & data() const {return biascoeffs_;} // Required variables const oops::Variables & requiredVars() const {return geovars_;} const oops::Variables & requiredHdiagnostics() const {return hdiags_;} + const std::vector & requiredPredictors() const {return prednames_;} + + /// Return a reference to the vector of all (static and variable) predictors. + const Predictors & predictors() const {return predictors_;} + + /// Return the vector of variable predictors. + std::vector> variablePredictors() const; + + /// Return the list of bias-corrected variables. + const oops::Variables & correctedVars() const {return vars_;} // Operator - operator bool() const {return biascoeffs_.size() > 0;} + operator bool() const { + return (numStaticPredictors_ > 0 || numVariablePredictors_ > 0) && vars_.size() > 0; + } private: - void print(std::ostream &) const; + void print(std::ostream &) const override; + + /// index in the flattened biascoeffs_ for predictor \p jpred and variable \p jvar + size_t index(size_t jpred, size_t jvar) const {return jvar*numVariablePredictors_ + jpred;} - ioda::ObsSpace & odb_; - eckit::LocalConfiguration conf_; + void initPredictor(const eckit::Configuration &predictorConf); - std::vector biascoeffs_; - std::vector> predbases_; + /// bias correction coefficients (npredictors x nprimitivevariables) + Eigen::VectorXd biascoeffs_; + + /// bias correction predictors + Predictors predictors_; + /// predictor names std::vector prednames_; - std::vector jobs_; + /// number of static predictors (i.e. predictors with fixed coefficients all equal to 1) + std::size_t numStaticPredictors_; + /// number of variable predictors (i.e. predictors with variable coefficients) + std::size_t numVariablePredictors_; + + /// corrected variables names (for now has to be same as "simulated variables") + oops::Variables vars_; + + /// Variables that need to be requested from the model (for computation of predictors) oops::Variables geovars_; + /// Diagnostics that need to be requested from the obs operator (for computation of predictors) oops::Variables hdiags_; }; diff --git a/src/ufo/ObsBiasCovariance.cc b/src/ufo/ObsBiasCovariance.cc index 1e2fa00d5..324ca84be 100644 --- a/src/ufo/ObsBiasCovariance.cc +++ b/src/ufo/ObsBiasCovariance.cc @@ -14,6 +14,10 @@ #include "ufo/ObsBiasCovariance.h" +#include "ioda/distribution/Accumulator.h" +#include "ioda/Engines/Factory.h" +#include "ioda/Layout.h" +#include "ioda/ObsGroup.h" #include "ioda/ObsSpace.h" #include "ioda/ObsVector.h" @@ -24,95 +28,84 @@ #include "ufo/ObsBias.h" #include "ufo/ObsBiasIncrement.h" #include "ufo/predictors/PredictorBase.h" +#include "ufo/utils/IodaGroupIndices.h" namespace ufo { // ----------------------------------------------------------------------------- -ObsBiasCovariance::ObsBiasCovariance(ioda::ObsSpace & odb, const eckit::Configuration & conf) - : conf_(conf), odb_(odb), prednames_(0), jobs_(0), variances_(0), preconditioner_(0), +ObsBiasCovariance::ObsBiasCovariance(ioda::ObsSpace & odb, + const Parameters_ & params) + : odb_(odb), prednames_(0), vars_(odb.obsvariables()), variances_(), + preconditioner_(0), ht_rinv_h_(0), obs_num_(0), analysis_variances_(0), minimal_required_obs_number_(0) { oops::Log::trace() << "ObsBiasCovariance::Constructor starting" << std::endl; - // Get the jobs(channels) - if (conf_.has("obs bias.jobs")) { - const std::set jobs = oops::parseIntSet(conf_.getString("obs bias.jobs")); - jobs_.assign(jobs.begin(), jobs.end()); - } - // Predictor factory - if (conf_.has("obs bias.predictors")) { - std::vector confs; - conf_.get("obs bias.predictors", confs); - for (std::size_t j = 0; j < confs.size(); ++j) { - std::shared_ptr pred(PredictorFactory::create(confs[j], jobs_)); - prednames_.push_back(pred->name()); - } + for (const eckit::LocalConfiguration &conf : params.variationalBC.value().predictors.value()) { + std::shared_ptr pred(PredictorFactory::create(conf, vars_)); + prednames_.push_back(pred->name()); } - if (prednames_.size()*jobs_.size() > 0) { + if (prednames_.size()*vars_.size() > 0) { + if (params.covariance.value() == boost::none) + throw eckit::UserError("obs bias.covariance section missing from the YAML file"); + const ObsBiasCovarianceParameters &biasCovParams = *params.covariance.value(); + // Get the minimal required filtered obs number - minimal_required_obs_number_ = - conf_.getUnsigned("obs bias.covariance.minimal required obs number"); + minimal_required_obs_number_ = biasCovParams.minimalRequiredObsNumber; // Override the variance range if provided - if (conf_.has("obs bias.covariance.variance range")) { - const std::vector - range = conf_.getDoubleVector("obs bias.covariance.variance range"); + { + const std::vector &range = biasCovParams.varianceRange.value(); ASSERT(range.size() == 2); smallest_variance_ = range[0]; largest_variance_ = range[1]; } - // Override the preconditioning step size if provided - if (conf_.has("obs bias.covariance.step size")) { - step_size_ = conf_.getDouble("obs bias.covariance.step size"); - } + // Override the preconditioning step size if provided + step_size_ = biasCovParams.stepSize; // Override the largest analysis variance if provided - if (conf_.has("obs bias.covariance.largest analysis variance")) { - largest_analysis_variance_ = - conf_.getDouble("obs bias.covariance.largest analysis variance"); - } + largest_analysis_variance_ = biasCovParams.largestAnalysisVariance; // Initialize the variances to upper limit - variances_.resize(prednames_.size() * jobs_.size()); - std::fill(variances_.begin(), variances_.end(), largest_variance_); + variances_ = Eigen::VectorXd::Constant(prednames_.size()*vars_.size(), largest_variance_); // Initialize the hessian contribution to zero - ht_rinv_h_.resize(prednames_.size() * jobs_.size()); + ht_rinv_h_.resize(prednames_.size() * vars_.size()); std::fill(ht_rinv_h_.begin(), ht_rinv_h_.end(), 0.0); // Initialize the preconditioner to default step size - preconditioner_.resize(prednames_.size() * jobs_.size()); + preconditioner_.resize(prednames_.size() * vars_.size()); std::fill(preconditioner_.begin(), preconditioner_.end(), step_size_); // Initialize obs_num_ to ZERO - obs_num_.resize(jobs_.size()); + obs_num_.resize(vars_.size()); std::fill(obs_num_.begin(), obs_num_.end(), 0); // Initialize analysis error variances to the upper limit - analysis_variances_.resize(prednames_.size() * jobs_.size()); + analysis_variances_.resize(prednames_.size() * vars_.size()); std::fill(analysis_variances_.begin(), analysis_variances_.end(), largest_variance_); // Initializes from given prior - if (conf_.has("obs bias.covariance.prior")) { + if (biasCovParams.prior.value() != boost::none) { + const ObsBiasCovariancePriorParameters &priorParams = *biasCovParams.prior.value(); + // Get default inflation ratio - const double inflation_ratio = - conf.getDouble("obs bias.covariance.prior.inflation.ratio"); + const double inflation_ratio = priorParams.inflation.value().ratio; // Check the large inflation ratio when obs number < minimal_required_obs_number - const double large_inflation_ratio = - conf.getDouble("obs bias.covariance.prior.inflation.ratio for small dataset"); + const double large_inflation_ratio = priorParams.inflation.value().ratioForSmallDataset; // read in Variances prior (analysis_variances_) and number of obs. (obs_num_) // from previous cycle - this->read(conf_); + this->read(priorParams); // set variances for bias predictor coeff. based on diagonal info // of previous analysis error variance std::size_t ii; - for (std::size_t j = 0; j < jobs_.size(); ++j) { + for (std::size_t j = 0; j < vars_.size(); ++j) { const double inflation = (obs_num_[j] <= minimal_required_obs_number_) ? large_inflation_ratio : inflation_ratio; for (std::size_t p = 0; p < prednames_.size(); ++p) { @@ -133,72 +126,48 @@ ObsBiasCovariance::ObsBiasCovariance(ioda::ObsSpace & odb, const eckit::Configur // ----------------------------------------------------------------------------- -void ObsBiasCovariance::read(const eckit::Configuration & conf) { +void ObsBiasCovariance::read(const ObsBiasCovariancePriorParameters & params) { oops::Log::trace() << "ObsBiasCovariance::read from file " << std::endl; - // Default predictor names from GSI - const std::vector gsi_predictors = {"constant", - "zenith_angle", - "cloud_liquid_water", - "lapse_rate_order_2", - "lapse_rate", - "cosine_of_latitude_times_orbit_node", - "sine_of_latitude", - "emissivity", - "scan_angle_order_4", - "scan_angle_order_3", - "scan_angle_order_2", - "scan_angle" - }; - - const std::string sensor = conf.getString("obs bias.sensor"); - - if (conf.has("obs bias.covariance.prior.datain")) { - const std::string filename = conf.getString("obs bias.covariance.prior.datain"); - std::ifstream infile(filename); - - if (infile.is_open()) - { - int ich; // sequential number - std::string nusis; // sensor/instrument/satellite - int nuchan; // channel number - float number; // QCed obs number from previous cycle - - float par; - while (infile >> ich) - { - infile >> nusis; - infile >> nuchan; - infile >> number; - if (nusis == sensor) { - auto ijob = std::find(jobs_.begin(), jobs_.end(), nuchan); - if (ijob != jobs_.end()) { - int j = std::distance(jobs_.begin(), ijob); - obs_num_[j] = static_cast(number); - - for (auto & item : gsi_predictors) { - infile >> par; - auto ipred = std::find(prednames_.begin(), prednames_.end(), item); - if (ipred != prednames_.end()) { - int p = std::distance(prednames_.begin(), ipred); - analysis_variances_.at(j*prednames_.size() + p) = static_cast(par); - } - } - } else { - for (auto & item : gsi_predictors) - infile >> par; - } - } else { - for (auto & item : gsi_predictors) - infile >> par; - } + if (params.inputFile.value() != boost::none) { + // Open an hdf5 file, read only + ioda::Engines::BackendNames backendName = ioda::Engines::BackendNames::Hdf5File; + ioda::Engines::BackendCreationParameters backendParams; + backendParams.fileName = *params.inputFile.value(); + backendParams.action = ioda::Engines::BackendFileActions::Open; + backendParams.openMode = ioda::Engines::BackendOpenModes::Read_Only; + + // Create the backend and attach it to an ObsGroup + // Use the None DataLyoutPolicy for now to accommodate the current file format + ioda::Group backend = constructBackend(backendName, backendParams); + ioda::ObsGroup obsgroup = ioda::ObsGroup(backend, + ioda::detail::DataLayoutPolicy::generate( + ioda::detail::DataLayoutPolicy::Policies::None)); + + // Read coefficients error variances into the Eigen array + ioda::Variable bcerrvar = obsgroup.vars["bias_coeff_errors"]; + Eigen::ArrayXXf allbcerrors; + bcerrvar.readWithEigenRegular(allbcerrors); + + // Read nobs into Eigen array + ioda::Variable nobsvar = obsgroup.vars["number_obs_assimilated"]; + Eigen::ArrayXf nobsassim; + nobsvar.readWithEigenRegular(nobsassim); + + // Find indices of predictors and variables/channels that we need in the data read from the file + const std::vector pred_idx = getRequiredVariableIndices(obsgroup, "predictors", + prednames_.begin(), prednames_.end()); + const std::vector var_idx = getRequiredVarOrChannelIndices(obsgroup, vars_); + + // Filter predictors and channels that we need + // FIXME: may be possible by indexing allbcerrors(pred_idx, chan_idx) when Eigen 3.4 + // is available + for (size_t jvar = 0; jvar < var_idx.size(); ++jvar) { + obs_num_[jvar] = nobsassim(var_idx[jvar]); + for (size_t jpred = 0; jpred < pred_idx.size(); ++jpred) { + analysis_variances_[jvar*pred_idx.size()+jpred] = + allbcerrors(pred_idx[jpred], var_idx[jvar]); } - infile.close(); - oops::Log::trace() << "ObsBiasCovariance::read from prior file: " - << filename << " Done " << std::endl; - } else { - oops::Log::error() << "Unable to open file : " << filename << std::endl; - ABORT("Unable to open bias correction coeffs variance prior file "); } } @@ -221,32 +190,32 @@ void ObsBiasCovariance::linearize(const ObsBias & bias, const eckit::Configurati // Retrieve the QC flags and do statistics from second outer loop const int jouter = innerConf.getInt("iteration"); if (jouter >= 1) { - // Reset the number of filtered Obs. - std::fill(obs_num_.begin(), obs_num_.end(), 0); + std::unique_ptr>> obs_num_accumulator = + odb_.distribution()->createAccumulator(obs_num_.size()); - // Retrieve the QC flags of previous outer loop and get the number of effective obs. + // Retrieve the QC flags of previous outer loop and recalculate the number of effective obs. const std::string group_name = "EffectiveQC" + std::to_string(jouter-1); const std::vector vars = odb_.obsvariables().variables(); std::vector qc_flags(odb_.nlocs(), 999); - for (std::size_t j = 0; j < vars.size(); ++j) { - if (odb_.has(group_name , vars[j])) { - odb_.get_db(group_name, vars[j], qc_flags); - obs_num_[j] = std::count(qc_flags.begin(), qc_flags.end(), 0); + for (std::size_t jvar = 0; jvar < vars.size(); ++jvar) { + if (odb_.has(group_name, vars[jvar])) { + odb_.get_db(group_name, vars[jvar], qc_flags); + for (std::size_t jloc = 0; jloc < qc_flags.size(); ++jloc) + if (qc_flags[jloc] == 0) + obs_num_accumulator->addTerm(jloc, jvar, 1); } else { - throw eckit::UserError("Unable to find QC flags : " + vars[j] + "@" + group_name); + throw eckit::UserError("Unable to find QC flags : " + vars[jvar] + "@" + group_name); } } - // Sum across the processros - if (odb_.isDistributed()) - odb_.comm().allReduceInPlace(obs_num_.begin(), obs_num_.end(), - eckit::mpi::sum()); + // Sum across the processors + obs_num_ = obs_num_accumulator->computeResult(); const float missing = util::missingValue(missing); // compute the hessian contribution from Jo bias terms channel by channel // retrieve the effective error (after QC) for this channel - ioda::ObsVector r_inv(odb_, "EffectiveError", true); + ioda::ObsVector r_inv(odb_, "EffectiveError"); // compute \mathrm{R}^{-1} std::size_t nvars = r_inv.nvars(); @@ -260,16 +229,13 @@ void ObsBiasCovariance::linearize(const ObsBias & bias, const eckit::Configurati } } - // Sum the total number of effective obs. across tasks - if (odb_.isDistributed()) - odb_.comm().allReduceInPlace(obs_num_.begin(), obs_num_.end(), eckit::mpi::sum()); - // compute \mathrm{H}_\beta^\intercal \mathrm{R}^{-1} \mathrm{H}_\beta // ----------------------------------------- - std::fill(ht_rinv_h_.begin(), ht_rinv_h_.end(), 0.0); + std::unique_ptr>> ht_rinv_h_accumulator = + odb_.distribution()->createAccumulator(ht_rinv_h_.size()); for (std::size_t p = 0; p < prednames_.size(); ++p) { // retrieve the predictors - const ioda::ObsVector predx(odb_, prednames_[p] + "Predictor", true); + const ioda::ObsVector predx(odb_, prednames_[p] + "Predictor"); // for each variable ASSERT(r_inv.nlocs() == predx.nlocs()); @@ -277,18 +243,18 @@ void ObsBiasCovariance::linearize(const ObsBias & bias, const eckit::Configurati // only keep the diagnoal for (size_t vv = 0; vv < nvars; ++vv) { for (size_t ii = 0; ii < predx.nlocs(); ++ii) - ht_rinv_h_[vv*prednames_.size() + p] += - pow(predx[ii*nvars + vv], 2) * r_inv[ii*nvars + vv]; + ht_rinv_h_accumulator->addTerm(ii, + vv*prednames_.size() + p, + pow(predx[ii*nvars + vv], 2) * r_inv[ii*nvars + vv]); } } // Sum the hessian contributions across the tasks - if (odb_.isDistributed()) - odb_.comm().allReduceInPlace(ht_rinv_h_.begin(), ht_rinv_h_.end(), eckit::mpi::sum()); + ht_rinv_h_ = ht_rinv_h_accumulator->computeResult(); } // reset variances for bias predictor coeff. based on current data count - for (std::size_t j = 0; j < jobs_.size(); ++j) { + for (std::size_t j = 0; j < obs_num_.size(); ++j) { if (obs_num_[j] <= minimal_required_obs_number_) { for (std::size_t p = 0; p < prednames_.size(); ++p) variances_[j*prednames_.size() + p] = smallest_variance_; @@ -296,10 +262,9 @@ void ObsBiasCovariance::linearize(const ObsBias & bias, const eckit::Configurati } // set a coeff. factor for variances of control variables - std::size_t index; - for (std::size_t j = 0; j < jobs_.size(); ++j) { + for (std::size_t j = 0; j < vars_.size(); ++j) { for (std::size_t p = 0; p < prednames_.size(); ++p) { - index = j*prednames_.size() + p; + const std::size_t index = j*prednames_.size() + p; preconditioner_[index] = step_size_; // L = \mathrm{A}^{-1} if (obs_num_[j] > 0) @@ -323,9 +288,7 @@ void ObsBiasCovariance::multiply(const ObsBiasIncrement & dx1, ObsBiasIncrement & dx2) const { oops::Log::trace() << "ObsBiasCovariance::multiply starts" << std::endl; - for (std::size_t jj = 0; jj < variances_.size(); ++jj) { - dx2[jj] = dx1[jj] * variances_[jj]; - } + dx2.data().array() = dx1.data().array() * variances_.array(); oops::Log::trace() << "ObsBiasCovariance::multiply is done" << std::endl; } @@ -336,9 +299,7 @@ void ObsBiasCovariance::inverseMultiply(const ObsBiasIncrement & dx1, ObsBiasIncrement & dx2) const { oops::Log::trace() << "ObsBiasCovariance::inverseMultiply starts" << std::endl; - for (std::size_t jj = 0; jj < variances_.size(); ++jj) { - dx2[jj] = dx1[jj] / variances_[jj]; - } + dx2.data().array() = dx1.data().array() / variances_.array(); oops::Log::trace() << "ObsBiasCovariance::inverseMultiply is done" << std::endl; } @@ -350,7 +311,7 @@ void ObsBiasCovariance::randomize(ObsBiasIncrement & dx) const { if (dx) { static util::NormalDistribution dist(variances_.size()); for (std::size_t jj = 0; jj < variances_.size(); ++jj) { - dx[jj] = dist[jj] * std::sqrt(variances_[jj]); + dx.data()[jj] = dist[jj] * std::sqrt(variances_[jj]); } } oops::Log::trace() << "ObsBiasCovariance::randomize is done" << std::endl; diff --git a/src/ufo/ObsBiasCovariance.h b/src/ufo/ObsBiasCovariance.h index 2304047b7..cc47d8ab1 100644 --- a/src/ufo/ObsBiasCovariance.h +++ b/src/ufo/ObsBiasCovariance.h @@ -15,9 +15,12 @@ #include "eckit/config/LocalConfiguration.h" +#include "oops/base/Variables.h" #include "oops/util/ObjectCounter.h" #include "oops/util/Printable.h" +#include "ufo/ObsBiasParameters.h" + namespace eckit { class Configuration; } @@ -36,10 +39,12 @@ class ObsBiasCovariance : public util::Printable, private boost::noncopyable, private util::ObjectCounter { public: + typedef ObsBiasParameters Parameters_; + static const std::string classname() {return "ufo::ObsBiasCovariance";} // Constructor, destructor - ObsBiasCovariance(ioda::ObsSpace &, const eckit::Configuration &); + ObsBiasCovariance(ioda::ObsSpace & odb, const Parameters_ & params); ~ObsBiasCovariance() {} // Linear algebra operators @@ -49,14 +54,13 @@ class ObsBiasCovariance : public util::Printable, void randomize(ObsBiasIncrement &) const; // Utilities - const eckit::Configuration & config() const {return conf_;} - void read(const eckit::Configuration &); + void read(const ObsBiasCovariancePriorParameters &); void write(const eckit::Configuration &); const std::vector predictorNames() const {return prednames_;} private: void print(std::ostream &) const {} - const eckit::LocalConfiguration conf_; + ioda::ObsSpace & odb_; // Hessian contribution from Jo bias correction terms @@ -75,22 +79,24 @@ class ObsBiasCovariance : public util::Printable, std::vector analysis_variances_; // Error variances - std::vector variances_; + Eigen::VectorXd variances_; -// Default smallest variance value - double smallest_variance_ = 1.0e-6; +// Smallest variance value + double smallest_variance_ = ObsBiasCovarianceParameters::defaultSmallestVariance(); -// Default largest variance value - double largest_variance_ = 10.0; +// Largest variance value + double largest_variance_ = ObsBiasCovarianceParameters::defaultLargestVariance(); -// Default largest analysis error variance - double largest_analysis_variance_ = 10000.0; +// Largest analysis error variance + double largest_analysis_variance_ = ObsBiasCovarianceParameters::defaultLargestAnalysisVariance(); -// Default stepsize - double step_size_ = 1.e-4; +// Step size + double step_size_ = ObsBiasCovarianceParameters::defaultStepSize(); std::vector prednames_; - std::vector jobs_; + + /// variables for which bias correction coefficients will be updated + oops::Variables vars_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/ObsBiasIncrement.cc b/src/ufo/ObsBiasIncrement.cc index d48c446ff..cec4bd807 100644 --- a/src/ufo/ObsBiasIncrement.cc +++ b/src/ufo/ObsBiasIncrement.cc @@ -1,5 +1,5 @@ /* - * (C) Copyright 2018-2019 UCAR + * (C) Copyright 2018-2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -8,124 +8,68 @@ #include "ufo/ObsBiasIncrement.h" #include -#include +#include #include "eckit/mpi/Comm.h" - #include "ioda/ObsSpace.h" -#include "ioda/ObsVector.h" - -#include "oops/util/IntSetParser.h" #include "oops/util/Logger.h" -#include "oops/util/missingValues.h" - #include "ufo/ObsBias.h" namespace ufo { // ----------------------------------------------------------------------------- -ObsBiasIncrement::ObsBiasIncrement(const ioda::ObsSpace & odb, const eckit::Configuration & conf) - : predbases_(0), jobs_(0), odb_(odb), conf_(conf) { - oops::Log::trace() << "ObsBiasIncrement::create starting." << std::endl; - - // Get the jobs(channels) - if (conf_.has("obs bias.jobs")) { - const std::set jobs = oops::parseIntSet(conf_.getString("obs bias.jobs")); - jobs_.assign(jobs.begin(), jobs.end()); - } +ObsBiasIncrement::ObsBiasIncrement(const ioda::ObsSpace & odb, + const Parameters_ & params) + : vars_(odb.obsvariables()) { + oops::Log::trace() << "ufo::ObsBiasIncrement::create starting." << std::endl; // Predictor factory - if (conf_.has("obs bias.predictors")) { - std::vector confs; - conf_.get("obs bias.predictors", confs); - typedef std::unique_ptr predictor; - for (std::size_t j = 0; j < confs.size(); ++j) { - predbases_.push_back(predictor(PredictorFactory::create(confs[j], jobs_))); - prednames_.push_back(predbases_[j]->name()); - } + for (const eckit::LocalConfiguration &conf : params.variationalBC.value().predictors.value()) { + std::unique_ptr predictor(PredictorFactory::create(conf, vars_)); + prednames_.push_back(predictor->name()); } // initialize bias coefficient perturbations - biascoeffsinc_.resize(prednames_.size()*jobs_.size()); - std::fill(biascoeffsinc_.begin(), biascoeffsinc_.end(), 0.0); + biascoeffsinc_ = Eigen::VectorXd::Zero(prednames_.size() * vars_.size()); - oops::Log::trace() << "ObsBiasIncrement::create done." << std::endl; + oops::Log::trace() << "ufo::ObsBiasIncrement::create done." << std::endl; } // ----------------------------------------------------------------------------- ObsBiasIncrement::ObsBiasIncrement(const ObsBiasIncrement & other, const bool copy) - : odb_(other.odb_), conf_(other.conf_), predbases_(other.predbases_), - prednames_(other.prednames_), jobs_(other.jobs_) { - oops::Log::trace() << "ObsBiasIncrement::copy ctor starting" << std::endl; - - // initialize bias coefficient perturbations - biascoeffsinc_.resize(prednames_.size()*jobs_.size()); - std::fill(biascoeffsinc_.begin(), biascoeffsinc_.end(), 0.0); - - // Copy the bias model coeff data - if (biascoeffsinc_.size() > 0) *this = other; - - oops::Log::trace() << "ObsBiasIncrement::copy ctor done." << std::endl; -} - -// ----------------------------------------------------------------------------- - -ObsBiasIncrement::ObsBiasIncrement(const ObsBiasIncrement & other, - const eckit::Configuration & conf) - : odb_(other.odb_), conf_(conf), predbases_(), prednames_(), jobs_() { - oops::Log::trace() << "ObsBiasIncrement::copy ctor starting." << std::endl; - // Get the jobs(channels) - if (conf_.has("obs bias.jobs")) { - const std::set jobs = oops::parseIntSet(conf_.getString("obs bias.jobs")); - jobs_.assign(jobs.begin(), jobs.end()); + : prednames_(other.prednames_), vars_(other.vars_) { + oops::Log::trace() << "ufo::ObsBiasIncrement::copy ctor starting" << std::endl; + + // Copy the bias coefficients data, or fill in with zeros + if (copy) { + biascoeffsinc_ = other.biascoeffsinc_; + } else { + biascoeffsinc_ = Eigen::VectorXd::Zero(prednames_.size() * vars_.size()); } - // Predictor factory - if (conf_.has("obs bias.predictors")) { - std::vector confs; - conf_.get("obs bias.predictors", confs); - typedef std::unique_ptr predictor; - for (std::size_t j = 0; j < confs.size(); ++j) { - predbases_.push_back(predictor(PredictorFactory::create(confs[j], jobs_))); - prednames_.push_back(predbases_[j]->name()); - } - } - - // initialize bias coefficient perturbations - biascoeffsinc_.resize(prednames_.size()*jobs_.size()); - std::fill(biascoeffsinc_.begin(), biascoeffsinc_.end(), 0.0); - - // Copy the data - if (biascoeffsinc_.size() > 0) *this = other; - - oops::Log::trace() << "ObsBiasIncrement::copy ctor done." << std::endl; + oops::Log::trace() << "ufo::ObsBiasIncrement::copy ctor done." << std::endl; } // ----------------------------------------------------------------------------- void ObsBiasIncrement::diff(const ObsBias & b1, const ObsBias & b2) { - for (std::size_t jj = 0; jj < biascoeffsinc_.size(); ++jj) - biascoeffsinc_[jj]= b1[jj] - b2[jj]; + biascoeffsinc_ = b1.data() - b2.data(); } // ----------------------------------------------------------------------------- void ObsBiasIncrement::zero() { - for (std::size_t jj = 0; jj < biascoeffsinc_.size(); ++jj) - biascoeffsinc_[jj]= 0.0; + biascoeffsinc_ = Eigen::VectorXd::Zero(prednames_.size() * vars_.size()); } // ----------------------------------------------------------------------------- ObsBiasIncrement & ObsBiasIncrement::operator=(const ObsBiasIncrement & rhs) { if (rhs) { - predbases_.clear(); - jobs_.clear(); - - predbases_ = rhs.predbases_; - jobs_ = rhs.jobs_; + prednames_ = rhs.prednames_; + vars_ = rhs.vars_; biascoeffsinc_ = rhs.biascoeffsinc_; } return *this; @@ -134,171 +78,71 @@ ObsBiasIncrement & ObsBiasIncrement::operator=(const ObsBiasIncrement & rhs) { // ----------------------------------------------------------------------------- ObsBiasIncrement & ObsBiasIncrement::operator+=(const ObsBiasIncrement & rhs) { - for (std::size_t jj = 0; jj < biascoeffsinc_.size(); ++jj) - biascoeffsinc_[jj] += rhs[jj]; + biascoeffsinc_ += rhs.biascoeffsinc_; return *this; } // ----------------------------------------------------------------------------- ObsBiasIncrement & ObsBiasIncrement::operator-=(const ObsBiasIncrement & rhs) { - for (std::size_t jj = 0; jj < biascoeffsinc_.size(); ++jj) - biascoeffsinc_[jj] -= rhs[jj]; + biascoeffsinc_ -= rhs.biascoeffsinc_; return *this; } // ----------------------------------------------------------------------------- ObsBiasIncrement & ObsBiasIncrement::operator*=(const double fact) { - for (std::size_t jj = 0; jj < biascoeffsinc_.size(); ++jj) - biascoeffsinc_[jj] *= fact; + biascoeffsinc_ *= fact; return *this; } // ----------------------------------------------------------------------------- void ObsBiasIncrement::axpy(const double fact, const ObsBiasIncrement & rhs) { - for (std::size_t jj = 0; jj < biascoeffsinc_.size(); ++jj) - biascoeffsinc_[jj] += fact * rhs[jj]; + biascoeffsinc_ += fact * rhs.biascoeffsinc_; } // ----------------------------------------------------------------------------- double ObsBiasIncrement::dot_product_with(const ObsBiasIncrement & rhs) const { - double zz = 0.0; - for (std::size_t jj = 0; jj < biascoeffsinc_.size(); ++jj) { - zz += biascoeffsinc_[jj] * rhs[jj]; - } - return zz; + return biascoeffsinc_.dot(rhs.biascoeffsinc_); } // ----------------------------------------------------------------------------- double ObsBiasIncrement::norm() const { double zz = 0.0; - for (std::size_t jj = 0; jj < biascoeffsinc_.size(); ++jj) { - zz += biascoeffsinc_[jj] * biascoeffsinc_[jj]; + if (biascoeffsinc_.size() > 0) { + zz = biascoeffsinc_.norm()/std::sqrt(biascoeffsinc_.size()); } - if (biascoeffsinc_.size() > 0) zz = std::sqrt(zz/biascoeffsinc_.size()); return zz; } // ----------------------------------------------------------------------------- -void ObsBiasIncrement::computeObsBiasTL(const GeoVaLs & geovals, - const std::vector & predData, - ioda::ObsVector & ybiasinc) const { - oops::Log::trace() << "ObsBiasIncrement::computeObsBiasTL starts." << std::endl; - - if (serialSize() > 0) { - const std::size_t nlocs = ybiasinc.nlocs(); - const std::size_t npreds = prednames_.size(); - const std::size_t njobs = jobs_.size(); - - ASSERT(biascoeffsinc_.size() == npreds*njobs); - ASSERT(predData.size() == npreds); - ASSERT(ybiasinc.nvars() == njobs); - - /* predData memory layout (npreds X nlocs X njobs) - * Loc 0 1 2 3 - * -------------------------- - * pred1 Chan1 | 0 3 6 9 - * Chan2 | 1 4 7 10 - * .... | 2 5 8 11 - * - * pred2 Chan1 |12 15 18 21 - * Chan2 |13 16 19 22 - * .... |14 17 20 23 - */ - - /* ybiasinc memory layout (nlocs X njobs) - * ch1 ch2 ch3 ch4 - * Loc -------------------------- - * 0 | 0 1 2 3 - * 1 | 4 5 6 7 - * 2 | 8 9 10 11 - * 3 |12 13 14 15 - * 4 |16 17 18 19 - * ...| - */ - - ybiasinc.zero(); - - // map bias coeffs to eigen matrix (read only) - Eigen::Map coeffs(biascoeffsinc_.data(), npreds, njobs); - - // For each channel: ( nlocs X 1 ) = ( nlocs X npreds ) * ( npreds X 1 ) - for (std::size_t jch = 0; jch < njobs; ++jch) { - for (std::size_t jp = 0; jp < npreds; ++jp) { - const double beta = coeffs(jp, jch); - /// axpy - for (std::size_t jl = 0; jl < nlocs; ++jl) { - ybiasinc[jl*njobs+jch] += predData[jp][jl*njobs+jch] * beta; - } - } - } +void ObsBiasIncrement::serialize(std::vector & vect) const { + if (this->serialSize() > 0) { + throw eckit::NotImplemented("ufo::ObsBiasIncrement::serialize not implemented", Here()); } - - oops::Log::trace() << "ObsBiasIncrement::computeObsBiasTL done." << std::endl; } // ----------------------------------------------------------------------------- -void ObsBiasIncrement::computeObsBiasAD(GeoVaLs & geovals, - const std::vector & predData, - const ioda::ObsVector & ybiasinc) { - oops::Log::trace() << "ObsBiasIncrement::computeObsBiasAD starts." << std::endl; - +void ObsBiasIncrement::deserialize(const std::vector & vect, std::size_t & index) { if (this->serialSize() > 0) { - const std::size_t nlocs = ybiasinc.nlocs(); - const std::size_t npreds = prednames_.size(); - const std::size_t njobs = jobs_.size(); - - ASSERT(biascoeffsinc_.size() == npreds*njobs); - ASSERT(predData.size() == npreds); - ASSERT(ybiasinc.nvars() == njobs); - - // map bias coeffs to eigen matrix (writable) - Eigen::Map coeffs(biascoeffsinc_.data(), npreds, njobs); - - std::size_t indx; - Eigen::VectorXd tmp = Eigen::VectorXd::Zero(nlocs, 1); - for (std::size_t jch = 0; jch < njobs; ++jch) { - for (std::size_t jl = 0; jl < nlocs; ++jl) { - indx = jl*njobs+jch; - if (ybiasinc[indx] != util::missingValue(ybiasinc[indx])) { - tmp(jl) = ybiasinc[indx]; - } - } - // For each channel: ( npreds X 1 ) = ( npreds X nlocs ) * ( nlocs X 1 ) - for (std::size_t jp = 0; jp < npreds; ++jp) { - for (std::size_t jl = 0; jl < nlocs; ++jl) { - coeffs(jp, jch) += predData[jp][jl*njobs+jch] * tmp(jl); - } - } - - // zero out for next job - tmp.setConstant(0.0); - } - - // Sum across the processros - odb_.comm().allReduceInPlace(biascoeffsinc_.begin(), biascoeffsinc_.end(), eckit::mpi::sum()); + throw eckit::NotImplemented("ufo::ObsBiasIncrement::deserialize not implemented", Here()); } - - oops::Log::trace() << "ObsBiasIncrement::computeAD done." << std::endl; } // ----------------------------------------------------------------------------- void ObsBiasIncrement::print(std::ostream & os) const { if (this->serialSize() > 0) { - // map bias coeffs to eigen matrix (writable) + // map bias coeffs to eigen matrix Eigen::Map - coeffs(biascoeffsinc_.data(), prednames_.size(), jobs_.size()); - - os << "ObsBiasIncrement::print " << std::endl; + coeffs(biascoeffsinc_.data(), prednames_.size(), vars_.size()); + os << "ufo::ObsBiasIncrement::print " << std::endl; os << "---------------------------------------------------------------" << std::endl; - auto njobs = jobs_.size(); for (std::size_t p = 0; p < prednames_.size(); ++p) { os << std::fixed << std::setw(20) << prednames_[p] << ": Min= " << std::setw(15) << std::setprecision(8) @@ -310,7 +154,7 @@ void ObsBiasIncrement::print(std::ostream & os) const { << std::endl; } os << "---------------------------------------------------------------" << std::endl; - os << "ObsBiasIncrement::print done" << std::endl; + os << "ufo::ObsBiasIncrement::print done" << std::endl; } } diff --git a/src/ufo/ObsBiasIncrement.h b/src/ufo/ObsBiasIncrement.h index be6b2236b..a3dcd115c 100644 --- a/src/ufo/ObsBiasIncrement.h +++ b/src/ufo/ObsBiasIncrement.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2017-2018 UCAR + * (C) Copyright 2017-2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -8,43 +8,31 @@ #ifndef UFO_OBSBIASINCREMENT_H_ #define UFO_OBSBIASINCREMENT_H_ -#include - -#include #include #include -#include "eckit/config/LocalConfiguration.h" - #include "oops/util/Printable.h" +#include "ufo/ObsBiasParameters.h" #include "ufo/predictors/PredictorBase.h" namespace ioda { class ObsSpace; - class ObsVector; -} - -namespace ioda { - class ObsSpace; - class ObsVector; } namespace ufo { - class GeoVaLs; class ObsBias; -// ----------------------------------------------------------------------------- - +/// Contains increments to bias correction coefficients class ObsBiasIncrement : public util::Printable { public: -// Constructor, destructor - ObsBiasIncrement(const ioda::ObsSpace &, const eckit::Configuration &); + typedef ObsBiasParameters Parameters_; + + ObsBiasIncrement(const ioda::ObsSpace & odb, + const Parameters_ & params); ObsBiasIncrement(const ObsBiasIncrement &, const bool = true); - ObsBiasIncrement(const ObsBiasIncrement &, const eckit::Configuration &); - ~ObsBiasIncrement() {} -// Linear algebra operators + // Linear algebra operators void diff(const ObsBias &, const ObsBias &); void zero(); ObsBiasIncrement & operator=(const ObsBiasIncrement &); @@ -54,41 +42,50 @@ class ObsBiasIncrement : public util::Printable { void axpy(const double, const ObsBiasIncrement &); double dot_product_with(const ObsBiasIncrement &) const; -// I/O and diagnostics + // I/O and diagnostics void read(const eckit::Configuration &) {} void write(const eckit::Configuration &) const {} double norm() const; - double & operator[](const unsigned int ii) {return biascoeffsinc_[ii];} - const double & operator[](const unsigned int ii) const {return biascoeffsinc_[ii];} - -// Linear obs bias model - void computeObsBiasTL(const GeoVaLs &, - const std::vector &, - ioda::ObsVector &) const; - - void computeObsBiasAD(GeoVaLs &, - const std::vector &, - const ioda::ObsVector &); - -// Serialize and deserialize + /// Return bias coefficient increments + const Eigen::VectorXd & data() const {return biascoeffsinc_;} + Eigen::VectorXd & data() {return biascoeffsinc_;} + + // We could store coefficients in a different order. Then it would + // be easier to extract part of the existing vector. + /// Return bias coefficient increments for predictor with index \p jpred + std::vector coefficients(size_t jpred) const { + std::vector coeffs(vars_.size()); + for (size_t jvar = 0; jvar < vars_.size(); ++jvar) { + coeffs[jvar] = biascoeffsinc_(jvar*prednames_.size() + jpred); + } + return coeffs; + } + /// Increment bias coeffiecient increments for predictor with index \p jpred + /// with \p coeffs + void updateCoeff(size_t jpred, const std::vector &coeffs) { + for (size_t jj = 0; jj < vars_.size(); ++jj) { + biascoeffsinc_[jj*prednames_.size() + jpred] += coeffs[jj]; + } + } + + // Serialize and deserialize std::size_t serialSize() const {return biascoeffsinc_.size();} - void serialize(std::vector &) const {} - void deserialize(const std::vector &, std::size_t &) {} + void serialize(std::vector &) const; + void deserialize(const std::vector &, std::size_t &); -// Operator + // Operator operator bool() const {return biascoeffsinc_.size() > 0;} private: void print(std::ostream &) const; - const ioda::ObsSpace & odb_; - const eckit::LocalConfiguration conf_; - - std::vector biascoeffsinc_; - std::vector> predbases_; + /// Bias coefficient increments + Eigen::VectorXd biascoeffsinc_; std::vector prednames_; - std::vector jobs_; + + /// Variables that need to be bias-corrected + oops::Variables vars_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/ObsBiasOperator.cc b/src/ufo/ObsBiasOperator.cc new file mode 100644 index 000000000..095138e26 --- /dev/null +++ b/src/ufo/ObsBiasOperator.cc @@ -0,0 +1,108 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/ObsBiasOperator.h" + +#include +#include + +#include "ioda/ObsVector.h" + +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" + +#include "ufo/ObsBias.h" +#include "ufo/ObsDiagnostics.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- + +ObsBiasOperator::ObsBiasOperator(ioda::ObsSpace & odb) + : odb_(odb) { + oops::Log::trace() << "ObsBiasOperator::create done." << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsBiasOperator::computeObsBias(const GeoVaLs & geovals, ioda::ObsVector & ybias, + const ObsBias & biascoeffs, ObsDiagnostics & ydiags) const { + oops::Log::trace() << "ObsBiasOperator::computeObsBias starting" << std::endl; + + const double missing = util::missingValue(missing); + const Predictors & predictors = biascoeffs.predictors(); + const std::size_t npreds = predictors.size(); + std::vector predData(npreds, ioda::ObsVector(odb_)); + for (std::size_t p = 0; p < npreds; ++p) { + predictors[p]->compute(odb_, geovals, ydiags, predData[p]); + predData[p].save(predictors[p]->name() + "Predictor"); + } + + const oops::Variables &correctedVars = biascoeffs.correctedVars(); + // At present we can label predictors with either the channel number or the variable name, but not + // both. So if there are multiple channels, make sure there's only one (multi-channel) variable. + ASSERT(correctedVars.channels().empty() || + correctedVars.variables().size() == correctedVars.channels().size()); + + const std::size_t nlocs = ybias.nlocs(); + const std::size_t nvars = ybias.nvars(); + + ybias.zero(); + + /* ybias memory layout (nlocs X nvars) + * ch1 ch2 ch3 ch4 + * Loc -------------------------- + * 0 | 0 1 2 3 + * 1 | 4 5 6 7 + * 2 | 8 9 10 11 + * 3 |12 13 14 15 + * 4 |16 17 18 19 + * ...| + */ + + std::vector biasTerm(nlocs); + // For each channel: ( nlocs X 1 ) = ( nlocs X npreds ) * ( npreds X 1 ) + for (std::size_t jvar = 0; jvar < nvars; ++jvar) { + std::string predictorSuffix; + if (correctedVars.channels().empty()) + predictorSuffix = correctedVars[jvar]; + else + predictorSuffix = std::to_string(correctedVars.channels()[jvar]); + + for (std::size_t jp = 0; jp < npreds; ++jp) { + // axpy + const double beta = biascoeffs(jp, jvar); + for (std::size_t jl = 0; jl < nlocs; ++jl) { + if (predData[jp][jl*nvars+jvar] != missing) { + biasTerm[jl] = predData[jp][jl*nvars+jvar] * beta; + ybias[jl*nvars+jvar] += biasTerm[jl]; + } + } + // Save ObsBiasOperatorTerms (bias_coeff * predictor) for QC + const std::string varname = predictors[jp]->name() + "_" + predictorSuffix; + if (ydiags.has(varname)) { + ydiags.allocate(1, oops::Variables({varname})); + ydiags.save(biasTerm, varname, 1); + } else { + oops::Log::error() << varname << " is not reserved in ydiags !" << std::endl; + ABORT("ObsBiasOperatorTerm variable is not reserved in ydiags"); + } + } + } + + oops::Log::trace() << "ObsBiasOperator::computeObsBiasOperator done." << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsBiasOperator::print(std::ostream & os) const { + os << "ObsBiasOperator: linear combination of the predictors." << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/ObsBiasOperator.h b/src/ufo/ObsBiasOperator.h new file mode 100644 index 000000000..d9b7e35cc --- /dev/null +++ b/src/ufo/ObsBiasOperator.h @@ -0,0 +1,47 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_OBSBIASOPERATOR_H_ +#define UFO_OBSBIASOPERATOR_H_ + +#include "oops/util/Printable.h" + +// forward declarations + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsBias; + class ObsDiagnostics; + +/// \brief Application of bias correction. +/// \details Bias correction is computed as a linear combination of bias +/// predictors using bias correction coefficients, both specified in ObsBias class. +class ObsBiasOperator : public util::Printable { + public: + explicit ObsBiasOperator(ioda::ObsSpace &); + + /// Compute bias correction + void computeObsBias(const GeoVaLs &, ioda::ObsVector &, const ObsBias &, ObsDiagnostics &) const; + + private: + /// Print details (used for logging) + void print(std::ostream &) const override; + + /// ObsSpace used for computing predictors + ioda::ObsSpace & odb_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_OBSBIASOPERATOR_H_ diff --git a/src/ufo/ObsBiasParameters.h b/src/ufo/ObsBiasParameters.h new file mode 100644 index 000000000..f44448df6 --- /dev/null +++ b/src/ufo/ObsBiasParameters.h @@ -0,0 +1,89 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_OBSBIASPARAMETERS_H_ +#define UFO_OBSBIASPARAMETERS_H_ + +#include +#include + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +namespace ufo { + +class StaticOrVariationalBCParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(StaticOrVariationalBCParameters, Parameters) + + public: + /// Each element of this list is used to configure a separate predictor. + oops::Parameter> predictors{"predictors", {}, this}; +}; + +class ObsBiasCovariancePriorInflationParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsBiasCovariancePriorInflationParameters, Parameters) + + public: + oops::RequiredParameter ratio{"ratio", this}; + oops::RequiredParameter ratioForSmallDataset{"ratio for small dataset", this}; +}; + +class ObsBiasCovariancePriorParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsBiasCovariancePriorParameters, Parameters) + + public: + oops::RequiredParameter inflation{"inflation", this}; + oops::OptionalParameter inputFile{"input file", this}; +}; + +class ObsBiasCovarianceParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsBiasCovarianceParameters, Parameters) + + public: + /// Default smallest variance value + static double defaultSmallestVariance() { return 1.0e-6; } + /// Default largest variance value + static double defaultLargestVariance() { return 10.0; } + /// Default largest analysis error variance + static double defaultLargestAnalysisVariance() { return 10000.0; } + /// Default step size + static double defaultStepSize() { return 1.e-4; } + + oops::RequiredParameter minimalRequiredObsNumber{ + "minimal required obs number", this}; + oops::Parameter> varianceRange{ + "variance range", {defaultSmallestVariance(), defaultLargestVariance()}, this}; + oops::Parameter stepSize{ + "step size", defaultStepSize(), this}; + oops::Parameter largestAnalysisVariance{ + "largest analysis variance", defaultLargestAnalysisVariance(), this}; + + oops::OptionalParameter prior{ + "prior", this}; +}; + +/// Parameters influencing the bias correction process. +class ObsBiasParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsBiasParameters, Parameters) + + public: + /// List of predictors with unit coefficients (unaffected by VarBC). + oops::Parameter staticBC{"static bc", {}, this}; + /// List of predictors with coefficients determined by variational analysis (VarBC). + oops::Parameter variationalBC{"variational bc", {}, this}; + /// Path to a NetCDF file containing initial values of the coefficients of predictors used + /// in VarBC. + oops::OptionalParameter inputFile{"input file", this}; + /// Options controlling the covariance matrix. + oops::OptionalParameter covariance{"covariance", this}; +}; + +} // namespace ufo + +#endif // UFO_OBSBIASPARAMETERS_H_ diff --git a/src/ufo/ObsDiagnostics.cc b/src/ufo/ObsDiagnostics.cc index 81985a341..0da5bcb8a 100644 --- a/src/ufo/ObsDiagnostics.cc +++ b/src/ufo/ObsDiagnostics.cc @@ -34,6 +34,12 @@ ObsDiagnostics::ObsDiagnostics(const eckit::Configuration & conf, const ioda::Ob // ----------------------------------------------------------------------------- +void ObsDiagnostics::allocate(const int nlev, const oops::Variables & vars) { + gdiags_.allocate(nlev, vars); +} + +// ----------------------------------------------------------------------------- + void ObsDiagnostics::save(const std::vector & vals, const std::string & var, const int lev) { diff --git a/src/ufo/ObsDiagnostics.h b/src/ufo/ObsDiagnostics.h index eb3129d4b..f531a2ff6 100644 --- a/src/ufo/ObsDiagnostics.h +++ b/src/ufo/ObsDiagnostics.h @@ -39,7 +39,14 @@ class ObsDiagnostics : public util::Printable, const oops::Variables &); ~ObsDiagnostics() {} -// I/O + /// \brief Allocate diagnostics for variables \p vars with \p nlev number of levels + /// \details Fails if at least one of the \p vars doesn't exist in the ObsDiagnostics object. + /// Only allocates variables that haven't been allocated before. + /// Fails if one of \p vars is already allocated with a number of levels + /// different than \p nlev; doesn't reallocate variables that are already + /// allocated with \p nlev. + void allocate(const int nlev, const oops::Variables & vars); + void save(const std::vector &, const std::string &, const int); // Interfaces diff --git a/src/ufo/ObsOperator.cc b/src/ufo/ObsOperator.cc index 2cc5d14ce..5adfdd7e6 100644 --- a/src/ufo/ObsOperator.cc +++ b/src/ufo/ObsOperator.cc @@ -1,5 +1,5 @@ /* - * (C) Copyright 2018 UCAR + * (C) Copyright 2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -15,11 +15,11 @@ #include "ioda/ObsVector.h" #include "oops/base/Variables.h" -#include "oops/util/DateTime.h" #include "ufo/GeoVaLs.h" #include "ufo/Locations.h" #include "ufo/ObsBias.h" +#include "ufo/ObsBiasOperator.h" #include "ufo/ObsDiagnostics.h" #include "ufo/ObsOperatorBase.h" @@ -29,11 +29,18 @@ namespace ufo { ObsOperator::ObsOperator(ioda::ObsSpace & os, const eckit::Configuration & conf) : oper_(ObsOperatorFactory::create(os, conf)), odb_(os) -{} - -// ----------------------------------------------------------------------------- - -ObsOperator::~ObsOperator() {} +{ + // We use += rather than = to make sure the Variables objects contain no duplicate entries + // and the variables are sorted alphabetically. + oops::Variables operatorVars; + operatorVars += oper_->simulatedVars(); + oops::Variables obsSpaceVars; + obsSpaceVars += os.obsvariables(); + if (!(operatorVars == obsSpaceVars)) + throw eckit::UserError("The list of variables simulated by the obs operator differs from " + "the list of simulated variables in the obs space", + Here()); +} // ----------------------------------------------------------------------------- @@ -42,7 +49,10 @@ void ObsOperator::simulateObs(const GeoVaLs & gvals, ioda::ObsVector & yy, oper_->simulateObs(gvals, yy, ydiags); if (bias) { ioda::ObsVector ybias(odb_); - bias.computeObsBias(ybias, ydiags, bias.computePredictors(gvals, ydiags)); + ObsBiasOperator biasoper(odb_); + biasoper.computeObsBias(gvals, ybias, bias, ydiags); + // update H(x) with bias correction + yy += ybias; ybias.save("ObsBias"); } } @@ -55,9 +65,8 @@ const oops::Variables & ObsOperator::requiredVars() const { // ----------------------------------------------------------------------------- -std::unique_ptr ObsOperator::locations(const util::DateTime & t1, - const util::DateTime & t2) const { - return oper_->locations(t1, t2); +std::unique_ptr ObsOperator::locations() const { + return oper_->locations(); } // ----------------------------------------------------------------------------- diff --git a/src/ufo/ObsOperator.h b/src/ufo/ObsOperator.h index ec2d69257..33f5e99c2 100644 --- a/src/ufo/ObsOperator.h +++ b/src/ufo/ObsOperator.h @@ -15,6 +15,8 @@ #include "oops/util/Printable.h" +#include "ufo/ObsOperatorBase.h" + // Forward declarations namespace eckit { class Configuration; @@ -24,10 +26,6 @@ namespace oops { class Variables; } -namespace util { - class DateTime; -} - namespace ioda { class ObsSpace; class ObsVector; @@ -38,7 +36,6 @@ namespace ufo { class Locations; class ObsBias; class ObsDiagnostics; - class ObsOperatorBase; // ----------------------------------------------------------------------------- @@ -46,7 +43,6 @@ class ObsOperator : public util::Printable, private boost::noncopyable { public: ObsOperator(ioda::ObsSpace &, const eckit::Configuration &); - ~ObsOperator(); /// Obs Operator void simulateObs(const GeoVaLs &, ioda::ObsVector &, const ObsBias &, ObsDiagnostics &) const; @@ -55,7 +51,7 @@ class ObsOperator : public util::Printable, const oops::Variables & requiredVars() const; /// Operator locations - std::unique_ptr locations(const util::DateTime &, const util::DateTime &) const; + std::unique_ptr locations() const; private: void print(std::ostream &) const; diff --git a/src/ufo/ObsOperatorBase.cc b/src/ufo/ObsOperatorBase.cc index 407372ff9..895402704 100644 --- a/src/ufo/ObsOperatorBase.cc +++ b/src/ufo/ObsOperatorBase.cc @@ -7,6 +7,8 @@ #include "ufo/ObsOperatorBase.h" +#include + #include "eckit/config/Configuration.h" #include "ioda/ObsSpace.h" #include "oops/util/abor1_cpp.h" @@ -17,9 +19,20 @@ namespace ufo { // ----------------------------------------------------------------------------- -std::unique_ptr ObsOperatorBase::locations(const util::DateTime & t1, - const util::DateTime & t2) const { - return std::unique_ptr(new Locations(odb_, t1, t2)); +std::unique_ptr ObsOperatorBase::locations() const { + std::vector lons(odb_.nlocs()); + std::vector lats(odb_.nlocs()); + std::vector times(odb_.nlocs()); + odb_.get_db("MetaData", "latitude", lats); + odb_.get_db("MetaData", "longitude", lons); + odb_.get_db("MetaData", "datetime", times); + return std::unique_ptr(new Locations(lons, lats, times, odb_.distribution())); +} + +// ----------------------------------------------------------------------------- + +oops::Variables ObsOperatorBase::simulatedVars() const { + return odb_.obsvariables(); } // ----------------------------------------------------------------------------- diff --git a/src/ufo/ObsOperatorBase.h b/src/ufo/ObsOperatorBase.h index 435448b5e..3b7bda8f4 100644 --- a/src/ufo/ObsOperatorBase.h +++ b/src/ufo/ObsOperatorBase.h @@ -18,7 +18,6 @@ #include "ioda/ObsSpace.h" #include "oops/base/Variables.h" #include "oops/util/abor1_cpp.h" -#include "oops/util/DateTime.h" #include "oops/util/Printable.h" namespace ioda { @@ -47,8 +46,12 @@ class ObsOperatorBase : public util::Printable, virtual const oops::Variables & requiredVars() const = 0; /// Locations for GeoVaLs - virtual std::unique_ptr locations(const util::DateTime &, - const util::DateTime &) const; + virtual std::unique_ptr locations() const; + +/// \brief List of variables simulated by this operator. +/// +/// The default implementation returns the list of all simulated variables in the ObsSpace. + virtual oops::Variables simulatedVars() const; private: virtual void print(std::ostream &) const = 0; diff --git a/src/ufo/aerosols/AOP/CMakeLists.txt b/src/ufo/aerosols/AOP/CMakeLists.txt new file mode 100644 index 000000000..402d652cf --- /dev/null +++ b/src/ufo/aerosols/AOP/CMakeLists.txt @@ -0,0 +1,19 @@ +# (C) Copyright 2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( aodext_src_files + ObsAodExt.h + ObsAodExt.cc + ObsAodExtTLAD.h + ObsAodExtTLAD.cc + ObsAodExt.interface.F90 + ObsAodExt.interface.h + ObsAodExtTLAD.interface.F90 + ObsAodExtTLAD.interface.h + ufo_aodext_mod.F90 + ufo_aodext_tlad_mod.F90 +PARENT_SCOPE +) + diff --git a/src/ufo/radarreflectivity/ObsRadarReflectivity.cc b/src/ufo/aerosols/AOP/ObsAodExt.cc similarity index 50% rename from src/ufo/radarreflectivity/ObsRadarReflectivity.cc rename to src/ufo/aerosols/AOP/ObsAodExt.cc index a9c5f63e5..e7d28c30b 100644 --- a/src/ufo/radarreflectivity/ObsRadarReflectivity.cc +++ b/src/ufo/aerosols/AOP/ObsAodExt.cc @@ -1,11 +1,11 @@ /* - * (C) Copyright 2017-2018 UCAR + * (C) Copyright 2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#include "ufo/radarreflectivity/ObsRadarReflectivity.h" +#include "ufo/aerosols/AOP/ObsAodExt.h" #include @@ -19,38 +19,37 @@ namespace ufo { // ----------------------------------------------------------------------------- -static ObsOperatorMaker makerRadarReflectivity_("RadarReflectivity"); +static ObsOperatorMaker makerAodExt_("AodExt"); // ----------------------------------------------------------------------------- -ObsRadarReflectivity::ObsRadarReflectivity(const ioda::ObsSpace & odb, +ObsAodExt::ObsAodExt(const ioda::ObsSpace & odb, const eckit::Configuration & config) : ObsOperatorBase(odb, config), keyOper_(0), odb_(odb), varin_() { - ufo_radarreflectivity_setup_f90(keyOper_, config, odb.obsvariables(), varin_); - - oops::Log::trace() << "ObsRadarReflectivity created." << std::endl; + ufo_aodext_setup_f90(keyOper_, config, odb.obsvariables(), varin_); + oops::Log::trace() << "ObsAodExt created." << std::endl; } // ----------------------------------------------------------------------------- -ObsRadarReflectivity::~ObsRadarReflectivity() { - ufo_radarreflectivity_delete_f90(keyOper_); - oops::Log::trace() << "ObsRadarReflectivity destructed" << std::endl; +ObsAodExt::~ObsAodExt() { + ufo_aodext_delete_f90(keyOper_); + oops::Log::trace() << "ObsAodExt destructed" << std::endl; } // ----------------------------------------------------------------------------- -void ObsRadarReflectivity::simulateObs(const GeoVaLs & gv, ioda::ObsVector & ovec, - ObsDiagnostics &) const { - ufo_radarreflectivity_simobs_f90(keyOper_, gv.toFortran(), odb_, ovec.nvars(), ovec.nlocs(), +void ObsAodExt::simulateObs(const GeoVaLs & gv, ioda::ObsVector & ovec, + ObsDiagnostics &) const { + ufo_aodext_simobs_f90(keyOper_, gv.toFortran(), odb_, ovec.nvars(), ovec.nlocs(), ovec.toFortran()); - oops::Log::trace() << "ObsRadarReflectivity: observation operator run" << std::endl; + oops::Log::trace() << "ObsAodExt: observation operator run" << std::endl; } // ----------------------------------------------------------------------------- -void ObsRadarReflectivity::print(std::ostream & os) const { - os << "ObsRadarReflectivity::print not implemented"; +void ObsAodExt::print(std::ostream & os) const { + os << "ObsAodExt::print not implemented"; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/radarreflectivity/ObsRadarReflectivity.h b/src/ufo/aerosols/AOP/ObsAodExt.h similarity index 63% rename from src/ufo/radarreflectivity/ObsRadarReflectivity.h rename to src/ufo/aerosols/AOP/ObsAodExt.h index 3ad0db10a..442fc3cfe 100644 --- a/src/ufo/radarreflectivity/ObsRadarReflectivity.h +++ b/src/ufo/aerosols/AOP/ObsAodExt.h @@ -1,12 +1,12 @@ /* - * (C) Copyright 2017-2018 UCAR + * (C) Copyright 2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#ifndef UFO_RADARREFLECTIVITY_OBSRADARREFLECTIVITY_H_ -#define UFO_RADARREFLECTIVITY_OBSRADARREFLECTIVITY_H_ +#ifndef UFO_AEROSOLS_AOP_OBSAODEXT_H_ +#define UFO_AEROSOLS_AOP_OBSAODEXT_H_ #include #include @@ -14,8 +14,8 @@ #include "oops/base/Variables.h" #include "oops/util/ObjectCounter.h" +#include "ufo/aerosols/AOP/ObsAodExt.interface.h" #include "ufo/ObsOperatorBase.h" -#include "ufo/radarreflectivity/ObsRadarReflectivity.interface.h" /// Forward declarations namespace eckit { @@ -32,14 +32,14 @@ namespace ufo { class ObsDiagnostics; // ----------------------------------------------------------------------------- -/// RadarReflectivity observation operator class -class ObsRadarReflectivity : public ObsOperatorBase, - private util::ObjectCounter { +/// AodExt observation operator class +class ObsAodExt : public ObsOperatorBase, + private util::ObjectCounter { public: - static const std::string classname() {return "ufo::ObsRadarReflectivity";} + static const std::string classname() {return "ufo::ObsAodExt";} - ObsRadarReflectivity(const ioda::ObsSpace &, const eckit::Configuration &); - virtual ~ObsRadarReflectivity(); + ObsAodExt(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsAodExt(); // Obs Operator void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; @@ -60,4 +60,4 @@ class ObsRadarReflectivity : public ObsOperatorBase, // ----------------------------------------------------------------------------- } // namespace ufo -#endif // UFO_RADARREFLECTIVITY_OBSRADARREFLECTIVITY_H_ +#endif // UFO_AEROSOLS_AOP_OBSAODEXT_H_ diff --git a/src/ufo/radarreflectivity/ObsRadarReflectivity.interface.F90 b/src/ufo/aerosols/AOP/ObsAodExt.interface.F90 similarity index 59% rename from src/ufo/radarreflectivity/ObsRadarReflectivity.interface.F90 rename to src/ufo/aerosols/AOP/ObsAodExt.interface.F90 index 67aa5459e..9644dafd3 100644 --- a/src/ufo/radarreflectivity/ObsRadarReflectivity.interface.F90 +++ b/src/ufo/aerosols/AOP/ObsAodExt.interface.F90 @@ -1,28 +1,26 @@ -! (C) Copyright 2017-2018 UCAR +! (C) Copyright 2021 UCAR ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -!> Fortran radarreflectivity module for functions on the interface between C++ and Fortran +!> Fortran aodext module for functions on the interface between C++ and Fortran ! to handle observation operators -module ufo_radarreflectivity_mod_c +module ufo_aodext_mod_c use iso_c_binding - use ufo_radarreflectivity_mod - use ufo_geovals_mod, only: ufo_geovals - use ufo_geovals_mod_c, only: ufo_geovals_registry + use ufo_aodext_mod implicit none private ! ------------------------------------------------------------------------------ -#define LISTED_TYPE ufo_radarreflectivity +#define LISTED_TYPE ufo_aodext !> Linked list interface - defines registry_t type #include "oops/util/linkedList_i.f" !> Global registry - type(registry_t) :: ufo_radarreflectivity_registry + type(registry_t) :: ufo_aodext_registry ! ------------------------------------------------------------------------------ @@ -34,7 +32,7 @@ module ufo_radarreflectivity_mod_c ! ------------------------------------------------------------------------------ -subroutine ufo_radarreflectivity_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_radarreflectivity_setup_f90') +subroutine ufo_aodext_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_aodext_setup_f90') use fckit_configuration_module, only: fckit_configuration use oops_variables_mod implicit none @@ -43,10 +41,10 @@ subroutine ufo_radarreflectivity_setup_c(c_key_self, c_conf, c_obsvars, c_geovar type(c_ptr), value, intent(in) :: c_obsvars ! variables to be simulated type(c_ptr), value, intent(in) :: c_geovars ! variables requested from the model -type(ufo_radarreflectivity), pointer :: self +type(ufo_aodext), pointer :: self type(fckit_configuration) :: f_conf -call ufo_radarreflectivity_registry%setup(c_key_self, self) +call ufo_aodext_registry%setup(c_key_self, self) f_conf = fckit_configuration(c_conf) self%obsvars = oops_variables(c_obsvars) @@ -54,25 +52,26 @@ subroutine ufo_radarreflectivity_setup_c(c_key_self, c_conf, c_obsvars, c_geovar call self%setup(f_conf) -end subroutine ufo_radarreflectivity_setup_c +end subroutine ufo_aodext_setup_c ! ------------------------------------------------------------------------------ -subroutine ufo_radarreflectivity_delete_c(c_key_self) bind(c,name='ufo_radarreflectivity_delete_f90') +subroutine ufo_aodext_delete_c(c_key_self) bind(c,name='ufo_aodext_delete_f90') implicit none integer(c_int), intent(inout) :: c_key_self -type(ufo_radarreflectivity), pointer :: self +type(ufo_aodext), pointer :: self -call ufo_radarreflectivity_registry%delete(c_key_self, self) +call ufo_aodext_registry%delete(c_key_self, self) -end subroutine ufo_radarreflectivity_delete_c +end subroutine ufo_aodext_delete_c ! ------------------------------------------------------------------------------ -subroutine ufo_radarreflectivity_simobs_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, & - c_hofx) bind(c,name='ufo_radarreflectivity_simobs_f90') - +subroutine ufo_aodext_simobs_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, & + c_hofx) bind(c,name='ufo_aodext_simobs_f90') +use ufo_geovals_mod, only: ufo_geovals +use ufo_geovals_mod_c, only: ufo_geovals_registry implicit none integer(c_int), intent(in) :: c_key_self integer(c_int), intent(in) :: c_key_geovals @@ -80,15 +79,15 @@ subroutine ufo_radarreflectivity_simobs_c(c_key_self, c_key_geovals, c_obsspace, integer(c_int), intent(in) :: c_nvars, c_nlocs real(c_double), intent(inout) :: c_hofx(c_nvars, c_nlocs) -type(ufo_radarreflectivity), pointer :: self -type(ufo_geovals), pointer :: geovals +type(ufo_aodext), pointer :: self +type(ufo_geovals), pointer :: geovals -call ufo_radarreflectivity_registry%get(c_key_self, self) +call ufo_aodext_registry%get(c_key_self, self) call ufo_geovals_registry%get(c_key_geovals, geovals) call self%simobs(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) -end subroutine ufo_radarreflectivity_simobs_c +end subroutine ufo_aodext_simobs_c ! ------------------------------------------------------------------------------ -end module ufo_radarreflectivity_mod_c +end module ufo_aodext_mod_c diff --git a/src/ufo/radarreflectivity/ObsRadarReflectivity.interface.h b/src/ufo/aerosols/AOP/ObsAodExt.interface.h similarity index 55% rename from src/ufo/radarreflectivity/ObsRadarReflectivity.interface.h rename to src/ufo/aerosols/AOP/ObsAodExt.interface.h index 47975c78f..44f9301e6 100644 --- a/src/ufo/radarreflectivity/ObsRadarReflectivity.interface.h +++ b/src/ufo/aerosols/AOP/ObsAodExt.interface.h @@ -1,12 +1,12 @@ /* - * (C) Copyright 2017-2018 UCAR + * (C) Copyright 2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#ifndef UFO_RADARREFLECTIVITY_OBSRADARREFLECTIVITY_INTERFACE_H_ -#define UFO_RADARREFLECTIVITY_OBSRADARREFLECTIVITY_INTERFACE_H_ +#ifndef UFO_AEROSOLS_AOP_OBSAODEXT_INTERFACE_H_ +#define UFO_AEROSOLS_AOP_OBSAODEXT_INTERFACE_H_ #include "ioda/ObsSpace.h" #include "oops/base/Variables.h" @@ -14,16 +14,16 @@ namespace ufo { -/// Interface to Fortran UFO radarreflectivity routines +/// Interface to Fortran UFO aodext routines extern "C" { // ----------------------------------------------------------------------------- - void ufo_radarreflectivity_setup_f90(F90hop &, const eckit::Configuration &, + void ufo_aodext_setup_f90(F90hop &, const eckit::Configuration &, const oops::Variables &, oops::Variables &); - void ufo_radarreflectivity_delete_f90(F90hop &); - void ufo_radarreflectivity_simobs_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + void ufo_aodext_delete_f90(F90hop &); + void ufo_aodext_simobs_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, const int &, const int &, double &); // ----------------------------------------------------------------------------- @@ -31,4 +31,4 @@ extern "C" { } // extern C } // namespace ufo -#endif // UFO_RADARREFLECTIVITY_OBSRADARREFLECTIVITY_INTERFACE_H_ +#endif // UFO_AEROSOLS_AOP_OBSAODEXT_INTERFACE_H_ diff --git a/src/ufo/aerosols/AOP/ObsAodExtTLAD.cc b/src/ufo/aerosols/AOP/ObsAodExtTLAD.cc new file mode 100644 index 000000000..d52c13362 --- /dev/null +++ b/src/ufo/aerosols/AOP/ObsAodExtTLAD.cc @@ -0,0 +1,76 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/aerosols/AOP/ObsAodExtTLAD.h" + +#include + +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" +#include "ufo/GeoVaLs.h" +#include "ufo/ObsBias.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static LinearObsOperatorMaker makerAodExtTL_("AodExt"); +// ----------------------------------------------------------------------------- + +ObsAodExtTLAD::ObsAodExtTLAD(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : LinearObsOperatorBase(odb), keyOper_(0), varin_() +{ + const eckit::Configuration * configc = &config; + + ufo_aodext_tlad_setup_f90(keyOper_, config, odb.obsvariables(), varin_); + + oops::Log::trace() << "ObsAodExtTLAD created" << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsAodExtTLAD::~ObsAodExtTLAD() { + ufo_aodext_tlad_delete_f90(keyOper_); + oops::Log::trace() << "ObsAodExtTLAD destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsAodExtTLAD::setTrajectory(const GeoVaLs & geovals, + const ObsBias & bias, ObsDiagnostics &) { + oops::Log::trace() << "ObsAodExtTLAD:trajectory entering" < +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" + +#include "ufo/aerosols/AOP/ObsAodExtTLAD.interface.h" +#include "ufo/LinearObsOperatorBase.h" + +// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsBias; + +// ----------------------------------------------------------------------------- +/// AodExt TL/AD observation operator class +class ObsAodExtTLAD : public LinearObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsAodExtTLAD";} + + ObsAodExtTLAD(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsAodExtTLAD(); + + // Obs Operators + void setTrajectory(const GeoVaLs &, const ObsBias &, ObsDiagnostics &) override; + void simulateObsTL(const GeoVaLs &, ioda::ObsVector &) const override; + void simulateObsAD(GeoVaLs &, const ioda::ObsVector &) const override; + + // Other + const oops::Variables & requiredVars() const override {return varin_;} + + int & toFortran() {return keyOper_;} + const int & toFortran() const {return keyOper_;} + + private: + void print(std::ostream &) const override; + F90hop keyOper_; + oops::Variables varin_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_AEROSOLS_AOP_OBSAODEXTTLAD_H_ diff --git a/src/ufo/aerosols/AOP/ObsAodExtTLAD.interface.F90 b/src/ufo/aerosols/AOP/ObsAodExtTLAD.interface.F90 new file mode 100644 index 000000000..b14f67804 --- /dev/null +++ b/src/ufo/aerosols/AOP/ObsAodExtTLAD.interface.F90 @@ -0,0 +1,130 @@ +! (C) Copyright 2021 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran aodext module for functions on the interface between C++ and Fortran +! to handle tl/ad observation operators + +module ufo_aodext_tlad_mod_c + + use iso_c_binding + use ufo_aodext_tlad_mod + implicit none + private + +#define LISTED_TYPE ufo_aodext_tlad + + !> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + + !> Global registry + type(registry_t) :: ufo_aodext_tlad_registry + +contains + + ! ------------------------------------------------------------------------------ + !> Linked list implementation +#include "oops/util/linkedList_c.f" + +! ------------------------------------------------------------------------------ + +subroutine ufo_aodext_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_aodext_tlad_setup_f90') +use fckit_configuration_module, only: fckit_configuration +use oops_variables_mod +implicit none +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), value, intent(in) :: c_conf +type(c_ptr), value, intent(in) :: c_obsvars ! variables to be simulated +type(c_ptr), value, intent(in) :: c_geovars ! variables requested from the model + +type(ufo_aodext_tlad), pointer :: self +type(fckit_configuration) :: f_conf + +call ufo_aodext_tlad_registry%setup(c_key_self, self) +f_conf = fckit_configuration(c_conf) + +self%obsvars = oops_variables(c_obsvars) +self%geovars = oops_variables(c_geovars) + +call self%setup(f_conf) + +end subroutine ufo_aodext_tlad_setup_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_aodext_tlad_delete_c(c_key_self) bind(c,name='ufo_aodext_tlad_delete_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self + +type(ufo_aodext_tlad), pointer :: self + +call ufo_aodext_tlad_registry%delete(c_key_self, self) + +end subroutine ufo_aodext_tlad_delete_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_aodext_tlad_settraj_c(c_key_self, c_key_geovals, c_obsspace) bind(c,name='ufo_aodext_tlad_settraj_f90') +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_geovals_mod, only: ufo_geovals +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace + +type(ufo_aodext_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +call ufo_aodext_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) +call self%settraj(geovals, c_obsspace) + +end subroutine ufo_aodext_tlad_settraj_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_aodext_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) bind(c,name='ufo_aodext_simobs_tl_f90') +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_geovals_mod, only: ufo_geovals +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nvars, c_nlocs +real(c_double), intent(inout) :: c_hofx(c_nvars, c_nlocs) + +type(ufo_aodext_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +call ufo_aodext_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) +call self%simobs_tl(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) + +end subroutine ufo_aodext_simobs_tl_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_aodext_simobs_ad_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) bind(c,name='ufo_aodext_simobs_ad_f90') +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_geovals_mod, only: ufo_geovals +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nvars, c_nlocs +real(c_double), intent(in) :: c_hofx(c_nvars, c_nlocs) + +type(ufo_aodext_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +call ufo_aodext_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) +call self%simobs_ad(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) + +end subroutine ufo_aodext_simobs_ad_c + +! ------------------------------------------------------------------------------ + + +end module ufo_aodext_tlad_mod_c diff --git a/src/ufo/aerosols/AOP/ObsAodExtTLAD.interface.h b/src/ufo/aerosols/AOP/ObsAodExtTLAD.interface.h new file mode 100644 index 000000000..b2e43c84c --- /dev/null +++ b/src/ufo/aerosols/AOP/ObsAodExtTLAD.interface.h @@ -0,0 +1,36 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_AEROSOLS_AOP_OBSAODEXTTLAD_INTERFACE_H_ +#define UFO_AEROSOLS_AOP_OBSAODEXTTLAD_INTERFACE_H_ + +#include "ioda/ObsSpace.h" +#include "oops/base/Variables.h" +#include "ufo/Fortran.h" + +namespace ufo { + +/// Interface to Fortran UFO aodext routines + +extern "C" { + +// ----------------------------------------------------------------------------- + + void ufo_aodext_tlad_setup_f90(F90hop &, const eckit::Configuration &, + const oops::Variables &, oops::Variables &); + void ufo_aodext_tlad_delete_f90(F90hop &); + void ufo_aodext_tlad_settraj_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &); + void ufo_aodext_simobs_tl_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + const int &, const int &, double &); + void ufo_aodext_simobs_ad_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + const int &, const int &, const double &); +// ----------------------------------------------------------------------------- + +} // extern C + +} // namespace ufo +#endif // UFO_AEROSOLS_AOP_OBSAODEXTTLAD_INTERFACE_H_ diff --git a/src/ufo/aerosols/AOP/ufo_aodext_mod.F90 b/src/ufo/aerosols/AOP/ufo_aodext_mod.F90 new file mode 100644 index 000000000..970bdb78a --- /dev/null +++ b/src/ufo/aerosols/AOP/ufo_aodext_mod.F90 @@ -0,0 +1,237 @@ +! (C) Copyright 2021 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module for aodext observation operator + +module ufo_aodext_mod + + use iso_c_binding + use kinds + use oops_variables_mod + use fckit_log_module, only : fckit_log + use ufo_vars_mod + use missing_values_mod + + implicit none + private + +!> Fortran derived type for the observation type + + type, public :: ufo_aodext + private + type(oops_variables), public :: obsvars + type(oops_variables), public :: geovars + real(kind_real), public, allocatable :: wavelength(:)!(nprofiles) + integer, public :: nprofiles + contains + procedure :: setup => ufo_aodext_setup + procedure :: simobs => ufo_aodext_simobs + final :: destructor + end type ufo_aodext + +!> Default variables required from model + character(len=maxvarlen), dimension(2), parameter :: varindefault = (/var_delp, var_airdens/) + character(len=maxvarlen), dimension(3), parameter :: extdefault = (/var_ext1, var_ext2, var_ext3/) +! needs at least 2 profiles of extinction in bkg for angstrom law +contains + +!---------------- +integer function b_channel( bracket, nprofiles, bkg_wavelengths, obs_wavelength ) ! given a wavelength, return the index of bracket wavelengths +implicit none +integer, intent(in) :: bracket ! bracket index (1 or 2) +integer, intent(in) :: nprofiles ! number of bkg profiles +real(kind_real), dimension(nprofiles) :: bkg_wavelengths ! array of bkg wavelengths +real(kind_real) :: obs_wavelength ! observed wavelength + +character(len=maxvarlen) :: err_msg +integer :: j + j = 1 + do while(j < nprofiles) + if(obs_wavelength >= bkg_wavelengths(j) .and.& + obs_wavelength < bkg_wavelengths(j+1)) then + if (bracket == 1) then + b_channel = j + else if (bracket == 2) then + b_channel = j + 1 + else + write(err_msg,*) "ufo_aodext_mod: function b_channel: bracket index should be 1 (lower) or 2 (upper)" + call abor1_ftn(err_msg) + endif + j = j+ 1 + else if(obs_wavelength > bkg_wavelengths(j) .and.& + obs_wavelength <= bkg_wavelengths(j+1)) then + if (bracket == 1) then + b_channel = j + else if (bracket == 2) then + b_channel = j + 1 + else + write(err_msg,*) "ufo_aodext_mod: function b_channel: bracket index should be 1 (lower) or 2 (upper)" + call abor1_ftn(err_msg) + endif + j = j +1 + endif + j = j + 1 + enddo + return +end function b_channel + +! ------------------------------------------------------------------------------ +subroutine ufo_aodext_setup(self, f_conf) +use fckit_configuration_module, only: fckit_configuration +implicit none +class(ufo_aodext), intent(inout) :: self +type(fckit_configuration), intent(in) :: f_conf + +!Locals +integer n +character(len=maxvarlen) :: err_msg + + ! Fill in geovars: input variables requested from the model + ! Need slots for airdens and delp + + call f_conf%get_or_die("nprofiles", self%nprofiles) + + if (self%nprofiles < 2 .or. self%nprofiles > 3) then + write(err_msg,*) 'ufo_aodext_setup: number of extinction profiles must be 2 or 3' + call abor1_ftn(err_msg) + endif + + do n = 1, self%nprofiles + call self%geovars%push_back(extdefault(n)) + enddo + call self%geovars%push_back(varindefault) + + ! Wavelengths for extinction profiles, specified in yaml in croissant order + allocate(self%wavelength(self%nprofiles)) + call f_conf%get_or_die("bkg_wavelengths", self%wavelength) + !Check that the wavelengths are in an ascending order + n = 1 + do while (n < self%nprofiles) + if(self%wavelength(n) > self%wavelength(n+1)) then + write(err_msg,*) ' ufo_aodext_setup: bkg wavelengths should be in an ascending order' + call abor1_ftn(err_msg) + endif + n = n + 1 + enddo + +end subroutine ufo_aodext_setup + +! ------------------------------------------------------------------------------ +subroutine destructor(self) +implicit none +type(ufo_aodext), intent(inout) :: self + + if (allocated(self%wavelength)) deallocate(self%wavelength) + +end subroutine destructor +! ------------------------------------------------------------------------------ +subroutine ufo_aodext_simobs(self, geovals, obss, nvars, nlocs, hofx) +use kinds +use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var +use iso_c_binding +use obsspace_mod +use ufo_constants_mod, only: grav, zero + +implicit none +class(ufo_aodext), intent(in) :: self +integer, intent(in) :: nvars, nlocs +type(ufo_geovals), intent(in) :: geovals +real(c_double), intent(inout) :: hofx(nvars, nlocs) +type(c_ptr), value, intent(in) :: obss + +! Local variables +type(ufo_geoval), pointer :: ext_profile +type(ufo_geoval), pointer :: delp_profile +type(ufo_geoval), pointer :: airdens_profile + +real(kind_real), dimension(:,:,:), allocatable :: ext !(km, nlocs, nch) ext profiles interp at obs loc [km-1] +real(kind_real), dimension(:,:), allocatable :: airdens !(km, nlocs) airdens profiles interp at obs loc [kg.m-3] +real(kind_real), dimension(:,:), allocatable :: delp !(km, nlocs) air pressure thickness profiles at obs loc[Pa] +real(kind_real), dimension(:,:), allocatable :: aod_bkg !(nlocs, nch) AOD computed from modeled ext profiles +real(kind_real), dimension(:), allocatable :: obss_wavelength ! (nvars) observed AOD wavelengths [nm] + +real(kind_real) :: angstrom ! (Angstrom coefficient calculated from bkg AOD and wavelengths) +real(kind_real) :: logm + +character(len=MAXVARLEN) :: geovar +real(c_double) :: missing + +character(len=MAXVARLEN) :: message +integer :: nlayers +integer :: km, nobs, nch, ic, i, j, k + + ! Get airdens and delp and number of layers from geovals + ! ----------------------- + geovar = self%geovars%variable(1) + + call ufo_geovals_get_var(geovals, var_delp, delp_profile) + nlayers = delp_profile%nval ! number of model layers + + allocate(delp(nlayers,nlocs)) + delp = delp_profile%vals + + call ufo_geovals_get_var(geovals, var_airdens, airdens_profile) + allocate(airdens(nlayers,nlocs)) + airdens = airdens_profile%vals + + ! Get extinction profiles from geovals + ! --------------------- + allocate(ext(nlayers, nlocs, self%nprofiles)) + do nch = 1, self%nprofiles + geovar = self%geovars%variable(nch) + call ufo_geovals_get_var(geovals, geovar, ext_profile) + ext(:,:,nch) = ext_profile%vals + enddo + + ! Get some metadata from obsspace, observed AOD wavelengths + ! ----------------------- + allocate(obss_wavelength(nvars)) + call obsspace_get_db(obss,"VarMetaData", "obs_wavelength", obss_wavelength) + + ! Check if observed wavelength AOD is within the range of bkg wavelength to apply angstrom law + ! else hofx set to missing value + do ic = 1, nvars + if(obss_wavelength(ic) < self%wavelength(1) .or. obss_wavelength(ic) > self%wavelength(self%nprofiles)) then + write(message,*) 'ufo_aodext_simobs: observed wavelength outside of bkg wavelengths range', obss_wavelength(ic) + call fckit_log%info(message) + endif + enddo + ! Observation operator + ! ------------------ + + ! Calculation of AOD from extinction profiles + ! ------------------ + allocate(aod_bkg(nlocs, self%nprofiles)) + aod_bkg = zero + do nch = 1, self%nprofiles + do nobs = 1, nlocs + do k =1, nlayers + aod_bkg(nobs,nch) = aod_bkg(nobs, nch) + (ext(k,nobs,nch) * delp(k,nobs)/(airdens(k,nobs))/(grav*1000.0_kind_real)) + enddo + enddo + enddo + + ! hofx: angstrom law + ! ------------------ + missing =missing_value(missing) + hofx = zero + do nobs = 1, nlocs + do ic = 1, nvars + if(obss_wavelength(ic) < self%wavelength(1) .or. obss_wavelength(ic) > self%wavelength(self%nprofiles)) then + hofx(ic,nobs) = missing + else + i = b_channel(1, self%nprofiles, self%wavelength, obss_wavelength(ic)) + j = b_channel(2, self%nprofiles, self%wavelength, obss_wavelength(ic)) + logm = log(self%wavelength(i)/self%wavelength(j)) + angstrom = log(aod_bkg(nobs,i)/aod_bkg(nobs,j))/logm + hofx(ic,nobs) = aod_bkg(nobs,i) * (obss_wavelength(ic)/self%wavelength(i))**angstrom + endif + enddo + enddo + deallocate(ext, airdens, delp, aod_bkg, obss_wavelength) + +end subroutine ufo_aodext_simobs +! ------------------------------------------------------------------------------ +end module ufo_aodext_mod diff --git a/src/ufo/aerosols/AOP/ufo_aodext_tlad_mod.F90 b/src/ufo/aerosols/AOP/ufo_aodext_tlad_mod.F90 new file mode 100644 index 000000000..d448e8ac7 --- /dev/null +++ b/src/ufo/aerosols/AOP/ufo_aodext_tlad_mod.F90 @@ -0,0 +1,404 @@ +! (C) Copyright 2021 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module for aodext tl/ad observation operator + +module ufo_aodext_tlad_mod + + use kinds + use missing_values_mod + use oops_variables_mod + use ufo_vars_mod + + implicit none + private + + type, public :: ufo_aodext_tlad + private + type(oops_variables), public :: obsvars + type(oops_variables), public :: geovars + real(kind_real), allocatable :: airdens(:,:) !(km, nlocs) airdens profile interp at obs loc [kg.m-3] + real(kind_real), allocatable :: delp(:,:) !(km, nlocs) air pressure thickness profiles at obs loc[Pa] + real(kind_real), allocatable :: ext(:,:,:) !(km, nlocs, nch) extinction profiles at obs loc [km-1] + real(kind_real), allocatable :: obss_wavelength(:)!(nvars) observed AOD wavelengths [nm] + real(kind_real), public, allocatable :: wavelength(:)!(nch) background extinction profile's wavelengths[nm] + integer :: nlayers, nprofiles + + contains + procedure :: setup => ufo_aodext_tlad_setup + procedure :: settraj => ufo_aodext_tlad_settraj + procedure :: simobs_tl => ufo_aodext_simobs_tl + procedure :: simobs_ad => ufo_aodext_simobs_ad + final :: destructor + end type ufo_aodext_tlad + +!> Default variables required from model + character(len=maxvarlen), dimension(3), parameter :: extdefault = (/var_ext1, var_ext2, var_ext3/) +! needs at least 2 profiles of extinction in bkg for angstrom law + +contains + +!---------------- +integer function b_channel( bracket, nprofiles, bkg_wavelengths, obs_wavelength ) ! given a wavelength, return the index of bracket wavelengths +implicit none +integer, intent(in) :: bracket ! bracket index (1 or 2) +integer, intent(in) :: nprofiles ! number of bkg profiles +real(kind_real), dimension(nprofiles) :: bkg_wavelengths ! array of bkg wavelengths +real(kind_real) :: obs_wavelength ! observed wavelength + +character(len=maxvarlen) :: err_msg +integer :: j + j = 1 + do while(j < nprofiles) + if(obs_wavelength .GE. bkg_wavelengths(j) .and.& + obs_wavelength .LT. bkg_wavelengths(j+1)) then + if (bracket == 1) then + b_channel = j + else if (bracket == 2) then + b_channel = j + 1 + else + write(err_msg,*) "ufo_aodext_mod: function b_channel: bracket index should be 1 (lower) or 2 (upper)" + call abor1_ftn(err_msg) + endif + j = j+ 1 + else if(obs_wavelength .GT. bkg_wavelengths(j) .and.& + obs_wavelength .LE. bkg_wavelengths(j+1)) then + if (bracket == 1) then + b_channel = j + else if (bracket == 2) then + b_channel = j + 1 + else + write(err_msg,*) "ufo_aodext_mod: function b_channel: bracket index should be 1 (lower) or 2 (upper)" + call abor1_ftn(err_msg) + endif + j = j +1 + endif + j = j + 1 + enddo + return +end function b_channel +!----------------------------------- +subroutine ufo_aodext_tlad_setup(self, f_conf) +use fckit_configuration_module, only: fckit_configuration +implicit none +class(ufo_aodext_tlad), intent(inout) :: self +type(fckit_configuration), intent(in) :: f_conf + +!Locals +integer n +character(len=maxvarlen) :: err_msg + + ! setup input variables varin (updated model variables) + call f_conf%get_or_die("nprofiles", self%nprofiles) + + if (self%nprofiles < 2 .or. self%nprofiles > 3) then + write(err_msg,*) 'ufo_aodext_tlad_setup: number of extinction profiles must be 2 or 3' + call abor1_ftn(err_msg) + endif + + do n = 1, self%nprofiles + call self%geovars%push_back(extdefault(n)) + enddo + + ! Wavelengths for bkg extinction profiles, specified in yaml + allocate(self%wavelength(self%nprofiles)) + call f_conf%get_or_die("bkg_wavelengths", self%wavelength) + !Check that the wavelengths are in an ascending order + n = 1 + do while (n < self%nprofiles) + if(self%wavelength(n) > self%wavelength(n+1)) then + write(err_msg,*) ' ufo_aodext_tlad_setup: bkg wavelengths should be in an ascending order' + call abor1_ftn(err_msg) + endif + n = n + 1 + enddo +end subroutine ufo_aodext_tlad_setup + +! ------------------------------------------------------------------------------ +subroutine destructor(self) +implicit none +type(ufo_aodext_tlad), intent(inout) :: self + + if (allocated(self%airdens)) deallocate(self%airdens) + if (allocated(self%delp)) deallocate(self%delp) + if (allocated(self%ext)) deallocate(self%ext) + if (allocated(self%obss_wavelength)) deallocate(self%obss_wavelength) + if (allocated(self%wavelength)) deallocate(self%wavelength) + +end subroutine destructor + +! ------------------------------------------------------------------------------ +subroutine ufo_aodext_tlad_settraj(self, geovals, obss) +use iso_c_binding +use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var +use obsspace_mod +implicit none +class(ufo_aodext_tlad), intent(inout) :: self +type(ufo_geovals), intent(in) :: geovals +type(c_ptr), value, intent(in) :: obss + +!locals + character(len=MAXVARLEN) :: geovar + integer :: nch, nvars, nlocs + +type(ufo_geoval), pointer :: ext_profile +type(ufo_geoval), pointer :: delp_profile +type(ufo_geoval), pointer :: airdens_profile + + ! Get number of locations + nlocs = obsspace_get_nlocs(obss) + + ! Get the number of obs type, for AOD it is the number of wavelengths + nvars = self%obsvars%nvars() + + ! Get airdens, delp, ext and number of layers from geovals + call ufo_geovals_get_var(geovals, var_delp, delp_profile) + self%nlayers = delp_profile%nval ! number of model layers + + allocate(self%delp(self%nlayers,nlocs)) + self%delp = delp_profile%vals + + call ufo_geovals_get_var(geovals, var_airdens, airdens_profile) + allocate(self%airdens(self%nlayers,nlocs)) + self%airdens = airdens_profile%vals + + allocate(self%ext(self%nlayers, nlocs, self%nprofiles)) + do nch = 1, self%nprofiles + geovar = self%geovars%variable(nch) + call ufo_geovals_get_var(geovals, geovar, ext_profile) + self%ext(:,:,nch) = ext_profile%vals + enddo + + ! Get some metadata from obsspace, observed AOD wavelengths + ! ----------------------- + allocate(self%obss_wavelength(nvars)) + call obsspace_get_db(obss,"VarMetaData", "obs_wavelength", self%obss_wavelength) + +end subroutine ufo_aodext_tlad_settraj + +! ------------------------------------------------------------------------------ +! Input geovals parameter represents dx for tangent linear model +subroutine ufo_aodext_simobs_tl(self, geovals, obss, nvars, nlocs, hofx) +use iso_c_binding +use kinds +use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var +use obsspace_mod +use ufo_constants_mod, only: grav, zero + +implicit none +class(ufo_aodext_tlad), intent(in) :: self +type(ufo_geovals), intent(in) :: geovals +integer, intent(in) :: nvars, nlocs +real(c_double), intent(inout) :: hofx(nvars, nlocs) +type(c_ptr), value, intent(in) :: obss + +!locals +real(kind_real), dimension(:,:,:), allocatable :: ext_tl !(km, nlocs, nch) extinction profiles perturbations +real(kind_real), dimension(:,:), allocatable :: aod_bkg !(nlocs, nch) AOD computed from modeled ext profiles +real(kind_real), dimension(:,:), allocatable :: aod_bkg_tl !(nlocs, nch) AOD tangent linear +real(kind_real), dimension(:,:), allocatable :: angstrom !(nvars, nlocs) Angstrom coefficient calculated from bkg AOD and wavelength +real(kind_real), dimension(:,:), allocatable :: angstrom_tl !(nvars, nlocs) Angstrom coefficient tangent linear +real(kind_real), dimension(:,:), allocatable :: logm !(nvars, nlocs) denominator of Angstrom parameter + +real(kind_real) :: arg1, arg1_tl, tmp, coef, coef_tl + +integer :: nch, nobs, ic, i, j, km + +type(ufo_geoval), pointer :: ext_profile +character(len=MAXVARLEN) :: geovar +real(c_double) :: missing + + ! Get extinction profile perturbations interpolated at obs loc + allocate(ext_tl(self%nlayers, nlocs, self%nprofiles)) + + do nch = 1, self%nprofiles + geovar = self%geovars%variable(nch) + call ufo_geovals_get_var(geovals, geovar, ext_profile) + ext_tl(:,:,nch) = ext_profile%vals + enddo + + allocate(aod_bkg(nlocs, self%nprofiles)) + allocate(aod_bkg_tl(nlocs, self%nprofiles)) + aod_bkg = zero + aod_bkg_tl = zero + + do nch = 1, self%nprofiles + do nobs = 1, nlocs + do km = 1, self%nlayers + + aod_bkg(nobs,nch) = aod_bkg(nobs, nch) + (self%ext(km,nobs,nch) * self%delp(km,nobs)& + * 1./(self%airdens(km,nobs) * grav*1000.0_kind_real)) + aod_bkg_tl(nobs,nch) = aod_bkg_tl(nobs, nch) + (ext_tl(km,nobs,nch) * self%delp(km,nobs)& + * 1./(self%airdens(km,nobs) * grav*1000.0_kind_real)) + + enddo + enddo + enddo + + allocate(angstrom_tl(nvars,nlocs)) + allocate(angstrom(nvars,nlocs)) + allocate(logm(nvars,nlocs)) + + missing =missing_value(missing) + hofx = zero + angstrom_tl = zero + + do nobs = 1, nlocs + do ic = 1, nvars + + if(self%obss_wavelength(ic) < self%wavelength(1) .or.& + self%obss_wavelength(ic) > self%wavelength(self%nprofiles)) then + hofx(ic, nobs) = missing + else + i = b_channel(1, self%nprofiles, self%wavelength, self%obss_wavelength(ic)) + j = b_channel(2, self%nprofiles, self%wavelength, self%obss_wavelength(ic)) + + logm(ic, nobs) = log(self%wavelength(i)/self%wavelength(j)) + tmp = aod_bkg(nobs, i) / aod_bkg(nobs, j) + arg1_tl = (aod_bkg_tl(nobs, i)-tmp*aod_bkg_tl(nobs,j))/aod_bkg(nobs,j) + arg1 = tmp + + angstrom_tl(ic, nobs) = arg1_tl/(logm(ic, nobs) * arg1) + angstrom(ic, nobs) = log(arg1)/logm(ic,nobs) + + coef = (self%wavelength(i)/self%wavelength(j))**angstrom(ic, nobs) + coef_tl = coef * log(self%wavelength(i)/self%wavelength(j))*angstrom_tl(ic,nobs) + hofx(ic, nobs) = coef * aod_bkg_tl(nobs, i) + aod_bkg(nobs, i)* coef_tl + endif + enddo + enddo + deallocate( angstrom_tl, angstrom, aod_bkg, aod_bkg_tl, ext_tl, logm) + +end subroutine ufo_aodext_simobs_tl + +! ------------------------------------------------------------------------------ +subroutine ufo_aodext_simobs_ad(self, geovals, obss, nvars, nlocs, hofx) +use kinds +use iso_c_binding +use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var +use obsspace_mod +use ufo_constants_mod, only: grav, zero + +implicit none +class(ufo_aodext_tlad), intent(in) :: self +type(ufo_geovals), intent(inout) :: geovals +integer, intent(in) :: nvars, nlocs +real(c_double), intent(in) :: hofx(nvars, nlocs) +type(c_ptr), value, intent(in) :: obss + + +real(kind_real), dimension(:,:,:), allocatable :: ext_ad !(km, nlocs, nch) Adjoint of ext profiles at obs loc +real(kind_real), dimension(:,:), allocatable :: aod_bkg !(nlocs, nch) AOD computed from modeled ext profiles +real(kind_real), dimension(:,:), allocatable :: aod_bkg_ad !(nlocs, nch) Adjoint of AOD +real(kind_real), dimension(:,:), allocatable :: angstrom !(nvars, nlocs) Angstrom coefficient +real(kind_real), dimension(:,:), allocatable :: angstrom_ad!(nvars, nlocs) Adjoint of Angstrom coefficient +real(kind_real), dimension(:,:), allocatable :: logm !(nvars, nlocs) Denominator of Angstrom coeff + +real(kind_real) :: arg1, arg1_ad, tmp, tmp_ad, coef, coef_ad + +integer :: nch, nobs, km, ic, i, j + +character(len=MAXVARLEN) :: geovar +type(ufo_geoval), pointer :: ext_profile +real(c_double) :: missing + + allocate(aod_bkg(nlocs, self%nprofiles)) + aod_bkg = zero + + do nch = 1, self%nprofiles + do nobs = 1, nlocs + do km = 1, self%nlayers + + aod_bkg(nobs,nch) = aod_bkg(nobs, nch) + (self%ext(km,nobs,nch) * self%delp(km,nobs) & + * 1./(self%airdens(km,nobs)*grav*1000.0_kind_real)) + + enddo + enddo + enddo + + missing = missing_value(missing) + allocate(angstrom(nvars,nlocs)) + allocate(logm(nvars,nlocs)) + do nobs = 1, nlocs + do ic = 1, nvars + + if(self%obss_wavelength(ic) < self%wavelength(1) .or.& + self%obss_wavelength(ic) > self%wavelength(self%nprofiles)) then + angstrom(ic, nobs) = missing + else + + i = b_channel(1, self%nprofiles, self%wavelength, self%obss_wavelength(ic)) + j = b_channel(2, self%nprofiles, self%wavelength, self%obss_wavelength(ic)) + + logm(ic, nobs) = log(self%wavelength(i)/self%wavelength(j)) + + angstrom(ic, nobs) = log(aod_bkg(nobs,i)/aod_bkg(nobs,j))/logm(ic, nobs) + endif + enddo + enddo + + allocate(aod_bkg_ad(nlocs, self%nprofiles)) + allocate(angstrom_ad(nvars,nlocs)) + aod_bkg_ad = zero + angstrom_ad = zero + + do nobs = nlocs, 1, -1 + do ic = nvars, 1, -1 + + if( hofx(ic,nobs)/=missing.and.angstrom(ic,nobs)/=missing )then + + i = b_channel(1, self%nprofiles, self%wavelength, self%obss_wavelength(ic)) + j = b_channel(2, self%nprofiles, self%wavelength, self%obss_wavelength(ic)) + + coef = (self%wavelength(i)/self%wavelength(j))**angstrom(ic, nobs) + aod_bkg_ad(nobs, i) = aod_bkg_ad(nobs, i) + coef * hofx(ic, nobs) + + angstrom_ad(ic, nobs) = angstrom_ad(ic,nobs) + coef * log(self%wavelength(i)/self%wavelength(j)) & + * aod_bkg(nobs, i) * hofx(ic, nobs) + + j = b_channel(2, self%nprofiles, self%wavelength, self%obss_wavelength(ic)) + + tmp = aod_bkg(nobs, i) / aod_bkg(nobs, j) + tmp_ad = angstrom_ad(ic, nobs)/(aod_bkg(nobs,j)*tmp*logm(ic, nobs)) + angstrom_ad = zero + aod_bkg_ad(nobs, i) = aod_bkg_ad(nobs,i) + tmp_ad + aod_bkg_ad(nobs, j) = aod_bkg_ad(nobs,j) - tmp * tmp_ad + endif + enddo + enddo + + allocate(ext_ad(self%nlayers, nlocs, self%nprofiles)) + ext_ad = zero + + do nch = self%nprofiles, 1, -1 + do nobs = nlocs, 1, -1 + do km = self%nlayers, 1, -1 + ext_ad(km, nobs, nch) = ext_ad(km, nobs, nch) + self%delp(km, nobs) & + * aod_bkg_ad(nobs, nch) * 1./(self%airdens(km, nobs) * grav * 1000.) + enddo + enddo + enddo + + !Get pointer to ext profiles in geovals and put adjoint of extinction profiles into geovals + do nch = self%nprofiles, 1, -1 + + geovar = self%geovars%variable(nch) + call ufo_geovals_get_var(geovals, geovar, ext_profile) + if(.not. allocated(ext_profile%vals)) then + ext_profile%nlocs = nlocs + ext_profile%nval = self%nlayers + allocate(ext_profile%vals(ext_profile%nval, ext_profile%nlocs)) + ext_profile%vals(:,:) = zero + endif + ext_profile%vals(:,:) = ext_ad(:,:,nch) + + enddo + deallocate(angstrom_ad, angstrom, aod_bkg_ad, aod_bkg, ext_ad) + +end subroutine ufo_aodext_simobs_ad + +! ------------------------------------------------------------------------------ + +end module ufo_aodext_tlad_mod diff --git a/src/ufo/aerosols/CMakeLists.txt b/src/ufo/aerosols/CMakeLists.txt new file mode 100644 index 000000000..176a4145e --- /dev/null +++ b/src/ufo/aerosols/CMakeLists.txt @@ -0,0 +1,24 @@ +# (C) Copyright 2019 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + + +add_subdirectory( AOP ) + +if( ${geos-aero_FOUND} ) + add_subdirectory( MR ) +endif( ${geos-aero_FOUND} ) + +PREPEND( _p_aodext_files "aerosols/AOP" ${aodext_src_files} ) + +if( ${geos-aero_FOUND} ) + PREPEND( _p_aodgeos_files "aerosols/MR" ${aodgeos_src_files} ) +endif( ${geos-aero_FOUND} ) + +set ( aerosols_src_files + ${_p_aodext_files} + ${_p_aodgeos_files} + PARENT_SCOPE +) + diff --git a/src/ufo/aerosols/MR/CMakeLists.txt b/src/ufo/aerosols/MR/CMakeLists.txt new file mode 100644 index 000000000..bbba151d6 --- /dev/null +++ b/src/ufo/aerosols/MR/CMakeLists.txt @@ -0,0 +1,18 @@ +# (C) Copyright 2019 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( aodgeos_src_files + ObsAodGeos.h + ObsAodGeos.cc + ObsAodGeosTLAD.h + ObsAodGeosTLAD.cc + ObsAodGeos.interface.F90 + ObsAodGeos.interface.h + ObsAodGeosTLAD.interface.F90 + ObsAodGeosTLAD.interface.h + ufo_aodgeos_mod.F90 + ufo_aodgeos_tlad_mod.F90 +PARENT_SCOPE +) diff --git a/src/ufo/geos_aero/ObsGeosAod.cc b/src/ufo/aerosols/MR/ObsAodGeos.cc similarity index 63% rename from src/ufo/geos_aero/ObsGeosAod.cc rename to src/ufo/aerosols/MR/ObsAodGeos.cc index c45be1536..2bbb95a20 100644 --- a/src/ufo/geos_aero/ObsGeosAod.cc +++ b/src/ufo/aerosols/MR/ObsAodGeos.cc @@ -5,7 +5,7 @@ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#include "ufo/geos_aero/ObsGeosAod.h" +#include "ufo/aerosols/MR/ObsAodGeos.h" #include #include @@ -24,37 +24,37 @@ namespace ufo { // ----------------------------------------------------------------------------- -static ObsOperatorMaker makerGeosAod_("GeosAod"); +static ObsOperatorMaker makerAodGeos_("AodGeos"); // ----------------------------------------------------------------------------- -ObsGeosAod::ObsGeosAod(const ioda::ObsSpace & odb, +ObsAodGeos::ObsAodGeos(const ioda::ObsSpace & odb, const eckit::Configuration & config) : ObsOperatorBase(odb, config), keyOper_(0), odb_(odb), varin_() { - ufo_geosaod_setup_f90(keyOper_, config, odb.obsvariables(), varin_); + ufo_aodgeos_setup_f90(keyOper_, config, odb.obsvariables(), varin_); - oops::Log::trace() << "ObsGeosAod created." << std::endl; + oops::Log::trace() << "ObsAodGeos created." << std::endl; } // ----------------------------------------------------------------------------- -ObsGeosAod::~ObsGeosAod() { - ufo_geosaod_delete_f90(keyOper_); - oops::Log::trace() << "ObsGeosAod destructed" << std::endl; +ObsAodGeos::~ObsAodGeos() { + ufo_aodgeos_delete_f90(keyOper_); + oops::Log::trace() << "ObsAodGeos destructed" << std::endl; } // ----------------------------------------------------------------------------- -void ObsGeosAod::simulateObs(const GeoVaLs & gv, ioda::ObsVector & ovec, ObsDiagnostics &) const { - ufo_geosaod_simobs_f90(keyOper_, gv.toFortran(), odb_, ovec.nvars(), ovec.nlocs(), +void ObsAodGeos::simulateObs(const GeoVaLs & gv, ioda::ObsVector & ovec, ObsDiagnostics &) const { + ufo_aodgeos_simobs_f90(keyOper_, gv.toFortran(), odb_, ovec.nvars(), ovec.nlocs(), ovec.toFortran()); - oops::Log::trace() << "ObsGeosAod: observation operator run" << std::endl; + oops::Log::trace() << "ObsAodGeos: observation operator run" << std::endl; } // ----------------------------------------------------------------------------- -void ObsGeosAod::print(std::ostream & os) const { - os << "ObsGeosAod::print not implemented"; +void ObsAodGeos::print(std::ostream & os) const { + os << "ObsAodGeos::print not implemented"; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/geos_aero/ObsGeosAod.h b/src/ufo/aerosols/MR/ObsAodGeos.h similarity index 71% rename from src/ufo/geos_aero/ObsGeosAod.h rename to src/ufo/aerosols/MR/ObsAodGeos.h index 1392174dc..c6eba6e0e 100644 --- a/src/ufo/geos_aero/ObsGeosAod.h +++ b/src/ufo/aerosols/MR/ObsAodGeos.h @@ -5,8 +5,8 @@ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#ifndef UFO_GEOS_AERO_OBSGEOSAOD_H_ -#define UFO_GEOS_AERO_OBSGEOSAOD_H_ +#ifndef UFO_AEROSOLS_MR_OBSAODGEOS_H_ +#define UFO_AEROSOLS_MR_OBSAODGEOS_H_ #include #include @@ -17,7 +17,7 @@ #include "oops/util/ObjectCounter.h" -#include "ufo/geos_aero/ObsGeosAod.interface.h" +#include "ufo/aerosols/MR/ObsAodGeos.interface.h" #include "ufo/ObsOperatorBase.h" /// Forward declarations @@ -35,14 +35,14 @@ namespace ufo { class ObsDiagnostics; // ----------------------------------------------------------------------------- -/// GeosAod observation operator class -class ObsGeosAod : public ObsOperatorBase, - private util::ObjectCounter { +/// AodGeos observation operator class +class ObsAodGeos : public ObsOperatorBase, + private util::ObjectCounter { public: - static const std::string classname() {return "ufo::ObsGeosAod";} + static const std::string classname() {return "ufo::ObsAodGeos";} - ObsGeosAod(const ioda::ObsSpace &, const eckit::Configuration &); - virtual ~ObsGeosAod(); + ObsAodGeos(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsAodGeos(); // Obs Operator void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; @@ -62,4 +62,4 @@ class ObsGeosAod : public ObsOperatorBase, // ----------------------------------------------------------------------------- } // namespace ufo -#endif // UFO_GEOS_AERO_OBSGEOSAOD_H_ +#endif // UFO_AEROSOLS_MR_OBSAODGEOS_H_ diff --git a/src/ufo/geos_aero/ObsGeosAod.interface.F90 b/src/ufo/aerosols/MR/ObsAodGeos.interface.F90 similarity index 71% rename from src/ufo/geos_aero/ObsGeosAod.interface.F90 rename to src/ufo/aerosols/MR/ObsAodGeos.interface.F90 index 4ad261bb3..8bd735ddc 100644 --- a/src/ufo/geos_aero/ObsGeosAod.interface.F90 +++ b/src/ufo/aerosols/MR/ObsAodGeos.interface.F90 @@ -3,26 +3,26 @@ ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -!> Fortran geosaod module for functions on the interface between C++ and Fortran +!> Fortran aodgeos module for functions on the interface between C++ and Fortran ! to handle observation operators -module ufo_geosaod_mod_c +module ufo_aodgeos_mod_c use iso_c_binding use fckit_configuration_module, only: fckit_configuration - use ufo_geosaod_mod + use ufo_aodgeos_mod use ufo_geovals_mod, only: ufo_geovals use ufo_geovals_mod_c, only: ufo_geovals_registry implicit none private ! ------------------------------------------------------------------------------ -#define LISTED_TYPE ufo_geosaod +#define LISTED_TYPE ufo_aodgeos !> Linked list interface - defines registry_t type #include "oops/util/linkedList_i.f" !> Global registry - type(registry_t) :: ufo_geosaod_registry + type(registry_t) :: ufo_aodgeos_registry ! ------------------------------------------------------------------------------ @@ -34,19 +34,19 @@ module ufo_geosaod_mod_c ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_geosaod_setup_f90') +subroutine ufo_aodgeos_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_aodgeos_setup_f90') use oops_variables_mod use ufo_vars_mod implicit none integer(c_int), intent(inout) :: c_key_self type(c_ptr), intent(in), value :: c_conf -type(ufo_geosaod), pointer :: self +type(ufo_aodgeos), pointer :: self type(c_ptr), intent(in), value :: c_obsvars !< variables to be simulated type(c_ptr), intent(in), value :: c_geovars !< variables requested from the model type(fckit_configuration) :: f_conf -call ufo_geosaod_registry%setup(c_key_self, self) +call ufo_aodgeos_registry%setup(c_key_self, self) f_conf = fckit_configuration(c_conf) self%obsvars = oops_variables(c_obsvars) @@ -54,24 +54,24 @@ subroutine ufo_geosaod_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c, call self%setup(f_conf) -end subroutine ufo_geosaod_setup_c +end subroutine ufo_aodgeos_setup_c ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_delete_c(c_key_self) bind(c,name='ufo_geosaod_delete_f90') +subroutine ufo_aodgeos_delete_c(c_key_self) bind(c,name='ufo_aodgeos_delete_f90') implicit none integer(c_int), intent(inout) :: c_key_self -type(ufo_geosaod), pointer :: self +type(ufo_aodgeos), pointer :: self -call ufo_geosaod_registry%delete(c_key_self, self) +call ufo_aodgeos_registry%delete(c_key_self, self) -end subroutine ufo_geosaod_delete_c +end subroutine ufo_aodgeos_delete_c ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_simobs_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, & - c_hofx) bind(c,name='ufo_geosaod_simobs_f90') +subroutine ufo_aodgeos_simobs_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, & + c_hofx) bind(c,name='ufo_aodgeos_simobs_f90') implicit none integer(c_int), intent(in) :: c_key_self @@ -80,15 +80,15 @@ subroutine ufo_geosaod_simobs_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, integer(c_int), intent(in) :: c_nvars, c_nlocs real(c_double), intent(inout) :: c_hofx(c_nvars, c_nlocs) -type(ufo_geosaod), pointer :: self +type(ufo_aodgeos), pointer :: self type(ufo_geovals), pointer :: geovals -call ufo_geosaod_registry%get(c_key_self, self) +call ufo_aodgeos_registry%get(c_key_self, self) call ufo_geovals_registry%get(c_key_geovals, geovals) call self%simobs(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) -end subroutine ufo_geosaod_simobs_c +end subroutine ufo_aodgeos_simobs_c ! ------------------------------------------------------------------------------ -end module ufo_geosaod_mod_c +end module ufo_aodgeos_mod_c diff --git a/src/ufo/geos_aero/ObsGeosAod.interface.h b/src/ufo/aerosols/MR/ObsAodGeos.interface.h similarity index 65% rename from src/ufo/geos_aero/ObsGeosAod.interface.h rename to src/ufo/aerosols/MR/ObsAodGeos.interface.h index cff5e7ac8..158d2605a 100644 --- a/src/ufo/geos_aero/ObsGeosAod.interface.h +++ b/src/ufo/aerosols/MR/ObsAodGeos.interface.h @@ -5,8 +5,8 @@ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#ifndef UFO_GEOS_AERO_OBSGEOSAOD_INTERFACE_H_ -#define UFO_GEOS_AERO_OBSGEOSAOD_INTERFACE_H_ +#ifndef UFO_AEROSOLS_MR_OBSAODGEOS_INTERFACE_H_ +#define UFO_AEROSOLS_MR_OBSAODGEOS_INTERFACE_H_ #include "ioda/ObsSpace.h" #include "oops/base/Variables.h" @@ -14,16 +14,16 @@ namespace ufo { -/// Interface to Fortran UFO geosaod routines +/// Interface to Fortran UFO aodgeos routines extern "C" { // ----------------------------------------------------------------------------- - void ufo_geosaod_setup_f90(F90hop &, const eckit::Configuration &, + void ufo_aodgeos_setup_f90(F90hop &, const eckit::Configuration &, const oops::Variables &, oops::Variables &); - void ufo_geosaod_delete_f90(F90hop &); - void ufo_geosaod_simobs_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + void ufo_aodgeos_delete_f90(F90hop &); + void ufo_aodgeos_simobs_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, const int &, const int &, double &); // ----------------------------------------------------------------------------- @@ -31,4 +31,4 @@ extern "C" { } // extern C } // namespace ufo -#endif // UFO_GEOS_AERO_OBSGEOSAOD_INTERFACE_H_ +#endif // UFO_AEROSOLS_MR_OBSAODGEOS_INTERFACE_H_ diff --git a/src/ufo/geos_aero/ObsGeosAodTLAD.cc b/src/ufo/aerosols/MR/ObsAodGeosTLAD.cc similarity index 54% rename from src/ufo/geos_aero/ObsGeosAodTLAD.cc rename to src/ufo/aerosols/MR/ObsAodGeosTLAD.cc index 591e920ff..479060025 100644 --- a/src/ufo/geos_aero/ObsGeosAodTLAD.cc +++ b/src/ufo/aerosols/MR/ObsAodGeosTLAD.cc @@ -5,7 +5,7 @@ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#include "ufo/geos_aero/ObsGeosAodTLAD.h" +#include "ufo/aerosols/MR/ObsAodGeosTLAD.h" #include @@ -20,54 +20,54 @@ namespace ufo { // ----------------------------------------------------------------------------- -static LinearObsOperatorMaker makerGeosAodTL_("GeosAod"); +static LinearObsOperatorMaker makerAodGeosTL_("AodGeos"); // ----------------------------------------------------------------------------- -ObsGeosAodTLAD::ObsGeosAodTLAD(const ioda::ObsSpace & odb, +ObsAodGeosTLAD::ObsAodGeosTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOper_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOper_(0), varin_() { - ufo_geosaod_tlad_setup_f90(keyOper_, config, odb.obsvariables(), varin_); + ufo_aodgeos_tlad_setup_f90(keyOper_, config, odb.obsvariables(), varin_); - oops::Log::trace() << "ObsGeosAodTLAD created" << std::endl; + oops::Log::trace() << "ObsAodGeosTLAD created" << std::endl; } // ----------------------------------------------------------------------------- -ObsGeosAodTLAD::~ObsGeosAodTLAD() { - ufo_geosaod_tlad_delete_f90(keyOper_); - oops::Log::trace() << "ObsGeosAodTLAD destructed" << std::endl; +ObsAodGeosTLAD::~ObsAodGeosTLAD() { + ufo_aodgeos_tlad_delete_f90(keyOper_); + oops::Log::trace() << "ObsAodGeosTLAD destructed" << std::endl; } // ----------------------------------------------------------------------------- -void ObsGeosAodTLAD::setTrajectory(const GeoVaLs & geovals, +void ObsAodGeosTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - oops::Log::trace() << "ObsGeosAodTLAD: trajectory entering" << std::endl; - ufo_geosaod_tlad_settraj_f90(keyOper_, geovals.toFortran(), odb_); - oops::Log::trace() << "ObsGeosAodTLAD: set trajectory exiting" << std::endl; + oops::Log::trace() << "ObsAodGeosTLAD: trajectory entering" << std::endl; + ufo_aodgeos_tlad_settraj_f90(keyOper_, geovals.toFortran(), obsspace()); + oops::Log::trace() << "ObsAodGeosTLAD: set trajectory exiting" << std::endl; } // ----------------------------------------------------------------------------- -void ObsGeosAodTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_geosaod_simobs_tl_f90(keyOper_, geovals.toFortran(), odb_, +void ObsAodGeosTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { + ufo_aodgeos_simobs_tl_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); - oops::Log::trace() << "ObsGeosAodTLAD: TL observation operator run" << std::endl; + oops::Log::trace() << "ObsAodGeosTLAD: TL observation operator run" << std::endl; } // ----------------------------------------------------------------------------- -void ObsGeosAodTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_geosaod_simobs_ad_f90(keyOper_, geovals.toFortran(), odb_, +void ObsAodGeosTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { + ufo_aodgeos_simobs_ad_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); - oops::Log::trace() << "ObsGeosAodTLAD: adjoint observation operator run" << std::endl; + oops::Log::trace() << "ObsAodGeosTLAD: adjoint observation operator run" << std::endl; } // ----------------------------------------------------------------------------- -void ObsGeosAodTLAD::print(std::ostream & os) const { - os << "ObsGeosAodTLAD::print not implemented" << std::endl; +void ObsAodGeosTLAD::print(std::ostream & os) const { + os << "ObsAodGeosTLAD::print not implemented" << std::endl; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/geos_aero/ObsGeosAodTLAD.h b/src/ufo/aerosols/MR/ObsAodGeosTLAD.h similarity index 71% rename from src/ufo/geos_aero/ObsGeosAodTLAD.h rename to src/ufo/aerosols/MR/ObsAodGeosTLAD.h index 2f8097b44..acf840258 100644 --- a/src/ufo/geos_aero/ObsGeosAodTLAD.h +++ b/src/ufo/aerosols/MR/ObsAodGeosTLAD.h @@ -5,8 +5,8 @@ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#ifndef UFO_GEOS_AERO_OBSGEOSAODTLAD_H_ -#define UFO_GEOS_AERO_OBSGEOSAODTLAD_H_ +#ifndef UFO_AEROSOLS_MR_OBSAODGEOSTLAD_H_ +#define UFO_AEROSOLS_MR_OBSAODGEOSTLAD_H_ #include #include @@ -14,7 +14,7 @@ #include "oops/base/Variables.h" #include "oops/util/ObjectCounter.h" -#include "ufo/geos_aero/ObsGeosAodTLAD.interface.h" +#include "ufo/aerosols/MR/ObsAodGeosTLAD.interface.h" #include "ufo/LinearObsOperatorBase.h" // Forward declarations @@ -33,14 +33,14 @@ namespace ufo { class ObsBiasIncrement; // ----------------------------------------------------------------------------- -/// GeosAod TL/AD observation operator class -class ObsGeosAodTLAD : public LinearObsOperatorBase, - private util::ObjectCounter { +/// AodGeos TL/AD observation operator class +class ObsAodGeosTLAD : public LinearObsOperatorBase, + private util::ObjectCounter { public: - static const std::string classname() {return "ufo::ObsGeosAodTLAD";} + static const std::string classname() {return "ufo::ObsAodGeosTLAD";} - ObsGeosAodTLAD(const ioda::ObsSpace &, const eckit::Configuration &); - virtual ~ObsGeosAodTLAD(); + ObsAodGeosTLAD(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsAodGeosTLAD(); // Obs Operators void setTrajectory(const GeoVaLs &, const ObsBias &, ObsDiagnostics &) override; @@ -56,11 +56,10 @@ class ObsGeosAodTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOper_; - const ioda::ObsSpace& odb_; oops::Variables varin_; }; // ----------------------------------------------------------------------------- } // namespace ufo -#endif // UFO_GEOS_AERO_OBSGEOSAODTLAD_H_ +#endif // UFO_AEROSOLS_MR_OBSAODGEOSTLAD_H_ diff --git a/src/ufo/geos_aero/ObsGeosAodTLAD.interface.F90 b/src/ufo/aerosols/MR/ObsAodGeosTLAD.interface.F90 similarity index 65% rename from src/ufo/geos_aero/ObsGeosAodTLAD.interface.F90 rename to src/ufo/aerosols/MR/ObsAodGeosTLAD.interface.F90 index 00a476914..4d710d51f 100644 --- a/src/ufo/geos_aero/ObsGeosAodTLAD.interface.F90 +++ b/src/ufo/aerosols/MR/ObsAodGeosTLAD.interface.F90 @@ -3,27 +3,27 @@ ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -!> Fortran geosaod module for functions on the interface between C++ and Fortran +!> Fortran aodgeos module for functions on the interface between C++ and Fortran ! to handle tl/ad observation operators -module ufo_geosaod_tlad_mod_c +module ufo_aodgeos_tlad_mod_c use iso_c_binding use fckit_configuration_module, only: fckit_configuration - use ufo_geosaod_tlad_mod + use ufo_aodgeos_tlad_mod use ufo_geovals_mod_c, only: ufo_geovals_registry use ufo_geovals_mod, only: ufo_geovals implicit none private -#define LISTED_TYPE ufo_geosaod_tlad +#define LISTED_TYPE ufo_aodgeos_tlad !> Linked list interface - defines registry_t type #include "oops/util/linkedList_i.f" !> Global registry - type(registry_t) :: ufo_geosaod_tlad_registry + type(registry_t) :: ufo_aodgeos_tlad_registry contains @@ -32,7 +32,7 @@ module ufo_geosaod_tlad_mod_c #include "oops/util/linkedList_c.f" ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_geosaod_tlad_setup_f90') +subroutine ufo_aodgeos_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_aodgeos_tlad_setup_f90') use oops_variables_mod implicit none @@ -41,11 +41,11 @@ subroutine ufo_geosaod_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bi type(c_ptr), value, intent(in) :: c_geovars ! variables requested from the model type(c_ptr), value, intent(in) :: c_obsvars ! variables to be simulated -type(ufo_geosaod_tlad), pointer :: self +type(ufo_aodgeos_tlad), pointer :: self type(fckit_configuration) :: f_conf -call ufo_geosaod_tlad_registry%setup(c_key_self, self) +call ufo_aodgeos_tlad_registry%setup(c_key_self, self) f_conf = fckit_configuration(c_conf) !> Set vars @@ -54,44 +54,44 @@ subroutine ufo_geosaod_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bi call self%setup(f_conf) -end subroutine ufo_geosaod_tlad_setup_c +end subroutine ufo_aodgeos_tlad_setup_c ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_tlad_delete_c(c_key_self) bind(c,name='ufo_geosaod_tlad_delete_f90') +subroutine ufo_aodgeos_tlad_delete_c(c_key_self) bind(c,name='ufo_aodgeos_tlad_delete_f90') implicit none integer(c_int), intent(inout) :: c_key_self -type(ufo_geosaod_tlad), pointer :: self +type(ufo_aodgeos_tlad), pointer :: self -call ufo_geosaod_tlad_registry%get(c_key_self, self) +call ufo_aodgeos_tlad_registry%get(c_key_self, self) call self%delete() -call ufo_geosaod_tlad_registry%remove(c_key_self) +call ufo_aodgeos_tlad_registry%remove(c_key_self) -end subroutine ufo_geosaod_tlad_delete_c +end subroutine ufo_aodgeos_tlad_delete_c ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_tlad_settraj_c(c_key_self, c_key_geovals, c_obsspace) bind(c,name='ufo_geosaod_tlad_settraj_f90') +subroutine ufo_aodgeos_tlad_settraj_c(c_key_self, c_key_geovals, c_obsspace) bind(c,name='ufo_aodgeos_tlad_settraj_f90') implicit none integer(c_int), intent(in) :: c_key_self integer(c_int), intent(in) :: c_key_geovals type(c_ptr), value, intent(in) :: c_obsspace -type(ufo_geosaod_tlad), pointer :: self +type(ufo_aodgeos_tlad), pointer :: self type(ufo_geovals), pointer :: geovals -call ufo_geosaod_tlad_registry%get(c_key_self, self) +call ufo_aodgeos_tlad_registry%get(c_key_self, self) call ufo_geovals_registry%get(c_key_geovals, geovals) call self%settraj(geovals, c_obsspace) -end subroutine ufo_geosaod_tlad_settraj_c +end subroutine ufo_aodgeos_tlad_settraj_c ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) bind(c,name='ufo_geosaod_simobs_tl_f90') +subroutine ufo_aodgeos_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) bind(c,name='ufo_aodgeos_simobs_tl_f90') implicit none integer(c_int), intent(in) :: c_key_self @@ -100,19 +100,19 @@ subroutine ufo_geosaod_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nvar integer(c_int), intent(in) :: c_nvars, c_nlocs real(c_double), intent(inout) :: c_hofx(c_nvars, c_nlocs) -type(ufo_geosaod_tlad), pointer :: self +type(ufo_aodgeos_tlad), pointer :: self type(ufo_geovals), pointer :: geovals -call ufo_geosaod_tlad_registry%get(c_key_self, self) +call ufo_aodgeos_tlad_registry%get(c_key_self, self) call ufo_geovals_registry%get(c_key_geovals, geovals) call self%simobs_tl(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) -end subroutine ufo_geosaod_simobs_tl_c +end subroutine ufo_aodgeos_simobs_tl_c ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_simobs_ad_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) bind(c,name='ufo_geosaod_simobs_ad_f90') +subroutine ufo_aodgeos_simobs_ad_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) bind(c,name='ufo_aodgeos_simobs_ad_f90') implicit none integer(c_int), intent(in) :: c_key_self @@ -121,16 +121,16 @@ subroutine ufo_geosaod_simobs_ad_c(c_key_self, c_key_geovals, c_obsspace, c_nvar integer(c_int), intent(in) :: c_nvars, c_nlocs real(c_double), intent(in) :: c_hofx(c_nvars, c_nlocs) -type(ufo_geosaod_tlad), pointer :: self +type(ufo_aodgeos_tlad), pointer :: self type(ufo_geovals), pointer :: geovals -call ufo_geosaod_tlad_registry%get(c_key_self, self) +call ufo_aodgeos_tlad_registry%get(c_key_self, self) call ufo_geovals_registry%get(c_key_geovals, geovals) call self%simobs_ad(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) -end subroutine ufo_geosaod_simobs_ad_c +end subroutine ufo_aodgeos_simobs_ad_c ! ------------------------------------------------------------------------------ -end module ufo_geosaod_tlad_mod_c +end module ufo_aodgeos_tlad_mod_c diff --git a/src/ufo/geos_aero/ObsGeosAodTLAD.interface.h b/src/ufo/aerosols/MR/ObsAodGeosTLAD.interface.h similarity index 62% rename from src/ufo/geos_aero/ObsGeosAodTLAD.interface.h rename to src/ufo/aerosols/MR/ObsAodGeosTLAD.interface.h index 228818cfd..ef64a1ab0 100644 --- a/src/ufo/geos_aero/ObsGeosAodTLAD.interface.h +++ b/src/ufo/aerosols/MR/ObsAodGeosTLAD.interface.h @@ -5,8 +5,8 @@ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#ifndef UFO_GEOS_AERO_OBSGEOSAODTLAD_INTERFACE_H_ -#define UFO_GEOS_AERO_OBSGEOSAODTLAD_INTERFACE_H_ +#ifndef UFO_AEROSOLS_MR_OBSAODGEOSTLAD_INTERFACE_H_ +#define UFO_AEROSOLS_MR_OBSAODGEOSTLAD_INTERFACE_H_ #include "ioda/ObsSpace.h" @@ -16,23 +16,23 @@ namespace ufo { -/// Interface to Fortran UFO geosaod routines +/// Interface to Fortran UFO aodgeos routines extern "C" { // ----------------------------------------------------------------------------- - void ufo_geosaod_tlad_setup_f90(F90hop &, const eckit::Configuration &, + void ufo_aodgeos_tlad_setup_f90(F90hop &, const eckit::Configuration &, const oops::Variables &, oops::Variables &); - void ufo_geosaod_tlad_delete_f90(F90hop &); - void ufo_geosaod_tlad_settraj_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &); - void ufo_geosaod_simobs_tl_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + void ufo_aodgeos_tlad_delete_f90(F90hop &); + void ufo_aodgeos_tlad_settraj_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &); + void ufo_aodgeos_simobs_tl_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, const int &, const int &, double &); - void ufo_geosaod_simobs_ad_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + void ufo_aodgeos_simobs_ad_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, const int &, const int &, const double &); // ----------------------------------------------------------------------------- } // extern C } // namespace ufo -#endif // UFO_GEOS_AERO_OBSGEOSAODTLAD_INTERFACE_H_ +#endif // UFO_AEROSOLS_MR_OBSAODGEOSTLAD_INTERFACE_H_ diff --git a/src/ufo/geos_aero/ufo_geosaod_mod.F90 b/src/ufo/aerosols/MR/ufo_aodgeos_mod.F90 similarity index 89% rename from src/ufo/geos_aero/ufo_geosaod_mod.F90 rename to src/ufo/aerosols/MR/ufo_aodgeos_mod.F90 index 1770d7e49..cb00ea1ee 100644 --- a/src/ufo/geos_aero/ufo_geosaod_mod.F90 +++ b/src/ufo/aerosols/MR/ufo_aodgeos_mod.F90 @@ -3,9 +3,9 @@ ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -!> Fortran module for geosaod observation operator +!> Fortran module for aodgeos observation operator -module ufo_geosaod_mod +module ufo_aodgeos_mod use iso_c_binding use kinds @@ -19,17 +19,17 @@ module ufo_geosaod_mod integer, parameter :: max_string=800 !> Fortran derived type for the observation type - type, public :: ufo_geosaod + type, public :: ufo_aodgeos type(oops_variables), public :: geovars type(oops_variables), public :: obsvars integer, public :: ntracers real(kind_real), public, allocatable :: wavelength(:) character(len=maxvarlen),public :: rcfile contains - procedure :: setup => ufo_geosaod_setup - procedure :: simobs => ufo_geosaod_simobs + procedure :: setup => ufo_aodgeos_setup + procedure :: simobs => ufo_aodgeos_simobs final :: destructor - end type ufo_geosaod + end type ufo_aodgeos !> Default variables required from model character(len=maxvarlen), dimension(2), parameter :: varindefault = (/var_delp, var_rh/) @@ -38,11 +38,11 @@ module ufo_geosaod_mod ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_setup(self, f_conf) +subroutine ufo_aodgeos_setup(self, f_conf) use fckit_configuration_module, only: fckit_configuration implicit none -class(ufo_geosaod), intent(inout) :: self +class(ufo_aodgeos), intent(inout) :: self type(fckit_configuration), intent(in) :: f_conf !Locals @@ -75,13 +75,13 @@ subroutine ufo_geosaod_setup(self, f_conf) self%rcfile = str deallocate(str) -end subroutine ufo_geosaod_setup +end subroutine ufo_aodgeos_setup ! ------------------------------------------------------------------------------ subroutine destructor(self) implicit none -type(ufo_geosaod), intent(inout) :: self +type(ufo_aodgeos), intent(inout) :: self if (allocated(self%wavelength)) deallocate(self%wavelength) @@ -89,14 +89,14 @@ end subroutine destructor ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_simobs(self, geovals, obss, nvars, nlocs, hofx) +subroutine ufo_aodgeos_simobs(self, geovals, obss, nvars, nlocs, hofx) use kinds use ufo_constants_mod, only: grav use ufo_geovals_mod use iso_c_binding implicit none -class(ufo_geosaod), intent(in) :: self +class(ufo_aodgeos), intent(in) :: self integer, intent(in) :: nvars, nlocs type(ufo_geovals), intent(in) :: geovals real(c_double), intent(inout) :: hofx(nvars, nlocs) @@ -150,8 +150,8 @@ subroutine ufo_geosaod_simobs(self, geovals, obss, nvars, nlocs, hofx) ! -------- deallocate(qm, rh, delp, tracer_name) -end subroutine ufo_geosaod_simobs +end subroutine ufo_aodgeos_simobs ! ------------------------------------------------------------------------------ -end module ufo_geosaod_mod +end module ufo_aodgeos_mod diff --git a/src/ufo/geos_aero/ufo_geosaod_tlad_mod.F90 b/src/ufo/aerosols/MR/ufo_aodgeos_tlad_mod.F90 similarity index 84% rename from src/ufo/geos_aero/ufo_geosaod_tlad_mod.F90 rename to src/ufo/aerosols/MR/ufo_aodgeos_tlad_mod.F90 index 3d9486b74..cb8df2ba3 100644 --- a/src/ufo/geos_aero/ufo_geosaod_tlad_mod.F90 +++ b/src/ufo/aerosols/MR/ufo_aodgeos_tlad_mod.F90 @@ -3,9 +3,9 @@ ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -!> Fortran module for geosaod tl/ad observation operator +!> Fortran module for aodgeos tl/ad observation operator -module ufo_geosaod_tlad_mod +module ufo_aodgeos_tlad_mod use iso_c_binding @@ -24,7 +24,7 @@ module ufo_geosaod_tlad_mod integer, parameter :: max_string=800 !> Fortran derived type for the tl/ad observation operator - type, public :: ufo_geosaod_tlad + type, public :: ufo_aodgeos_tlad integer :: nlocs, nlayers, ntracers, nvars type(oops_variables), public :: obsvars type(oops_variables), public :: geovars @@ -33,21 +33,21 @@ module ufo_geosaod_tlad_mod character(len=maxvarlen),public:: rcfile real(kind=kind_real), dimension(:,:), allocatable :: delp(:,:) contains - procedure :: setup => ufo_geosaod_tlad_setup - procedure :: delete => ufo_geosaod_tlad_delete - procedure :: settraj => ufo_geosaod_tlad_settraj - procedure :: simobs_tl => ufo_geosaod_simobs_tl - procedure :: simobs_ad => ufo_geosaod_simobs_ad - end type ufo_geosaod_tlad + procedure :: setup => ufo_aodgeos_tlad_setup + procedure :: delete => ufo_aodgeos_tlad_delete + procedure :: settraj => ufo_aodgeos_tlad_settraj + procedure :: simobs_tl => ufo_aodgeos_simobs_tl + procedure :: simobs_ad => ufo_aodgeos_simobs_ad + end type ufo_aodgeos_tlad contains ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_tlad_setup(self, f_conf) +subroutine ufo_aodgeos_tlad_setup(self, f_conf) use fckit_configuration_module, only: fckit_configuration implicit none -class(ufo_geosaod_tlad), intent(inout) :: self +class(ufo_aodgeos_tlad), intent(inout) :: self type(fckit_configuration), intent(in) :: f_conf !Locals @@ -74,25 +74,25 @@ subroutine ufo_geosaod_tlad_setup(self, f_conf) self%rcfile = str deallocate(str) -end subroutine ufo_geosaod_tlad_setup +end subroutine ufo_aodgeos_tlad_setup ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_tlad_delete(self) +subroutine ufo_aodgeos_tlad_delete(self) implicit none -class(ufo_geosaod_tlad), intent(inout) :: self +class(ufo_aodgeos_tlad), intent(inout) :: self if (allocated(self%bext)) deallocate(self%bext) if (allocated(self%delp)) deallocate(self%delp) if (allocated(self%wavelength)) deallocate(self%wavelength) -end subroutine ufo_geosaod_tlad_delete +end subroutine ufo_aodgeos_tlad_delete ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_tlad_settraj(self, geovals, obss) +subroutine ufo_aodgeos_tlad_settraj(self, geovals, obss) use obsspace_mod implicit none -class(ufo_geosaod_tlad), intent(inout) :: self +class(ufo_aodgeos_tlad), intent(inout) :: self type(ufo_geovals), intent(in) :: geovals type(c_ptr), value, intent(in) :: obss @@ -142,14 +142,14 @@ subroutine ufo_geosaod_tlad_settraj(self, geovals, obss) deallocate(self%wavelength) deallocate(tracer_name) -end subroutine ufo_geosaod_tlad_settraj +end subroutine ufo_aodgeos_tlad_settraj ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_simobs_tl(self, geovals, obss, nvars, nlocs, hofx) +subroutine ufo_aodgeos_simobs_tl(self, geovals, obss, nvars, nlocs, hofx) implicit none -class(ufo_geosaod_tlad), intent(in) :: self +class(ufo_aodgeos_tlad), intent(in) :: self type(ufo_geovals), intent(in) :: geovals type(c_ptr), value, intent(in) :: obss integer, intent(in) :: nvars, nlocs @@ -175,14 +175,14 @@ subroutine ufo_geosaod_simobs_tl(self, geovals, obss, nvars, nlocs, hofx) deallocate(qm_tl) -end subroutine ufo_geosaod_simobs_tl +end subroutine ufo_aodgeos_simobs_tl ! ------------------------------------------------------------------------------ -subroutine ufo_geosaod_simobs_ad(self, geovals, obss, nvars, nlocs, hofx) +subroutine ufo_aodgeos_simobs_ad(self, geovals, obss, nvars, nlocs, hofx) implicit none -class(ufo_geosaod_tlad), intent(in) :: self +class(ufo_aodgeos_tlad), intent(in) :: self type(ufo_geovals), intent(inout) :: geovals type(c_ptr), value, intent(in) :: obss integer, intent(in) :: nvars, nlocs @@ -215,9 +215,9 @@ subroutine ufo_geosaod_simobs_ad(self, geovals, obss, nvars, nlocs, hofx) deallocate(qm_ad) -end subroutine ufo_geosaod_simobs_ad +end subroutine ufo_aodgeos_simobs_ad ! ------------------------------------------------------------------------------ -end module ufo_geosaod_tlad_mod +end module ufo_aodgeos_tlad_mod diff --git a/src/ufo/atmsfcinterp/ufo_atmsfcinterp_mod.F90 b/src/ufo/atmsfcinterp/ufo_atmsfcinterp_mod.F90 index 81638fd86..6c852b153 100644 --- a/src/ufo/atmsfcinterp/ufo_atmsfcinterp_mod.F90 +++ b/src/ufo/atmsfcinterp/ufo_atmsfcinterp_mod.F90 @@ -78,6 +78,7 @@ subroutine atmsfcinterp_simobs_(self, geovals, obss, nvars, nlocs, hofx) use thermo_utils_mod, only: calc_theta, gsi_tp_to_qs use ufo_constants_mod, only: grav, rv, rd, rd_over_cp, von_karman use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var + use ufo_utils_mod, only: cmp_strings use obsspace_mod use iso_c_binding implicit none @@ -93,7 +94,7 @@ subroutine atmsfcinterp_simobs_(self, geovals, obss, nvars, nlocs, hofx) real(kind_real), allocatable :: obselev(:), obshgt(:) real(kind_real), parameter :: minroughlen = 1.0e-4_kind_real character(len=MAXVARLEN) :: geovar - real(kind_real) :: thv1, thv2, th1, thg, thvg, rib, V2 + real(kind_real) :: thv1, thv2, th1, thg, thvg, rib, V2, agl, zbot real(kind_real) :: gzsoz0, gzzoz0 real(kind_real) :: redfac, psim, psimz, psih, psihz real(kind_real) :: ttmp1, ttmpg, eg, qg @@ -157,16 +158,19 @@ subroutine atmsfcinterp_simobs_(self, geovals, obss, nvars, nlocs, hofx) ! calculate convective velocity call calc_conv_vel_gsi(u%vals(1,iobs), v%vals(1,iobs), thvg, thv1, V2) + ! although there could be first height below sea level, it causes floating-pt-except + zbot = max(0.1,phi%vals(1,iobs)) ! bottom model level in meters + agl = max(1.0, (obshgt(iobs)-obselev(iobs))) ! obs height above ground in meters + ! calculate bulk richardson number - rib = (grav * phi%vals(1,iobs) / th1) * (thv1 - thvg) / V2 + rib = (grav * zbot / th1) * (thv1 - thvg) / V2 - gzsoz0 = log(phi%vals(1,iobs)/z0) - gzzoz0 = log((obshgt(iobs)-obselev(iobs))/z0) + gzsoz0 = log(zbot/z0) + gzzoz0 = log(agl/z0) ! calculate parameters regardless of variable - call calc_psi_vars_gsi(rib, gzsoz0, gzzoz0, thv1, thv2, V2, th1,& - thg, phi%vals(1,iobs), obshgt(iobs)-obselev(iobs),& - psim, psih, psimz, psihz) + call calc_psi_vars_gsi(rib, gzsoz0, gzzoz0, thv1, thv2, V2, th1, & + thg, zbot, agl, psim, psih, psimz, psihz) do ivar = 1, nvars ! Get the name of input variable in geovals geovar = self%obsvars%variable(ivar) @@ -177,7 +181,7 @@ subroutine atmsfcinterp_simobs_(self, geovals, obss, nvars, nlocs, hofx) case("air_temperature", "virtual_temperature") psit = gzsoz0 - psih psitz = gzzoz0 - psihz - if (trim(geovar) == "air_temperature") then + if (cmp_strings(geovar, "air_temperature")) then ttmp1 = th1 ttmpg = thg else @@ -196,8 +200,8 @@ subroutine atmsfcinterp_simobs_(self, geovals, obss, nvars, nlocs, hofx) end if case("specific_humidity") ust = von_karman * sqrt(V2) / (gzsoz0 - psim) - psiq = log(von_karman*ust*phi%vals(1,iobs)/ka + phi%vals(1,iobs) / zq0) - psih - psiqz = log(von_karman*ust*(obshgt(iobs)-obselev(iobs))/ka + (obshgt(iobs)-obselev(iobs)) / zq0) - psihz + psiq = log(von_karman*ust*zbot/ka + zbot/zq0) - psih + psiqz = log(von_karman*ust*agl/ka + agl/zq0) - psihz hofx(ivar,iobs) = qg + (q%vals(1,iobs) - qg)*psiqz/psiq end select end do diff --git a/src/ufo/atmvertinterp/ObsAtmVertInterp.cc b/src/ufo/atmvertinterp/ObsAtmVertInterp.cc index 2e798831d..c42e977e6 100644 --- a/src/ufo/atmvertinterp/ObsAtmVertInterp.cc +++ b/src/ufo/atmvertinterp/ObsAtmVertInterp.cc @@ -8,13 +8,16 @@ #include "ufo/atmvertinterp/ObsAtmVertInterp.h" #include +#include #include "oops/util/Logger.h" #include "ioda/ObsVector.h" +#include "ufo/filters/Variables.h" #include "ufo/GeoVaLs.h" #include "ufo/ObsDiagnostics.h" +#include "ufo/utils/OperatorUtils.h" // for getOperatorVariables namespace ufo { @@ -27,7 +30,12 @@ ObsAtmVertInterp::ObsAtmVertInterp(const ioda::ObsSpace & odb, : ObsOperatorBase(odb, config), keyOperAtmVertInterp_(0), odb_(odb), varin_() { - ufo_atmvertinterp_setup_f90(keyOperAtmVertInterp_, config, odb.obsvariables(), varin_); + std::vector operatorVarIndices; + getOperatorVariables(config, odb.obsvariables(), operatorVars_, operatorVarIndices); + + ufo_atmvertinterp_setup_f90(keyOperAtmVertInterp_, config, + operatorVars_, operatorVarIndices.data(), operatorVarIndices.size(), + varin_); oops::Log::trace() << "ObsAtmVertInterp created." << std::endl; } diff --git a/src/ufo/atmvertinterp/ObsAtmVertInterp.h b/src/ufo/atmvertinterp/ObsAtmVertInterp.h index 21a58c2aa..ef1c37baa 100644 --- a/src/ufo/atmvertinterp/ObsAtmVertInterp.h +++ b/src/ufo/atmvertinterp/ObsAtmVertInterp.h @@ -46,6 +46,8 @@ class ObsAtmVertInterp : public ObsOperatorBase, // Other const oops::Variables & requiredVars() const override {return varin_;} + oops::Variables simulatedVars() const override {return operatorVars_;} + int & toFortran() {return keyOperAtmVertInterp_;} const int & toFortran() const {return keyOperAtmVertInterp_;} @@ -54,6 +56,7 @@ class ObsAtmVertInterp : public ObsOperatorBase, F90hop keyOperAtmVertInterp_; const ioda::ObsSpace& odb_; oops::Variables varin_; + oops::Variables operatorVars_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/atmvertinterp/ObsAtmVertInterp.interface.F90 b/src/ufo/atmvertinterp/ObsAtmVertInterp.interface.F90 index 8e7613d72..bdcad8e38 100644 --- a/src/ufo/atmvertinterp/ObsAtmVertInterp.interface.F90 +++ b/src/ufo/atmvertinterp/ObsAtmVertInterp.interface.F90 @@ -34,13 +34,16 @@ module ufo_atmvertinterp_mod_c ! ------------------------------------------------------------------------------ -subroutine ufo_atmvertinterp_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_atmvertinterp_setup_f90') +subroutine ufo_atmvertinterp_setup_c(c_key_self, c_conf, c_obsvars, c_obsvarindices, c_nobsvars, & + c_geovars) bind(c,name='ufo_atmvertinterp_setup_f90') use oops_variables_mod implicit none -integer(c_int), intent(inout) :: c_key_self -type(c_ptr), intent(in), value :: c_conf -type(c_ptr), intent(in), value :: c_obsvars !< variables to be simulated -type(c_ptr), intent(in), value :: c_geovars !< variables requested from the model +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), intent(in), value :: c_conf +type(c_ptr), intent(in), value :: c_obsvars ! variables to be simulated... +integer(c_int), intent(in), value :: c_nobsvars +integer(c_int), intent(in) :: c_obsvarindices(c_nobsvars) ! ... and their global indices +type(c_ptr), intent(in), value :: c_geovars ! variables requested from the model type(ufo_atmvertinterp), pointer :: self type(fckit_configuration) :: f_conf @@ -49,6 +52,8 @@ subroutine ufo_atmvertinterp_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) b f_conf = fckit_configuration(c_conf) self%obsvars = oops_variables(c_obsvars) +allocate(self%obsvarindices(self%obsvars%nvars())) +self%obsvarindices(:) = c_obsvarindices(:) + 1 ! Convert from C to Fortran indexing self%geovars = oops_variables(c_geovars) call self%setup(f_conf) diff --git a/src/ufo/atmvertinterp/ObsAtmVertInterp.interface.h b/src/ufo/atmvertinterp/ObsAtmVertInterp.interface.h index 835eedbea..a0933d602 100644 --- a/src/ufo/atmvertinterp/ObsAtmVertInterp.interface.h +++ b/src/ufo/atmvertinterp/ObsAtmVertInterp.interface.h @@ -26,8 +26,24 @@ extern "C" { // AtmVertInterp observation operator // ----------------------------------------------------------------------------- + /// \param operatorVars + /// Variables to be simulated by this operator. + /// \param operatorVarIndices + /// Indices of the variables from \p operatorVar in the list of all simulated + /// variables in the ObsSpace. + /// \param numOperatorVarIndices + /// Size of the \p operatorVarIndices array (must be the same as the number of variables in + /// \p operatorVars). + /// \param[out] requiredVars + /// GeoVaLs required for the simulation of the variables \p operatorVars. + /// + /// Example: if the list of simulated variables in the ObsSpace is + /// [air_temperature, northward_wind, eastward_wind] and \p operatorVars is + /// [northward_wind, eastward_wind], then \p operatorVarIndices should be set to [1, 2]. void ufo_atmvertinterp_setup_f90(F90hop &, const eckit::Configuration &, - const oops::Variables &, oops::Variables &); + const oops::Variables &operatorVars, + const int *operatorVarIndices, const int numOperatorVarIndices, + oops::Variables &requiredVars); void ufo_atmvertinterp_delete_f90(F90hop &); void ufo_atmvertinterp_simobs_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, const int &, const int &, double &); diff --git a/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.cc b/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.cc index 66f115b92..770e7c45e 100644 --- a/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.cc +++ b/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.cc @@ -8,6 +8,7 @@ #include "ufo/atmvertinterp/ObsAtmVertInterpTLAD.h" #include +#include #include "ioda/ObsSpace.h" #include "ioda/ObsVector.h" @@ -17,6 +18,7 @@ #include "ufo/GeoVaLs.h" #include "ufo/ObsBias.h" +#include "ufo/utils/OperatorUtils.h" // for getOperatorVariables namespace ufo { @@ -26,9 +28,15 @@ static LinearObsOperatorMaker makerVertInterpTL_("VertInte ObsAtmVertInterpTLAD::ObsAtmVertInterpTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOperAtmVertInterp_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOperAtmVertInterp_(0), varin_() { - ufo_atmvertinterp_tlad_setup_f90(keyOperAtmVertInterp_, config, odb.obsvariables(), varin_); + std::vector operatorVarIndices; + getOperatorVariables(config, odb.obsvariables(), operatorVars_, operatorVarIndices); + + ufo_atmvertinterp_tlad_setup_f90(keyOperAtmVertInterp_, config, + operatorVars_, + operatorVarIndices.data(), operatorVarIndices.size(), + varin_); oops::Log::trace() << "ObsAtmVertInterpTLAD created" << std::endl; } @@ -46,7 +54,7 @@ void ObsAtmVertInterpTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias ObsDiagnostics &) { oops::Log::trace() << "ObsAtmVertInterpTLAD::setTrajectory entering" << std::endl; - ufo_atmvertinterp_tlad_settraj_f90(keyOperAtmVertInterp_, geovals.toFortran(), odb_); + ufo_atmvertinterp_tlad_settraj_f90(keyOperAtmVertInterp_, geovals.toFortran(), obsspace()); oops::Log::trace() << "ObsAtmVertInterpTLAD::setTrajectory exiting" << std::endl; } @@ -54,7 +62,7 @@ void ObsAtmVertInterpTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias // ----------------------------------------------------------------------------- void ObsAtmVertInterpTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_atmvertinterp_simobs_tl_f90(keyOperAtmVertInterp_, geovals.toFortran(), odb_, + ufo_atmvertinterp_simobs_tl_f90(keyOperAtmVertInterp_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); oops::Log::trace() << "ObsAtmVertInterpTLAD::simulateObsTL exiting" << std::endl; @@ -63,7 +71,7 @@ void ObsAtmVertInterpTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVecto // ----------------------------------------------------------------------------- void ObsAtmVertInterpTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_atmvertinterp_simobs_ad_f90(keyOperAtmVertInterp_, geovals.toFortran(), odb_, + ufo_atmvertinterp_simobs_ad_f90(keyOperAtmVertInterp_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); oops::Log::trace() << "ObsAtmVertInterpTLAD::simulateObsAD exiting" << std::endl; diff --git a/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.h b/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.h index 65ee82e05..ba773d1e3 100644 --- a/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.h +++ b/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.h @@ -49,14 +49,16 @@ class ObsAtmVertInterpTLAD : public LinearObsOperatorBase, // Other const oops::Variables & requiredVars() const override {return varin_;} + oops::Variables simulatedVars() const override {return operatorVars_;} + int & toFortran() {return keyOperAtmVertInterp_;} const int & toFortran() const {return keyOperAtmVertInterp_;} private: void print(std::ostream &) const override; F90hop keyOperAtmVertInterp_; - const ioda::ObsSpace& odb_; oops::Variables varin_; + oops::Variables operatorVars_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.interface.F90 b/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.interface.F90 index ae4bdacd3..584269b08 100644 --- a/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.interface.F90 +++ b/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.interface.F90 @@ -31,13 +31,16 @@ module ufo_atmvertinterp_tlad_mod_c ! ------------------------------------------------------------------------------ -subroutine ufo_atmvertinterp_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_atmvertinterp_tlad_setup_f90') +subroutine ufo_atmvertinterp_tlad_setup_c(c_key_self, c_conf, c_obsvars, & + c_obsvarindices, c_nobsvars, c_geovars) bind(c,name='ufo_atmvertinterp_tlad_setup_f90') use oops_variables_mod implicit none -integer(c_int), intent(inout) :: c_key_self -type(c_ptr), value, intent(in) :: c_conf -type(c_ptr), value, intent(in) :: c_obsvars ! variables to be simulated -type(c_ptr), value, intent(in) :: c_geovars ! variables requested from the model +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), value, intent(in) :: c_conf +type(c_ptr), intent(in), value :: c_obsvars ! variables to be simulated... +integer(c_int), intent(in), value :: c_nobsvars +integer(c_int), intent(in) :: c_obsvarindices(c_nobsvars) ! ... and their global indices +type(c_ptr), value, intent(in) :: c_geovars ! variables requested from the model type(ufo_atmvertinterp_tlad), pointer :: self type(fckit_configuration) :: f_conf @@ -46,6 +49,8 @@ subroutine ufo_atmvertinterp_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_geova f_conf = fckit_configuration(c_conf) self%obsvars = oops_variables(c_obsvars) +allocate(self%obsvarindices(self%obsvars%nvars())) +self%obsvarindices(:) = c_obsvarindices(:) + 1 ! Convert from C to Fortran indexing self%geovars = oops_variables(c_geovars) call self%setup(f_conf) diff --git a/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.interface.h b/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.interface.h index 4cb2482ef..6936ce52f 100644 --- a/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.interface.h +++ b/src/ufo/atmvertinterp/ObsAtmVertInterpTLAD.interface.h @@ -26,8 +26,25 @@ extern "C" { // AtmVertInterp tl/ad observation operator // ----------------------------------------------------------------------------- + /// \param operatorVars + /// Variables to be simulated by this operator. + /// \param operatorVarIndices + /// Indices of the variables from \p operatorVar in the list of all simulated + /// variables in the ObsSpace. + /// \param numOperatorVarIndices + /// Size of the \p operatorVarIndices array (must be the same as the number of variables in + /// \p operatorVars). + /// \param[out] requiredVars + /// GeoVaLs required for the simulation of the variables \p operatorVars. + /// + /// Example: if the list of simulated variables in the ObsSpace is + /// [air_temperature, northward_wind, eastward_wind] and \p operatorVars is + /// [northward_wind, eastward_wind], then \p operatorVarIndices should be set to [1, 2]. void ufo_atmvertinterp_tlad_setup_f90(F90hop &, const eckit::Configuration &, - const oops::Variables &, oops::Variables &); + const oops::Variables &operatorVars, + const int *operatorVarIndices, + const int numOperatorVarIndices, + oops::Variables &requiredVars); void ufo_atmvertinterp_tlad_delete_f90(F90hop &); void ufo_atmvertinterp_tlad_settraj_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &); void ufo_atmvertinterp_simobs_tl_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, diff --git a/src/ufo/atmvertinterp/ufo_atmvertinterp_mod.F90 b/src/ufo/atmvertinterp/ufo_atmvertinterp_mod.F90 index 338124fe7..be7b5e81c 100644 --- a/src/ufo/atmvertinterp/ufo_atmvertinterp_mod.F90 +++ b/src/ufo/atmvertinterp/ufo_atmvertinterp_mod.F90 @@ -11,8 +11,11 @@ module ufo_atmvertinterp_mod type, public :: ufo_atmvertinterp type(oops_variables), public :: geovars - type(oops_variables), public :: obsvars + type(oops_variables), public :: obsvars ! Variables to be simulated + integer, allocatable, public :: obsvarindices(:) ! Indices of obsvars in the list of all + ! simulated variables in the ObsSpace character(len=MAXVARLEN), public :: v_coord ! GeoVaL to use to interpolate in vertical + character(len=MAXVARLEN), public :: o_v_coord ! Observation vertical coordinate logical, public :: use_ln ! if T, use ln(v_coord) not v_coord contains procedure :: setup => atmvertinterp_setup_ @@ -45,13 +48,27 @@ subroutine atmvertinterp_setup_(self, grid_conf) if( grid_conf%has("vertical coordinate") ) then call grid_conf%get_or_die("vertical coordinate",coord_name) self%v_coord = coord_name - if( trim(self%v_coord) .eq. var_prs ) self%use_ln = .true. + if( (trim(self%v_coord) .eq. var_prs) .or. & + (trim(self%v_coord) .eq. var_prsi) ) then + self%use_ln = .true. + endif else ! default self%v_coord = var_prs self%use_ln = .true. endif - call self%geovars%push_back(self%v_coord) + !> Determine observation vertical coordinate. + ! Use the model vertical coordinate unless the option + ! 'observation vertical coordinate' is specified. + if ( grid_conf%has("observation vertical coordinate") ) then + call grid_conf%get_or_die("observation vertical coordinate",coord_name) + self%o_v_coord = coord_name + else + self%o_v_coord = self%v_coord + endif + + call self%geovars%push_back(self%v_coord) + end subroutine atmvertinterp_setup_ ! ------------------------------------------------------------------------------ @@ -68,7 +85,7 @@ subroutine atmvertinterp_simobs_(self, geovals, obss, nvars, nlocs, hofx) real(c_double), intent(inout) :: hofx(nvars, nlocs) type(c_ptr), value, intent(in) :: obss - integer :: iobs, ivar + integer :: iobs, ivar, iobsvar real(kind_real), dimension(:), allocatable :: obsvcoord type(ufo_geoval), pointer :: vcoordprofile, profile real(kind_real), allocatable :: wf(:) @@ -83,7 +100,7 @@ subroutine atmvertinterp_simobs_(self, geovals, obss, nvars, nlocs, hofx) ! Get the observation vertical coordinates allocate(obsvcoord(nlocs)) - call obsspace_get_db(obss, "MetaData", self%v_coord, obsvcoord) + call obsspace_get_db(obss, "MetaData", self%o_v_coord, obsvcoord) ! Allocate arrays for interpolation weights allocate(wi(nlocs)) @@ -102,9 +119,12 @@ subroutine atmvertinterp_simobs_(self, geovals, obss, nvars, nlocs, hofx) call vert_interp_weights(vcoordprofile%nval, tmp2, tmp, wi(iobs), wf(iobs)) enddo - do ivar = 1, nvars + do iobsvar = 1, size(self%obsvarindices) + ! Get the index of the row of hofx to fill + ivar = self%obsvarindices(iobsvar) + ! Get the name of input variable in geovals - geovar = self%geovars%variable(ivar) + geovar = self%geovars%variable(iobsvar) ! Get profile for this variable from geovals call ufo_geovals_get_var(geovals, geovar, profile) diff --git a/src/ufo/atmvertinterp/ufo_atmvertinterp_tlad_mod.F90 b/src/ufo/atmvertinterp/ufo_atmvertinterp_tlad_mod.F90 index 8eeed86a5..6660a5fcf 100644 --- a/src/ufo/atmvertinterp/ufo_atmvertinterp_tlad_mod.F90 +++ b/src/ufo/atmvertinterp/ufo_atmvertinterp_tlad_mod.F90 @@ -16,12 +16,15 @@ module ufo_atmvertinterp_tlad_mod type, public :: ufo_atmvertinterp_tlad private - type(oops_variables), public :: obsvars + type(oops_variables), public :: obsvars ! Variables to be simulated + integer, allocatable, public :: obsvarindices(:) ! Indices of obsvars in the list of all + ! simulated variables in the ObsSpace type(oops_variables), public :: geovars integer :: nval, nlocs real(kind_real), allocatable :: wf(:) integer, allocatable :: wi(:) character(len=MAXVARLEN), public :: v_coord ! GeoVaL to use to interpolate in vertical + character(len=MAXVARLEN), public :: o_v_coord ! Observation vertical coordinate logical, public :: use_ln ! if T, use ln(v_coord) not v_coord contains procedure :: setup => atmvertinterp_tlad_setup_ @@ -54,12 +57,24 @@ subroutine atmvertinterp_tlad_setup_(self, grid_conf) self%use_ln = .false. if( grid_conf%has("vertical coordinate") ) then - call grid_conf%get_or_die("vertical coordinate",coord_name) - self%v_coord = coord_name - if( trim(self%v_coord) .eq. var_prs ) self%use_ln = .true. + call grid_conf%get_or_die("vertical coordinate",coord_name) + self%v_coord = coord_name + if( (self%v_coord .eq. var_prs) .or. (self%v_coord .eq. var_prsi) ) then + self%use_ln = .true. + endif else ! default - self%v_coord = var_prs - self%use_ln = .true. + self%v_coord = var_prs + self%use_ln = .true. + endif + + !> Determine observation vertical coordinate. + ! Use the model vertical coordinate unless the option + ! 'observation vertical coordinate' is specified. + if ( grid_conf%has("observation vertical coordinate") ) then + call grid_conf%get_or_die("observation vertical coordinate",coord_name) + self%o_v_coord = coord_name + else + self%o_v_coord = self%v_coord endif end subroutine atmvertinterp_tlad_setup_ @@ -89,7 +104,7 @@ subroutine atmvertinterp_tlad_settraj_(self, geovals, obss) ! Get the observation vertical coordinates self%nlocs = obsspace_get_nlocs(obss) allocate(obsvcoord(self%nlocs)) - call obsspace_get_db(obss, "MetaData", self%v_coord, obsvcoord) + call obsspace_get_db(obss, "MetaData", self%o_v_coord, obsvcoord) ! Allocate arrays for interpolation weights allocate(self%wi(self%nlocs)) @@ -124,13 +139,16 @@ subroutine atmvertinterp_simobs_tl_(self, geovals, obss, nvars, nlocs, hofx) real(c_double), intent(inout) :: hofx(nvars, nlocs) type(c_ptr), value, intent(in) :: obss - integer :: iobs, ivar + integer :: iobs, iobsvar, ivar type(ufo_geoval), pointer :: profile character(len=MAXVARLEN) :: geovar - do ivar = 1, nvars + do iobsvar = 1, size(self%obsvarindices) + ! Get the index of the row of hofx to fill + ivar = self%obsvarindices(iobsvar) + ! Get the name of input variable in geovals - geovar = self%geovars%variable(ivar) + geovar = self%geovars%variable(iobsvar) ! Get profile for this variable from geovals call ufo_geovals_get_var(geovals, geovar, profile) @@ -153,16 +171,19 @@ subroutine atmvertinterp_simobs_ad_(self, geovals, obss, nvars, nlocs, hofx) real(c_double), intent(in) :: hofx(nvars, nlocs) type(c_ptr), value, intent(in) :: obss - integer :: iobs, ivar + integer :: iobs, iobsvar, ivar type(ufo_geoval), pointer :: profile character(len=MAXVARLEN) :: geovar real(c_double) :: missing missing = missing_value(missing) - do ivar = 1, nvars + do iobsvar = 1, size(self%obsvarindices) + ! Get the index of the row of hofx to fill + ivar = self%obsvarindices(iobsvar) + ! Get the name of input variable in geovals - geovar = self%geovars%variable(ivar) + geovar = self%geovars%variable(iobsvar) ! Get pointer to profile for this variable in geovals call ufo_geovals_get_var(geovals, geovar, profile) diff --git a/src/ufo/atmvertinterplay/CMakeLists.txt b/src/ufo/atmvertinterplay/CMakeLists.txt index efbe7dae3..406f22cb6 100644 --- a/src/ufo/atmvertinterplay/CMakeLists.txt +++ b/src/ufo/atmvertinterplay/CMakeLists.txt @@ -9,7 +9,11 @@ set ( atmvertinterplay_files ObsAtmVertInterpLay.interface.F90 ObsAtmVertInterpLay.interface.h ufo_atmvertinterplay_mod.F90 - pindex.f90 + ObsAtmVertInterpLayTLAD.h + ObsAtmVertInterpLayTLAD.cc + ObsAtmVertInterpLayTLAD.interface.F90 + ufo_atmvertinterplay_tlad_mod.F90 + vert_interp_lay_mod.F90 ) PREPEND( _p_atmvertinterplay_files "atmvertinterplay" ${atmvertinterplay_files} ) diff --git a/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.cc b/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.cc new file mode 100644 index 000000000..fb5bb6594 --- /dev/null +++ b/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.cc @@ -0,0 +1,80 @@ +/* + * (C) Copyright 2017-2018 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.h" + +#include + +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsBias.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static LinearObsOperatorMaker makerVertInterpLayTL_("AtmVertInterpLay"); +// ----------------------------------------------------------------------------- + +ObsAtmVertInterpLayTLAD::ObsAtmVertInterpLayTLAD(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : LinearObsOperatorBase(odb), keyOperAtmVertInterpLay_(0), varin_() +{ + ufo_atmvertinterplay_tlad_setup_f90(keyOperAtmVertInterpLay_, config, odb.obsvariables(), varin_); + + oops::Log::trace() << "ObsAtmVertInterpLayTLAD created" << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsAtmVertInterpLayTLAD::~ObsAtmVertInterpLayTLAD() { + ufo_atmvertinterplay_tlad_delete_f90(keyOperAtmVertInterpLay_); + oops::Log::trace() << "ObsAtmVertInterpLayTLAD destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsAtmVertInterpLayTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, + ObsDiagnostics &) { + oops::Log::trace() << "ObsAtmVertInterpLayTLAD::setTrajectory entering" << std::endl; + + ufo_atmvertinterplay_tlad_settraj_f90(keyOperAtmVertInterpLay_, geovals.toFortran(), obsspace()); + + oops::Log::trace() << "ObsAtmVertInterpLayTLAD::setTrajectory exiting" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsAtmVertInterpLayTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { + ufo_atmvertinterplay_simobs_tl_f90(keyOperAtmVertInterpLay_, geovals.toFortran(), obsspace(), + ovec.nvars(), ovec.nlocs(), ovec.toFortran()); + + oops::Log::trace() << "ObsAtmVertInterpLayTLAD::simulateObsTL exiting" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsAtmVertInterpLayTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { + ufo_atmvertinterplay_simobs_ad_f90(keyOperAtmVertInterpLay_, geovals.toFortran(), obsspace(), + ovec.nvars(), ovec.nlocs(), ovec.toFortran()); + + oops::Log::trace() << "ObsAtmVertInterpLayTLAD::simulateObsAD exiting" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsAtmVertInterpLayTLAD::print(std::ostream & os) const { + os << "ObsAtmVertInterpLayTLAD::print not implemented" << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.h b/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.h new file mode 100644 index 000000000..036dad0a0 --- /dev/null +++ b/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.h @@ -0,0 +1,64 @@ +/* + * (C) Copyright 2017-2018 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_ATMVERTINTERPLAY_OBSATMVERTINTERPLAYTLAD_H_ +#define UFO_ATMVERTINTERPLAY_OBSATMVERTINTERPLAYTLAD_H_ + +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.interface.h" +#include "ufo/LinearObsOperatorBase.h" + +// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsBias; + class ObsDiagnostics; + +// ----------------------------------------------------------------------------- +/// AtmVertInterpLay observation operator +class ObsAtmVertInterpLayTLAD : public LinearObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsAtmVertInterpLayTLAD";} + + ObsAtmVertInterpLayTLAD(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsAtmVertInterpLayTLAD(); + + // Obs Operators + void setTrajectory(const GeoVaLs &, const ObsBias &, ObsDiagnostics &) override; + void simulateObsTL(const GeoVaLs &, ioda::ObsVector &) const override; + void simulateObsAD(GeoVaLs &, const ioda::ObsVector &) const override; + + // Other + const oops::Variables & requiredVars() const override {return varin_;} + + int & toFortran() {return keyOperAtmVertInterpLay_;} + const int & toFortran() const {return keyOperAtmVertInterpLay_;} + + private: + void print(std::ostream &) const override; + F90hop keyOperAtmVertInterpLay_; + oops::Variables varin_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_ATMVERTINTERPLAY_OBSATMVERTINTERPLAYTLAD_H_ diff --git a/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.interface.F90 b/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.interface.F90 new file mode 100644 index 000000000..9b12f9ade --- /dev/null +++ b/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.interface.F90 @@ -0,0 +1,134 @@ +! (C) Copyright 2017-2018 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to handle atmvertinterplay observations + +module ufo_atmvertinterplay_tlad_mod_c + + use fckit_configuration_module, only: fckit_configuration + use ufo_atmvertinterplay_tlad_mod + use ufo_geovals_mod_c, only: ufo_geovals_registry + use ufo_geovals_mod, only: ufo_geovals + implicit none + + private + +#define LISTED_TYPE ufo_atmvertinterplay_tlad + + !> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + + !> Global registry + type(registry_t) :: ufo_atmvertinterplay_tlad_registry + + ! ------------------------------------------------------------------------------ +contains + ! ------------------------------------------------------------------------------ + !> Linked list implementation +#include "oops/util/linkedList_c.f" + +! ------------------------------------------------------------------------------ + +subroutine ufo_atmvertinterplay_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_atmvertinterplay_tlad_setup_f90') +use oops_variables_mod +implicit none +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), value, intent(in) :: c_conf +type(c_ptr), value, intent(in) :: c_obsvars ! variables to be simulated +type(c_ptr), value, intent(in) :: c_geovars ! variables requested from the model + +type(ufo_atmvertinterplay_tlad), pointer :: self +type(fckit_configuration) :: f_conf + +call ufo_atmvertinterplay_tlad_registry%setup(c_key_self, self) +f_conf = fckit_configuration(c_conf) + +self%obsvars = oops_variables(c_obsvars) +self%geovars = oops_variables(c_geovars) +call self%setup(f_conf) + +end subroutine ufo_atmvertinterplay_tlad_setup_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_atmvertinterplay_tlad_delete_c(c_key_self) bind(c,name='ufo_atmvertinterplay_tlad_delete_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self + +type(ufo_atmvertinterplay_tlad), pointer :: self + +call ufo_atmvertinterplay_tlad_registry%delete(c_key_self, self) + +end subroutine ufo_atmvertinterplay_tlad_delete_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_atmvertinterplay_tlad_settraj_c(c_key_self, c_key_geovals, c_obsspace) bind(c,name='ufo_atmvertinterplay_tlad_settraj_f90') +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace + +type(ufo_atmvertinterplay_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +character(len=*), parameter :: myname_="ufo_atmvertinterplay_tlad_settraj_c" + +call ufo_atmvertinterplay_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) + +call self%settraj(geovals, c_obsspace) + +end subroutine ufo_atmvertinterplay_tlad_settraj_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_atmvertinterplay_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) & + bind(c,name='ufo_atmvertinterplay_simobs_tl_f90') +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nvars, c_nlocs +real(c_double), intent(inout) :: c_hofx(c_nvars, c_nlocs) + +type(ufo_atmvertinterplay_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +character(len=*), parameter :: myname_="ufo_atmvertinterplay_simobs_tl_c" + +call ufo_atmvertinterplay_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) + +call self%simobs_tl(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) + +end subroutine ufo_atmvertinterplay_simobs_tl_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_atmvertinterplay_simobs_ad_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) & + bind(c,name='ufo_atmvertinterplay_simobs_ad_f90') +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nvars, c_nlocs +real(c_double), intent(in) :: c_hofx(c_nvars, c_nlocs) + +type(ufo_atmvertinterplay_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +character(len=*), parameter :: myname_="ufo_atmvertinterplay_simobs_ad_c" + +call ufo_atmvertinterplay_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) + +call self%simobs_ad(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) + +end subroutine ufo_atmvertinterplay_simobs_ad_c + +! ------------------------------------------------------------------------------ + +end module ufo_atmvertinterplay_tlad_mod_c diff --git a/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.interface.h b/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.interface.h new file mode 100644 index 000000000..4e89f56ee --- /dev/null +++ b/src/ufo/atmvertinterplay/ObsAtmVertInterpLayTLAD.interface.h @@ -0,0 +1,44 @@ +/* + * (C) Copyright 2017 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_ATMVERTINTERPLAY_OBSATMVERTINTERPLAYTLAD_INTERFACE_H_ +#define UFO_ATMVERTINTERPLAY_OBSATMVERTINTERPLAYTLAD_INTERFACE_H_ + +#include "ioda/ObsSpace.h" +#include "oops/base/Variables.h" +#include "ufo/Fortran.h" + +namespace ufo { + +/// Interface to Fortran UFO routines +/*! + * The core of the UFO is coded in Fortran. + * Here we define the interfaces to the Fortran code. + */ + +extern "C" { + +// ----------------------------------------------------------------------------- +// AtmVertinterplay tl/ad observation operator +// ----------------------------------------------------------------------------- + + void ufo_atmvertinterplay_tlad_setup_f90(F90hop &, const eckit::Configuration &, + const oops::Variables &, oops::Variables &); + void ufo_atmvertinterplay_tlad_delete_f90(F90hop &); + void ufo_atmvertinterplay_tlad_settraj_f90(const F90hop &, const F90goms &, + const ioda::ObsSpace &); + void ufo_atmvertinterplay_simobs_tl_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + const int &, const int &, double &); + void ufo_atmvertinterplay_simobs_ad_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + const int &, const int &, const double &); + +// ----------------------------------------------------------------------------- + +} // extern C + +} // namespace ufo +#endif // UFO_ATMVERTINTERPLAY_OBSATMVERTINTERPLAYTLAD_INTERFACE_H_ diff --git a/src/ufo/atmvertinterplay/pindex.f90 b/src/ufo/atmvertinterplay/pindex.f90 deleted file mode 100644 index 302120901..000000000 --- a/src/ufo/atmvertinterplay/pindex.f90 +++ /dev/null @@ -1,36 +0,0 @@ -real function pindex(nx, press, obspressure) -! This routine handles press (pressure) in decendent order (bottom2top) -! press(1): highest pressure value -! press(nx): lowerest pressure value - -use kinds -implicit none - -integer :: ix, k, nx -real(kind_real) :: ozp, obspressure, psi -real(kind_real), dimension(nx) :: press - -psi = 1.0_kind_real/press(1) -if(obspressure*psi < 1.) then - ozp = obspressure -else - ozp = press(1) -endif -if( ozp >= press(1)) then - ix = 1 -else - ix = 0 - do k = 1, nx-1 - if(ozp >= press(k)) then - ix = k - exit - endif - enddo - if(ix == 0) ix = nx - if(ix > 1)ix = ix -1 -endif -ozp = float(ix) + & - (ozp-press(ix))/(press(ix+1)-press(ix)) -pindex = ozp -return -end diff --git a/src/ufo/atmvertinterplay/ufo_atmvertinterplay_mod.F90 b/src/ufo/atmvertinterplay/ufo_atmvertinterplay_mod.F90 index 45ea4031d..2d68809ec 100644 --- a/src/ufo/atmvertinterplay/ufo_atmvertinterplay_mod.F90 +++ b/src/ufo/atmvertinterplay/ufo_atmvertinterplay_mod.F90 @@ -9,7 +9,7 @@ module ufo_atmvertinterplay_mod use oops_variables_mod use ufo_vars_mod - + use vert_interp_lay_mod implicit none private @@ -79,6 +79,7 @@ subroutine ufo_atmvertinterplay_setup(self, conf) end subroutine ufo_atmvertinterplay_setup + ! ------------------------------------------------------------------------------ subroutine ufo_atmvertinterplay_simobs(self, geovals_in, obss, nvars, nlocs, hofx) use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var @@ -93,19 +94,15 @@ subroutine ufo_atmvertinterplay_simobs(self, geovals_in, obss, nvars, nlocs, hof type(c_ptr), value, intent(in) :: obss ! Local variables -integer :: iobs, ivar, iprof -integer :: iz1, iz2, kk -integer :: k1, k2 -integer :: nsig, nprof, nlev +integer :: iobs, ivar +integer :: nsig, nlevs real(kind_real), dimension(:), allocatable :: toppressure,botpressure,airpressure type(ufo_geovals) :: geovals type(ufo_geoval), pointer :: modelpressures, modelozone character(len=MAXVARLEN) :: geovar character(len=MAXVARLEN) :: var_zdir real :: pob,delp4,delz,dz1 -real(kind_real) :: rozcon, g -real(kind_real) :: topozp, botozp -real :: pindex +real(kind_real) :: layer_oz ! Notes: ! (1) Set desired vertical coordinate direction (top2bottom or bottom2top) based @@ -122,46 +119,16 @@ subroutine ufo_atmvertinterplay_simobs(self, geovals_in, obss, nvars, nlocs, hof ! Get pressure profiles from geovals [Pa] call ufo_geovals_get_var(geovals, var_prsi, modelpressures) nsig = modelpressures%nval - 1 - + ! Allocate pressure limits and get air_pressure metadata from obs allocate(toppressure(nlocs)) allocate(botpressure(nlocs)) allocate(airpressure(nlocs)) + call obsspace_get_db(obss, "MetaData", "air_pressure", airpressure) do ivar = 1, nvars write(6,*) 'ufo_atmvertinterplay_simobs: self%nlevels = ', self%nlevels - if (self%nlevels(ivar) == 1) then ! total column ozone - do iobs = 1, nlocs - toppressure(iobs) = modelpressures%vals(nsig+1, iobs) - botpressure(iobs) = modelpressures%vals(1, iobs) - enddo - else - !Obs pressures read in as Pa - call obsspace_get_db(obss, "MetaData", "air_pressure", airpressure) - nlev = self%nlevels(ivar) - nprof = nlocs/nlev - iobs = 0 - do iprof = 1, nprof - do kk = 1, nlev - k1 = kk - k2 = kk - 1 - if (k2 == 0) k2 = 1 - if (kk == nlev) then - k1 = nlev - 1 - k2 = 1 - endif - iobs = iobs+1 - toppressure(iobs) = airpressure(k2) - botpressure(iobs) = airpressure(k1) - if( kk== 1 ) then - toppressure(iobs) =modelpressures%vals(nsig+1, iobs) - botpressure(iobs) = airpressure(1) - else if( kk == nlev) then - toppressure(iobs) = modelpressures%vals(nsig+1, iobs) - botpressure(iobs) = modelpressures%vals(1, iobs) - endif - enddo - enddo - endif + nlevs = self%nlevels(ivar) + call get_integral_limits(airpressure, botpressure, toppressure, modelpressures%vals(:,:), nlevs, nlocs, nsig) !Get the name of input variable in geovals geovar = self%geovars%variable(ivar) @@ -170,35 +137,13 @@ subroutine ufo_atmvertinterplay_simobs(self, geovals_in, obss, nvars, nlocs, hof call ufo_geovals_get_var(geovals, geovar, modelozone) do iobs = 1, nlocs - topozp = pindex(nsig+1, modelpressures%vals(1, iobs), toppressure(iobs)) - botozp = pindex(nsig+1, modelpressures%vals(1, iobs), botpressure(iobs)) - - pob = botozp - iz1 = topozp - if (iz1>nsig) iz1=nsig - iz2 = pob - !For total column ozone - if(iz1 .eq. nsig .and. iz2 .lt.7)iz2 = 1 - g = 0. - dz1 = topozp - do kk=iz1,iz2,-1 - delz = 1. - if(kk==iz1)delz=dz1-iz1 - if (kk==iz2) delz=delz-pob+iz2 - !For total column ozone - if(iz1 .eq. nsig .and. iz2 .eq. 1)delz = 1 - !Interpolate in cbars - delp4 = (modelpressures%vals(kk,iobs)-modelpressures%vals(kk+1,iobs)) ! [Pa] - g = g + modelozone%vals(kk,iobs)*self%coefficients(ivar)*(delz*delp4) - enddo - hofx(ivar,iobs) = g - dz1 = pob + call apply_layer_integral(self%coefficients(ivar), modelozone%vals(:,iobs), modelpressures%vals(:,iobs), botpressure(iobs), toppressure(iobs), nsig, layer_oz ) + hofx(ivar,iobs) = layer_oz enddo enddo deallocate(toppressure) deallocate(botpressure) call ufo_geovals_delete(geovals) - end subroutine ufo_atmvertinterplay_simobs diff --git a/src/ufo/atmvertinterplay/ufo_atmvertinterplay_tlad_mod.F90 b/src/ufo/atmvertinterplay/ufo_atmvertinterplay_tlad_mod.F90 new file mode 100644 index 000000000..5d45b8399 --- /dev/null +++ b/src/ufo/atmvertinterplay/ufo_atmvertinterplay_tlad_mod.F90 @@ -0,0 +1,237 @@ +! (C) Copyright 2017-2019 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +module ufo_atmvertinterplay_tlad_mod + + use oops_variables_mod + use ufo_vars_mod + use ufo_geovals_mod + use vert_interp_lay_mod + use missing_values_mod + use, intrinsic :: iso_c_binding + use kinds, only: kind_real + use missing_values_mod + +! ------------------------------------------------------------------------------ + + type, public :: ufo_atmvertinterplay_tlad + private + type(oops_variables), public :: obsvars + type(oops_variables), public :: geovars + integer :: nval, nlocs + logical :: flip_it + real(kind_real), dimension(:), allocatable :: toppressure, botpressure + integer, allocatable :: nlevels(:) + real, allocatable :: coefficients(:) ! unit conversion from geoval to obs + real(kind_real),allocatable :: modelpressures(:,:) + contains + procedure :: setup => atmvertinterplay_tlad_setup_ + procedure :: cleanup => atmvertinterplay_tlad_cleanup_ + procedure :: settraj => atmvertinterplay_tlad_settraj_ + procedure :: simobs_tl => atmvertinterplay_simobs_tl_ + procedure :: simobs_ad => atmvertinterplay_simobs_ad_ + final :: destructor + end type ufo_atmvertinterplay_tlad + +! ------------------------------------------------------------------------------ +contains +! ------------------------------------------------------------------------------ +subroutine atmvertinterplay_tlad_setup_(self, grid_conf) + use fckit_configuration_module, only: fckit_configuration + implicit none + class(ufo_atmvertinterplay_tlad), intent(inout) :: self + type(fckit_configuration), intent(in) :: grid_conf + + character(kind=c_char,len=:), allocatable :: coord_name + character(kind=c_char,len=:), allocatable :: gvars(:) + real(kind=c_double), allocatable :: coefficients(:) + integer(kind=c_int), allocatable :: nlevels(:) + !Local Variables + integer :: ivar, nlevs=0, nvars=0, ngvars=0, ncoefs=0 + ! Check configurations + if (grid_conf%has("geovals")) then + ngvars = grid_conf%get_size("geovals") + call grid_conf%get_or_die("geovals", gvars) + ! add to geovars list + do ivar = 1, ngvars + call self%geovars%push_back(gvars(ivar)) + enddo + endif + nvars = self%obsvars%nvars() + if (ngvars == 0 .and. nvars > 0) then + allocate(self%coefficients(nvars)) + do ivar = 1, nvars + call self%geovars%push_back(self%obsvars%variable(ivar)) + self%coefficients(ivar) = 1.0 + enddo + endif + if (grid_conf%has("coefficients")) then + ncoefs = grid_conf%get_size("coefficients") + call grid_conf%get_or_die("coefficients", coefficients) + allocate(self%coefficients(ncoefs)) + self%coefficients(1:ncoefs) = coefficients(1:ncoefs) + endif + if (grid_conf%has("nlevels")) then + nlevs = grid_conf%get_size("nlevels") + call grid_conf%get_or_die("nlevels", nlevels) + allocate(self%nlevels(nlevs)) + self%nlevels(1:nlevs) = nlevels(1:nlevs) + endif + + +end subroutine atmvertinterplay_tlad_setup_ + +! ------------------------------------------------------------------------------ + +subroutine atmvertinterplay_tlad_settraj_(self, geovals_in, obss) + use obsspace_mod + implicit none + class(ufo_atmvertinterplay_tlad), intent(inout) :: self + type(ufo_geovals), intent(in) :: geovals_in + type(c_ptr), value, intent(in) :: obss + integer :: iobs,nlevs,nsig,ilev + type(ufo_geovals) :: geovals + type(ufo_geoval),pointer :: modelpres + type(ufo_geoval),pointer :: p_temp + type(ufo_geoval),pointer :: profile + character(len=MAXVARLEN) :: var_zdir + character(len=MAXVARLEN) :: geovar + real(kind_real), dimension(:), allocatable :: airpressure + ! Make sure nothing already allocated + call self%cleanup() + + ! Get the observation vertical coordinates + self%nlocs = obsspace_get_nlocs(obss) + ! Allocate arrays for top and bottom pressures for integral + allocate(self%toppressure(self%nlocs)) + allocate(self%botpressure(self%nlocs)) + allocate(airpressure(self%nlocs)) + call ufo_geovals_copy(geovals_in, geovals) ! dont want to change geovals_in + + call ufo_geovals_get_var(geovals, var_prsi, p_temp) + var_zdir = var_prsi ! vertical coordinate variable + !if profile direction is top2bottom flip geovals, and make sure tlm and adj follow suit with self%flip_it + if( p_temp%vals(1,1) < p_temp%vals(p_temp%nval,1) ) then + call ufo_geovals_reorderzdir(geovals, var_zdir, "bottom2top") + self%flip_it = .true. + else + self%flip_it = .false. + endif + + ! Get pressure profiles from geovals [Pa] + call ufo_geovals_get_var(geovals, var_prsi, modelpres) + nlevs = self%nlevels(1) + nsig = modelpres%nval - 1 + self%nval = modelpres%nval + call obsspace_get_db(obss, "MetaData", "air_pressure", airpressure) + + allocate(self%modelpressures(modelpres%nval,self%nlocs)) + self%modelpressures(1:nsig+1,1:self%nlocs) = modelpres%vals(1:nsig+1,1:self%nlocs) + call get_integral_limits(airpressure(:), self%botpressure(:), self%toppressure(:), self%modelpressures(:,:), nlevs, self%nlocs, nsig) + + + ! Cleanup memory + deallocate(airpressure) + call ufo_geovals_delete(geovals) +end subroutine atmvertinterplay_tlad_settraj_ + +! ------------------------------------------------------------------------------ + +subroutine atmvertinterplay_simobs_tl_(self, geovals_in, obss, nvars, nlocs, hofx) + implicit none + class(ufo_atmvertinterplay_tlad), intent(in) :: self + type(ufo_geovals), intent(in) :: geovals_in + integer, intent(in) :: nvars, nlocs + real(c_double), intent(inout) :: hofx(nvars, nlocs) + type(c_ptr), value, intent(in) :: obss + + integer :: iobs, ivar + type(ufo_geoval), pointer :: profile + type(ufo_geoval), pointer :: pressure + character(len=MAXVARLEN) :: geovar + character(len=MAXVARLEN) :: var_zdir + type(ufo_geovals) :: geovals + integer :: nsig + call ufo_geovals_copy(geovals_in, geovals) ! dont want to change geovals_in + do ivar = 1, nvars + ! Get the name of input variable in geovals + geovar = self%geovars%variable(ivar) + call ufo_geovals_get_var(geovals, geovar, profile) + nsig = self%nval-1 + do iobs = 1, nlocs + if(self%flip_it) profile%vals(1:profile%nval,iobs) = profile%vals(profile%nval:1:-1,iobs) + call vert_interp_lay_apply_tl(profile%vals(:,iobs), hofx(ivar,iobs), self%coefficients(ivar), self%modelpressures(:,iobs), self%botpressure(iobs), self%toppressure(iobs), nsig) + enddo + enddo + call ufo_geovals_delete(geovals) +end subroutine atmvertinterplay_simobs_tl_ + +! ------------------------------------------------------------------------------ + +subroutine atmvertinterplay_simobs_ad_(self, geovals, obss, nvars, nlocs, hofx) + implicit none + class(ufo_atmvertinterplay_tlad), intent(in) :: self + type(ufo_geovals), intent(inout) :: geovals + integer, intent(in) :: nvars, nlocs + real(c_double), intent(in) :: hofx(nvars, nlocs) + type(c_ptr), value, intent(in) :: obss + + integer :: iobs, ivar + type(ufo_geoval), pointer :: profile + type(ufo_geoval), pointer :: pressure + character(len=MAXVARLEN) :: geovar + character(len=MAXVARLEN) :: var_zdir + integer :: nsig + + + do ivar = 1, nvars + ! Get the name of input variable in geovals + geovar = self%geovars%variable(ivar) + call ufo_geovals_get_var(geovals, geovar, profile) + ! Allocate geovals profile if not yet allocated + if (.not. allocated(profile%vals)) then + profile%nlocs = self%nlocs + profile%nval = self%nval + allocate(profile%vals(profile%nval, profile%nlocs)) + profile%vals(:,:) = 0.0_kind_real + endif + if (.not. geovals%linit ) geovals%linit=.true. + + + nsig = self%nval-1 + do iobs = 1, nlocs + call vert_interp_lay_apply_ad(profile%vals(:,iobs), hofx(ivar,iobs), self%coefficients(ivar), self%modelpressures(:,iobs), self%botpressure(iobs), self%toppressure(iobs), nsig) + ! if the geovals come in as top2bottom (logic in traj part of code), make sure to output the adj in the same direction! + if(self%flip_it) profile%vals(1:profile%nval,iobs) = profile%vals(profile%nval:1:-1,iobs) + enddo + enddo + + +end subroutine atmvertinterplay_simobs_ad_ + +! ------------------------------------------------------------------------------ + +subroutine atmvertinterplay_tlad_cleanup_(self) + implicit none + class(ufo_atmvertinterplay_tlad), intent(inout) :: self + self%nval = 0 + self%nlocs = 0 + if (allocated(self%toppressure)) deallocate(self%toppressure) + if (allocated(self%botpressure)) deallocate(self%botpressure) + if (allocated(self%modelpressures)) deallocate(self%modelpressures) +end subroutine atmvertinterplay_tlad_cleanup_ + +! ------------------------------------------------------------------------------ + +subroutine destructor(self) + type(ufo_atmvertinterplay_tlad), intent(inout) :: self + + call self%cleanup() + +end subroutine destructor + +! ------------------------------------------------------------------------------ + +end module ufo_atmvertinterplay_tlad_mod diff --git a/src/ufo/atmvertinterplay/vert_interp_lay_mod.F90 b/src/ufo/atmvertinterplay/vert_interp_lay_mod.F90 new file mode 100644 index 000000000..edf9bb761 --- /dev/null +++ b/src/ufo/atmvertinterplay/vert_interp_lay_mod.F90 @@ -0,0 +1,181 @@ +! (C) Copyright 2018 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to perform linear interpolation + +module vert_interp_lay_mod + +use, intrinsic :: iso_c_binding +use kinds, only: kind_real +use missing_values_mod + +implicit none +public + +contains + +subroutine get_integral_limits(airpressure, botpressure, toppressure, modelpressure, nlevs, nlocs, nsig) +implicit none +integer,intent(in) :: nlevs, nlocs, nsig +real(kind_real) , dimension(nlocs),intent(in) :: airpressure +real(kind_real), dimension(nlocs),intent(inout) :: botpressure,toppressure +real(kind_real), dimension(nsig+1,nlocs),intent(in) :: modelpressure +! local +integer :: nprofs, iobs, iprof, kk, k1, k2 +if (nlevs == 1) then ! total column ozone + do iobs = 1, nlocs + toppressure(iobs) = modelpressure(nsig+1,iobs) + botpressure(iobs) = modelpressure(1,iobs) + enddo +else + !Obs pressures read in as Pa + nprofs = nlocs/nlevs + iobs = 0 + do iprof = 1, nprofs + do kk = 1, nlevs + k1 = kk + k2 = kk - 1 + if (k2 == 0) k2 = 1 + if (kk == nlevs) then + k1 = nlevs - 1 + k2 = 1 + endif + iobs = iobs+1 + toppressure(iobs) = airpressure(k2) + botpressure(iobs) = airpressure(k1) + if( kk == 1 ) then + toppressure(iobs) = modelpressure(nsig+1, iobs) + botpressure(iobs) = airpressure(k1) + if(botpressure(iobs) < modelpressure(nsig+1, iobs)) then + botpressure(iobs) = modelpressure(nsig+1, iobs) + endif + else if( kk == nlevs) then + toppressure(iobs) = modelpressure(nsig+1, iobs) + botpressure(iobs) = modelpressure(1, iobs) + endif + enddo + enddo +endif + +end subroutine get_integral_limits + +real function pindex(nx, press, obspressure) +! This routine handles press (pressure) in decendent order (bottom2top) +! press(1): highest pressure value +! press(nx): lowerest pressure value + +implicit none + +integer :: ix, k, nx +real(kind_real) :: ozp, obspressure, psi +real(kind_real), dimension(nx) :: press + +psi = 1.0_kind_real/press(1) +if(obspressure*psi < 1.) then + ozp = obspressure +else + ozp = press(1) +endif +if( ozp >= press(1)) then + ix = 1 +else + ix = 0 + do k = 1, nx-1 + if(ozp >= press(k)) then + ix = k + exit + endif + enddo + if(ix == 0) ix = nx + if(ix > 1)ix = ix -1 +endif +ozp = float(ix) + & + (ozp-press(ix))/(press(ix+1)-press(ix)) +pindex = ozp +return +end function pindex + +subroutine apply_layer_integral(coefficient, modelozone, modelpressure, botpressure, toppressure, nsig, layer_oz) +implicit none +integer,intent(in) :: nsig +real, intent(in) :: coefficient +real(kind_real),intent(in) :: botpressure, toppressure +real(kind_real), dimension(:), intent(in) :: modelpressure, modelozone +real(kind_real), intent(out) :: layer_oz +! local +integer :: kk, iz1, iz2 +real(kind_real) :: pob,delz,g,delp4,dz1 +real(kind_real) :: topozp, botozp +topozp = pindex(nsig+1, modelpressure, toppressure) +botozp = pindex(nsig+1, modelpressure, botpressure) +pob = botozp +iz1 = topozp +if (iz1>nsig) iz1=nsig +iz2 = pob +layer_oz = 0._kind_real +dz1 = topozp +do kk=iz1,iz2,-1 + delz = 1.0_kind_real + if(kk == iz1) delz = dz1 - iz1 + if (kk == iz2) delz = delz - pob + iz2 + delp4 = modelpressure(kk)-modelpressure(kk+1) ! [Pa] + layer_oz = layer_oz + modelozone(kk)*coefficient*(delz*delp4) +enddo + +end subroutine apply_layer_integral + +subroutine undo_layer_integral(coefficient, modelozone, modelpressure, botpressure, toppressure, nsig, layer_oz) +implicit none +integer,intent(in) :: nsig +real,intent(in) :: coefficient +real(kind_real),intent(in) :: botpressure, toppressure +real(kind_real), dimension(:),intent(in) :: modelpressure +real(kind_real), dimension(:),intent(out):: modelozone +real(kind_real), intent(in) :: layer_oz +! local +integer :: kk, iz1, iz2 +real(kind_real) :: pob,delz,g,delp4,dz1 +real(kind_real) :: topozp, botozp +topozp = pindex(nsig+1, modelpressure, toppressure) +botozp = pindex(nsig+1, modelpressure, botpressure) +pob = botozp +iz1 = topozp +if (iz1>nsig) iz1=nsig +iz2 = pob +dz1 = topozp +modelozone = 0.0_kind_real +do kk=iz1,iz2,-1 + delz = 1.0_kind_real + if(kk == iz1) delz = dz1 - iz1 + if (kk == iz2) delz = delz - pob + iz2 + delp4 = modelpressure(kk)-modelpressure(kk+1) ! [Pa] + modelozone(kk) = modelozone(kk) + layer_oz*coefficient*(delz*delp4) +enddo + +end subroutine undo_layer_integral + + +subroutine vert_interp_lay_apply_tl(modelozoned, layer_ozd, coefficient, modelpressure, botpressure, toppressure, nsig) +real(kind_real), intent(in) ::modelozoned(:) +real(kind_real),intent(in):: botpressure, toppressure +real(kind_real), intent(in), dimension(nsig+1) :: modelpressure +real(kind_real), intent(out) :: layer_ozd +integer,intent(in) :: nsig +real,intent(in) :: coefficient +call apply_layer_integral(coefficient, modelozoned, modelpressure, botpressure, toppressure, nsig, layer_ozd) +end subroutine vert_interp_lay_apply_tl + +subroutine vert_interp_lay_apply_ad(modelozoneb, layer_ozb, coefficient, modelpressure, botpressure, toppressure, nsig) +real(kind_real), intent(out) ::modelozoneb(:) +real(kind_real),intent(in):: botpressure, toppressure +real(kind_real), intent(in), dimension(nsig+1) :: modelpressure +real(kind_real), intent(in) :: layer_ozb +integer,intent(in) :: nsig +real,intent(in) :: coefficient +call undo_layer_integral(coefficient, modelozoneb, modelpressure, botpressure, toppressure, nsig, layer_ozb) +end subroutine vert_interp_lay_apply_ad + + +end module vert_interp_lay_mod diff --git a/src/ufo/avgkernel/CMakeLists.txt b/src/ufo/avgkernel/CMakeLists.txt index 0b33eff01..91a264388 100644 --- a/src/ufo/avgkernel/CMakeLists.txt +++ b/src/ufo/avgkernel/CMakeLists.txt @@ -10,6 +10,11 @@ set ( avgkernel_files ObsAvgKernel.interface.h ufo_avgkernel_mod.F90 ufo_satcolumn_mod.F90 + ObsAvgKernelTLAD.h + ObsAvgKernelTLAD.cc + ObsAvgKernelTLAD.interface.F90 + ObsAvgKernelTLAD.interface.h + ufo_avgkernel_tlad_mod.F90 ) PREPEND( _p_avgkernel_files "avgkernel" ${avgkernel_files} ) diff --git a/src/ufo/avgkernel/ObsAvgKernelTLAD.cc b/src/ufo/avgkernel/ObsAvgKernelTLAD.cc new file mode 100644 index 000000000..1ac5f2d2c --- /dev/null +++ b/src/ufo/avgkernel/ObsAvgKernelTLAD.cc @@ -0,0 +1,80 @@ +/* + * (C) Copyright 2017-2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/avgkernel/ObsAvgKernelTLAD.h" + +#include + +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsBias.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static LinearObsOperatorMaker makerAvgKernelTL_("AvgKernel"); +// ----------------------------------------------------------------------------- + +ObsAvgKernelTLAD::ObsAvgKernelTLAD(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : LinearObsOperatorBase(odb), keyOperAvgKernel_(0), varin_() +{ + ufo_avgkernel_tlad_setup_f90(keyOperAvgKernel_, config, odb.obsvariables(), varin_); + + oops::Log::trace() << "ObsAvgKernelTLAD created" << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsAvgKernelTLAD::~ObsAvgKernelTLAD() { + ufo_avgkernel_tlad_delete_f90(keyOperAvgKernel_); + oops::Log::trace() << "ObsAvgKernelTLAD destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsAvgKernelTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, + ObsDiagnostics &) { + oops::Log::trace() << "ObsAvgKernelTLAD::setTrajectory entering" << std::endl; + + ufo_avgkernel_tlad_settraj_f90(keyOperAvgKernel_, geovals.toFortran(), obsspace()); + + oops::Log::trace() << "ObsAvgKernelTLAD::setTrajectory exiting" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsAvgKernelTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { + ufo_avgkernel_simobs_tl_f90(keyOperAvgKernel_, geovals.toFortran(), obsspace(), + ovec.nvars(), ovec.nlocs(), ovec.toFortran()); + + oops::Log::trace() << "ObsAvgKernelTLAD::simulateObsTL exiting" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsAvgKernelTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { + ufo_avgkernel_simobs_ad_f90(keyOperAvgKernel_, geovals.toFortran(), obsspace(), + ovec.nvars(), ovec.nlocs(), ovec.toFortran()); + + oops::Log::trace() << "ObsAvgKernelTLAD::simulateObsAD exiting" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsAvgKernelTLAD::print(std::ostream & os) const { + os << "ObsAvgKernelTLAD::print not implemented" << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/avgkernel/ObsAvgKernelTLAD.h b/src/ufo/avgkernel/ObsAvgKernelTLAD.h new file mode 100644 index 000000000..cebe035db --- /dev/null +++ b/src/ufo/avgkernel/ObsAvgKernelTLAD.h @@ -0,0 +1,63 @@ +/* + * (C) Copyright 2017-2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_AVGKERNEL_OBSAVGKERNELTLAD_H_ +#define UFO_AVGKERNEL_OBSAVGKERNELTLAD_H_ + +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/avgkernel/ObsAvgKernelTLAD.interface.h" +#include "ufo/LinearObsOperatorBase.h" + +// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsBias; + +// ----------------------------------------------------------------------------- +/// AvgKernel TL/AD observation operator class +class ObsAvgKernelTLAD : public LinearObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsAvgKernelTLAD";} + + ObsAvgKernelTLAD(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsAvgKernelTLAD(); + + // Obs Operators + void setTrajectory(const GeoVaLs &, const ObsBias &, ObsDiagnostics &) override; + void simulateObsTL(const GeoVaLs &, ioda::ObsVector &) const override; + void simulateObsAD(GeoVaLs &, const ioda::ObsVector &) const override; + + // Other + const oops::Variables & requiredVars() const override {return varin_;} + + int & toFortran() {return keyOperAvgKernel_;} + const int & toFortran() const {return keyOperAvgKernel_;} + + private: + void print(std::ostream &) const override; + F90hop keyOperAvgKernel_; + oops::Variables varin_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_AVGKERNEL_OBSAVGKERNELTLAD_H_ diff --git a/src/ufo/avgkernel/ObsAvgKernelTLAD.interface.F90 b/src/ufo/avgkernel/ObsAvgKernelTLAD.interface.F90 new file mode 100644 index 000000000..e36cff295 --- /dev/null +++ b/src/ufo/avgkernel/ObsAvgKernelTLAD.interface.F90 @@ -0,0 +1,133 @@ +! (C) Copyright 2017-2020 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran avgkernel module for functions on the interface between C++ and Fortran +! to handle tl/ad observation operators + +module ufo_avgkernel_tlad_mod_c + + use iso_c_binding + use fckit_configuration_module, only: fckit_configuration + use ufo_avgkernel_tlad_mod + use ufo_geovals_mod_c, only: ufo_geovals_registry + use ufo_geovals_mod, only: ufo_geovals + implicit none + + private + +#define LISTED_TYPE ufo_avgkernel_tlad + + !> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + + !> Global registry + type(registry_t) :: ufo_avgkernel_tlad_registry + +contains + ! ------------------------------------------------------------------------------ + !> Linked list implementation +#include "oops/util/linkedList_c.f" + +! ------------------------------------------------------------------------------ + +subroutine ufo_avgkernel_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_avgkernel_tlad_setup_f90') +use oops_variables_mod +implicit none +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), value, intent(in) :: c_conf +type(c_ptr), value, intent(in) :: c_obsvars ! variables to be simulated +type(c_ptr), value, intent(in) :: c_geovars ! variables requested from the model + +type(ufo_avgkernel_tlad), pointer :: self +type(fckit_configuration) :: f_conf + +call ufo_avgkernel_tlad_registry%setup(c_key_self, self) +f_conf = fckit_configuration(c_conf) + +self%obsvars = oops_variables(c_obsvars) +self%geovars = oops_variables(c_geovars) +call self%setup(f_conf) + +end subroutine ufo_avgkernel_tlad_setup_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_avgkernel_tlad_delete_c(c_key_self) bind(c,name='ufo_avgkernel_tlad_delete_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self + +type(ufo_avgkernel_tlad), pointer :: self + +call ufo_avgkernel_tlad_registry%delete(c_key_self, self) + +end subroutine ufo_avgkernel_tlad_delete_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_avgkernel_tlad_settraj_c(c_key_self, c_key_geovals, c_obsspace) bind(c,name='ufo_avgkernel_tlad_settraj_f90') +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace + +type(ufo_avgkernel_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +character(len=*), parameter :: myname_="ufo_avgkernel_tlad_settraj_c" +call ufo_avgkernel_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) +call self%settraj(geovals, c_obsspace) + +end subroutine ufo_avgkernel_tlad_settraj_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_avgkernel_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, & + c_hofx) bind(c,name='ufo_avgkernel_simobs_tl_f90') +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nvars, c_nlocs +real(c_double), intent(inout) :: c_hofx(c_nvars, c_nlocs) + +type(ufo_avgkernel_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +character(len=*), parameter :: myname_="ufo_avgkernel_simobs_tl_c" + +call ufo_avgkernel_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) + +call self%simobs_tl(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) + +end subroutine ufo_avgkernel_simobs_tl_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_avgkernel_simobs_ad_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, & + c_hofx) bind(c,name='ufo_avgkernel_simobs_ad_f90') +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nvars, c_nlocs +real(c_double), intent(in) :: c_hofx(c_nvars, c_nlocs) + +type(ufo_avgkernel_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +character(len=*), parameter :: myname_="ufo_avgkernel_simobs_ad_c" + +call ufo_avgkernel_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) + +call self%simobs_ad(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) + +end subroutine ufo_avgkernel_simobs_ad_c + +! ------------------------------------------------------------------------------ + +end module ufo_avgkernel_tlad_mod_c diff --git a/src/ufo/avgkernel/ObsAvgKernelTLAD.interface.h b/src/ufo/avgkernel/ObsAvgKernelTLAD.interface.h new file mode 100644 index 000000000..4c25b86df --- /dev/null +++ b/src/ufo/avgkernel/ObsAvgKernelTLAD.interface.h @@ -0,0 +1,36 @@ +/* + * (C) Copyright 2017-2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_AVGKERNEL_OBSAVGKERNELTLAD_INTERFACE_H_ +#define UFO_AVGKERNEL_OBSAVGKERNELTLAD_INTERFACE_H_ + +#include "ioda/ObsSpace.h" +#include "oops/base/Variables.h" +#include "ufo/Fortran.h" + +namespace ufo { + +/// Interface to Fortran UFO avgkernel routines + +extern "C" { + +// ----------------------------------------------------------------------------- + + void ufo_avgkernel_tlad_setup_f90(F90hop &, const eckit::Configuration &, + const oops::Variables &, oops::Variables &); + void ufo_avgkernel_tlad_delete_f90(F90hop &); + void ufo_avgkernel_tlad_settraj_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &); + void ufo_avgkernel_simobs_tl_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + const int &, const int &, double &); + void ufo_avgkernel_simobs_ad_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + const int &, const int &, const double &); +// ----------------------------------------------------------------------------- + +} // extern C + +} // namespace ufo +#endif // UFO_AVGKERNEL_OBSAVGKERNELTLAD_INTERFACE_H_ diff --git a/src/ufo/avgkernel/ufo_avgkernel_mod.F90 b/src/ufo/avgkernel/ufo_avgkernel_mod.F90 index cea803849..972ae5700 100644 --- a/src/ufo/avgkernel/ufo_avgkernel_mod.F90 +++ b/src/ufo/avgkernel/ufo_avgkernel_mod.F90 @@ -9,6 +9,7 @@ module ufo_avgkernel_mod use oops_variables_mod use ufo_vars_mod + use missing_values_mod use kinds use iso_c_binding @@ -45,6 +46,7 @@ subroutine ufo_avgkernel_setup(self, f_conf) integer :: nlevs_yaml integer :: ivar, nvars character(len=max_string) :: err_msg + character(len=:), allocatable :: str_array(:) ! get configuration for the averaging kernel operator call f_conf%get_or_die("obs options",f_confOpts) @@ -74,13 +76,19 @@ subroutine ufo_avgkernel_setup(self, f_conf) ! get name of geoval/tracer to use from the model nvars = self%obsvars%nvars() - call f_confOpts%get_or_die("tracer variables", self%tracervars) + call f_confOpts%get_or_die("tracer variables", str_array) + self%tracervars = str_array ! determine if this is a total column or troposphere calculation - ! support stratosphere, etc. later? if (.not. f_confOpts%get("tropospheric column", self%troposphere)) self%troposphere = .false. if (.not. f_confOpts%get("total column", self%totalcolumn)) self%totalcolumn = .false. + ! both of these cannot be true + if (self%troposphere .and. self%totalcolumn) then + write(err_msg, *) "ufo_avgkernel_setup error: both tropospheric and total column set to TRUE, only one can be TRUE" + call abor1_ftn(err_msg) + end if + ! do we need a conversion factor, say between ppmv and unity? if (.not. f_confOpts%get("model units coeff", self%convert_factor_model)) self%convert_factor_model = one if (.not. f_confOpts%get("hofx units coeff", self%convert_factor_hofx)) self%convert_factor_hofx = one @@ -115,7 +123,7 @@ subroutine ufo_avgkernel_simobs(self, geovals_in, obss, nvars, nlocs, hofx) use kinds use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var, & ufo_geovals_reorderzdir, ufo_geovals_copy - use ufo_constants_mod, only: zero, half + use ufo_constants_mod, only: half use satcolumn_mod, only: simulate_column_ob use iso_c_binding use obsspace_mod @@ -136,6 +144,9 @@ subroutine ufo_avgkernel_simobs(self, geovals_in, obss, nvars, nlocs, hofx) integer, allocatable, dimension(:) :: troplev_obs real(kind_real) :: hofx_tmp type(ufo_geovals) :: geovals + real(c_double) :: missing + + missing = missing_value(missing) ! get geovals of atmospheric pressure call ufo_geovals_copy(geovals_in, geovals) ! dont want to change geovals_in @@ -183,16 +194,22 @@ subroutine ufo_avgkernel_simobs(self, geovals_in, obss, nvars, nlocs, hofx) geovar = self%tracervars(ivar) call ufo_geovals_get_var(geovals, geovar, tracer) do iobs = 1, nlocs - if (avgkernel_obs(1,iobs) > -1.0e9_kind_real) then ! take care of missing obs + if (avgkernel_obs(1,iobs) /= missing) then ! take care of missing obs if (self%troposphere) then call simulate_column_ob(self%nlayers_kernel, tracer%nval, avgkernel_obs(:,iobs), & prsl_obs(:,iobs), prsl%vals(:,iobs), temp%vals(:,iobs),& phii%vals(:,iobs), tracer%vals(:,iobs)*self%convert_factor_model, & hofx_tmp, troplev_obs(iobs), airmass_tot(iobs), airmass_trop(iobs)) hofx(ivar,iobs) = hofx_tmp * self%convert_factor_hofx + else if (self%totalcolumn) then + call simulate_column_ob(self%nlayers_kernel, tracer%nval, avgkernel_obs(:,iobs), & + prsl_obs(:,iobs), prsl%vals(:,iobs), temp%vals(:,iobs),& + phii%vals(:,iobs), tracer%vals(:,iobs)*self%convert_factor_model, & + hofx_tmp) + hofx(ivar,iobs) = hofx_tmp * self%convert_factor_hofx end if else - hofx(ivar,iobs) = zero ! default if we are unable to compute averaging kernel + hofx(ivar,iobs) = missing ! default if we are unable to compute averaging kernel end if end do end do diff --git a/src/ufo/avgkernel/ufo_avgkernel_tlad_mod.F90 b/src/ufo/avgkernel/ufo_avgkernel_tlad_mod.F90 new file mode 100644 index 000000000..77c0267b5 --- /dev/null +++ b/src/ufo/avgkernel/ufo_avgkernel_tlad_mod.F90 @@ -0,0 +1,346 @@ +! (C) Copyright 2017-2020 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module for avgkernel tl/ad observation operator + +module ufo_avgkernel_tlad_mod + use oops_variables_mod + use ufo_vars_mod + use ufo_geovals_mod + use missing_values_mod + use kinds + use iso_c_binding + + implicit none + private + integer, parameter :: max_string=800 + +! ------------------------------------------------------------------------------ + + type, public :: ufo_avgkernel_tlad + private + type(oops_variables), public :: obsvars + type(oops_variables), public :: geovars + integer :: nlayers_kernel + integer :: nlocs, nvars, nval + real(kind_real), allocatable, dimension(:) :: ak_kernel, bk_kernel + character(kind=c_char,len=:), allocatable :: obskernelvar, tracervars(:) + logical :: troposphere, totalcolumn + real(kind_real) :: convert_factor_model, convert_factor_hofx + real(kind_real), allocatable, dimension(:,:) :: avgkernel_obs, prsl_obs + real(kind_real), allocatable, dimension(:,:) :: prsl, temp, phii + real(kind_real), allocatable, dimension(:) :: airmass_tot, airmass_trop + integer, allocatable, dimension(:) :: troplev_obs + logical :: flip_it + contains + procedure :: setup => avgkernel_tlad_setup_ + procedure :: cleanup => avgkernel_tlad_cleanup_ + procedure :: settraj => avgkernel_tlad_settraj_ + procedure :: simobs_tl => avgkernel_simobs_tl_ + procedure :: simobs_ad => avgkernel_simobs_ad_ + final :: destructor + end type ufo_avgkernel_tlad + +contains + +! ------------------------------------------------------------------------------ +subroutine avgkernel_tlad_setup_(self, f_conf) + use fckit_configuration_module, only: fckit_configuration + use ufo_constants_mod, only: one + implicit none + class(ufo_avgkernel_tlad), intent(inout) :: self + type(fckit_configuration), intent(in) :: f_conf + type(fckit_configuration) :: f_confOpts + integer :: nlevs_yaml + integer :: ivar, nvars + character(len=max_string) :: err_msg + character(len=:), allocatable :: str_array(:) + + ! get configuration for the averaging kernel operator + call f_conf%get_or_die("obs options",f_confOpts) + + call f_confOpts%get_or_die("nlayers_kernel", self%nlayers_kernel) + nlevs_yaml = f_confOpts%get_size("ak") + if (nlevs_yaml /= self%nlayers_kernel+1) then + write(err_msg, *) "ufo_avgkernel_tlad_setup error: YAML nlayers_kernel != size of ak array" + call abor1_ftn(err_msg) + end if + nlevs_yaml = f_confOpts%get_size("bk") + if (nlevs_yaml /= self%nlayers_kernel+1) then + write(err_msg, *) "ufo_avgkernel_tlad_setup error: YAML nlayers_kernel != size of bk array" + call abor1_ftn(err_msg) + end if + + ! allocate and read ak/bk for the averaging kernel + allocate(self%ak_kernel(nlevs_yaml)) + allocate(self%bk_kernel(nlevs_yaml)) + call f_confOpts%get_or_die("ak", self%ak_kernel) + call f_confOpts%get_or_die("bk", self%bk_kernel) + + ! get variable name from IODA for observation averaging kernel + if (.not. f_confOpts%get("AvgKernelVar", self%obskernelvar)) then + self%obskernelvar = "averaging_kernel" ! default option + end if + + ! get name of geoval/tracer to use from the model + nvars = self%obsvars%nvars() + call f_confOpts%get_or_die("tracer variables", str_array) + self%tracervars = str_array + + ! determine if this is a total column or troposphere calculation + if (.not. f_confOpts%get("tropospheric column", self%troposphere)) self%troposphere = .false. + if (.not. f_confOpts%get("total column", self%totalcolumn)) self%totalcolumn = .false. + + ! both of these cannot be true + if (self%troposphere .and. self%totalcolumn) then + write(err_msg, *) "ufo_avgkernel_tlad_setup error: both tropospheric and total column set to TRUE, only one can be TRUE" + call abor1_ftn(err_msg) + end if + + ! do we need a conversion factor, say between ppmv and unity? + if (.not. f_confOpts%get("model units coeff", self%convert_factor_model)) self%convert_factor_model = one + if (.not. f_confOpts%get("hofx units coeff", self%convert_factor_hofx)) self%convert_factor_hofx = one + + ! add variables to geovars that are needed + ! specified tracers + do ivar = 1, nvars + call self%geovars%push_back(self%tracervars(ivar)) + end do + +end subroutine avgkernel_tlad_setup_ + +! ------------------------------------------------------------------------------ +subroutine destructor(self) + implicit none + type(ufo_avgkernel_tlad), intent(inout) :: self + + call self%cleanup() + +end subroutine destructor + +! ------------------------------------------------------------------------------ +subroutine avgkernel_tlad_settraj_(self, geovals_in, obss) + use iso_c_binding + use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var + use ufo_constants_mod, only: half + use obsspace_mod + implicit none + class(ufo_avgkernel_tlad), intent(inout) :: self + type(ufo_geovals), intent(in) :: geovals_in + type(c_ptr), value, intent(in) :: obss + + ! Local variables + type(ufo_geoval), pointer :: prsi, psfc + integer :: ivar, iobs, ilev + character(len=MAXVARLEN) :: varstring + character(len=4) :: levstr + real(kind_real), allocatable, dimension(:,:) :: prsi_obs + type(ufo_geovals) :: geovals + type(ufo_geoval), pointer :: prsl, temp, phii, p_temp + + ! get nlocs and nvars + self%nlocs = obsspace_get_nlocs(obss) + self%nvars = obsspace_get_nvars(obss) + + ! get geovals of atmospheric pressure + call ufo_geovals_copy(geovals_in, geovals) ! dont want to change geovals_in + call ufo_geovals_get_var(geovals, var_prs, p_temp) + if (p_temp%vals(1,1) < p_temp%vals(p_temp%nval,1) ) then + self%flip_it = .true. + else + self%flip_it = .false. + end if + call ufo_geovals_reorderzdir(geovals, var_prs, "bottom2top") + call ufo_geovals_get_var(geovals, var_ps, psfc) + call ufo_geovals_get_var(geovals, var_prs, prsl) + call ufo_geovals_get_var(geovals, var_prsi, prsi) + call ufo_geovals_get_var(geovals, var_ts, temp) + call ufo_geovals_get_var(geovals, var_zi, phii) + + ! get nval + self%nval = prsl%nval + + allocate(self%prsl(self%nval, self%nlocs)) + allocate(self%temp(self%nval, self%nlocs)) + allocate(self%phii(self%nval+1, self%nlocs)) + do iobs = 1, self%nlocs + self%prsl(:,iobs) = prsl%vals(:,iobs) + self%temp(:,iobs) = temp%vals(:,iobs) + self%phii(:,iobs) = phii%vals(:,iobs) + end do + + ! grab necesary metadata from IODA + ! get observation averaging kernel + ! once 2D arrays are allowed, rewrite/simplify this part + allocate(self%avgkernel_obs(self%nlayers_kernel, self%nlocs)) + do ilev = 1, self%nlayers_kernel + write(levstr, fmt = "(I3)") ilev + levstr = adjustl(levstr) + varstring = trim(self%obskernelvar)//"_"//trim(levstr) + call obsspace_get_db(obss, "MetaData", trim(varstring), self%avgkernel_obs(ilev, :)) + end do + + ! compute prsl_obs/prsi_obs from ak/bk/psfc + allocate(self%prsl_obs(self%nlayers_kernel, self%nlocs)) + allocate(prsi_obs(self%nlayers_kernel+1, self%nlocs)) + ! prsi_obs calculation + do ilev = 1, self%nlayers_kernel+1 + prsi_obs(ilev,:) = self%ak_kernel(ilev) + self%bk_kernel(ilev) * psfc%vals(1,:) + end do + ! using simple averaging for now for prsl, can use more complex way later + do ilev = 1, self%nlayers_kernel + self%prsl_obs(ilev,:) = (prsi_obs(ilev,:) + prsi_obs(ilev+1,:)) * half + end do + + if (self%troposphere) then + allocate(self%troplev_obs(self%nlocs)) + allocate(self%airmass_trop(self%nlocs)) + allocate(self%airmass_tot(self%nlocs)) + call obsspace_get_db(obss, "MetaData", "troposphere_layer_index", self%troplev_obs) + call obsspace_get_db(obss, "MetaData", "air_mass_factor_troposphere", self%airmass_trop) + call obsspace_get_db(obss, "MetaData", "air_mass_factor_total", self%airmass_tot) + end if + + ! cleanup things we do not need now + deallocate(prsi_obs) + +end subroutine avgkernel_tlad_settraj_ + +! ------------------------------------------------------------------------------ +subroutine avgkernel_simobs_tl_(self, geovals_in, obss, nvars, nlocs, hofx) + use iso_c_binding + use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var + use obsspace_mod + use satcolumn_mod, only: simulate_column_ob_tl + implicit none + class(ufo_avgkernel_tlad), intent(in) :: self + type(ufo_geovals), intent(in) :: geovals_in + integer, intent(in) :: nvars, nlocs + real(c_double), intent(inout) :: hofx(nvars, nlocs) + type(c_ptr), value, intent(in) :: obss + type(ufo_geoval), pointer :: tracer + integer :: ivar, iobs + character(len=MAXVARLEN) :: geovar + real(kind_real) :: hofx_tmp + type(ufo_geovals) :: geovals + real(c_double) :: missing + + missing = missing_value(missing) + + call ufo_geovals_copy(geovals_in, geovals) ! dont want to change geovals_in + + ! loop through all variables + do ivar = 1, nvars + geovar = self%tracervars(ivar) + call ufo_geovals_get_var(geovals, geovar, tracer) + do iobs = 1, nlocs + if (self%avgkernel_obs(1,iobs) /= missing) then ! take care of missing obs + if(self%flip_it) tracer%vals(1:tracer%nval,iobs) = tracer%vals(tracer%nval:1:-1,iobs) + if (self%troposphere) then + call simulate_column_ob_tl(self%nlayers_kernel, tracer%nval, self%avgkernel_obs(:,iobs), & + self%prsl_obs(:,iobs), self%prsl(:,iobs), self%temp(:,iobs),& + self%phii(:,iobs), tracer%vals(:,iobs)*self%convert_factor_model, & + hofx_tmp, self%troplev_obs(iobs), self%airmass_tot(iobs), self%airmass_trop(iobs)) + hofx(ivar,iobs) = hofx_tmp * self%convert_factor_hofx + else if (self%totalcolumn) then + call simulate_column_ob_tl(self%nlayers_kernel, tracer%nval, self%avgkernel_obs(:,iobs), & + self%prsl_obs(:,iobs), self%prsl(:,iobs), self%temp(:,iobs),& + self%phii(:,iobs), tracer%vals(:,iobs)*self%convert_factor_model, & + hofx_tmp) + hofx(ivar,iobs) = hofx_tmp * self%convert_factor_hofx + end if + else + hofx(ivar,iobs) = missing + end if + end do + end do + call ufo_geovals_delete(geovals) + +end subroutine avgkernel_simobs_tl_ + +! ------------------------------------------------------------------------------ +subroutine avgkernel_simobs_ad_(self, geovals_in, obss, nvars, nlocs, hofx) + use iso_c_binding + use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var + use satcolumn_mod, only: simulate_column_ob_ad + use obsspace_mod + implicit none + class(ufo_avgkernel_tlad), intent(in) :: self + type(ufo_geovals), intent(inout) :: geovals_in + integer, intent(in) :: nvars, nlocs + real(c_double), intent(in) :: hofx(nvars, nlocs) + type(c_ptr), value, intent(in) :: obss + type(ufo_geoval), pointer :: tracer + character(len=MAXVARLEN) :: geovar + type(ufo_geovals) :: geovals + real(kind_real) :: hofx_tmp + integer :: ivar, iobs + real(c_double) :: missing + + missing = missing_value(missing) + + if (.not. geovals_in%linit ) geovals_in%linit=.true. ! need this for var exe + + ! loop through all variables + do ivar = 1, nvars + geovar = self%tracervars(ivar) + call ufo_geovals_get_var(geovals_in, geovar, tracer) + + ! Allocate geovals profile if not yet allocated + if (.not. allocated(tracer%vals)) then + tracer%nlocs = self%nlocs + tracer%nval = self%nval + allocate(tracer%vals(tracer%nval, tracer%nlocs)) + tracer%vals(:,:) = 0.0_kind_real + endif + + do iobs = 1, nlocs + if (hofx(ivar,iobs) /= missing) then ! take care of missing obs + if (self%troposphere) then + hofx_tmp = hofx(ivar,iobs) * self%convert_factor_hofx + call simulate_column_ob_ad(self%nlayers_kernel, tracer%nval, self%avgkernel_obs(:,iobs), & + self%prsl_obs(:,iobs), self%prsl(:,iobs), self%temp(:,iobs),& + self%phii(:,iobs), tracer%vals(:,iobs), & + hofx_tmp, self%troplev_obs(iobs), self%airmass_tot(iobs), self%airmass_trop(iobs)) + tracer%vals(:,iobs) = tracer%vals(:,iobs) * self%convert_factor_model + else if (self%totalcolumn) then + hofx_tmp = hofx(ivar,iobs) * self%convert_factor_hofx + call simulate_column_ob_ad(self%nlayers_kernel, tracer%nval, self%avgkernel_obs(:,iobs), & + self%prsl_obs(:,iobs), self%prsl(:,iobs), self%temp(:,iobs),& + self%phii(:,iobs), tracer%vals(:,iobs), hofx_tmp) + tracer%vals(:,iobs) = tracer%vals(:,iobs) * self%convert_factor_model + end if + end if + if(self%flip_it) tracer%vals(1:tracer%nval,iobs) = tracer%vals(tracer%nval:1:-1,iobs) + end do + end do + +end subroutine avgkernel_simobs_ad_ + +! ------------------------------------------------------------------------------ +subroutine avgkernel_tlad_cleanup_(self) + implicit none + class(ufo_avgkernel_tlad), intent(inout) :: self + self%nvars = 0 + self%nlocs = 0 + self%nval = 0 + ! deallocate things + if (allocated(self%ak_kernel)) deallocate(self%ak_kernel) + if (allocated(self%bk_kernel)) deallocate(self%bk_kernel) + if (allocated(self%obskernelvar)) deallocate(self%obskernelvar) + if (allocated(self%tracervars)) deallocate(self%tracervars) + if (allocated(self%avgkernel_obs)) deallocate(self%avgkernel_obs) + if (allocated(self%prsl_obs)) deallocate(self%prsl_obs) + if (allocated(self%prsl)) deallocate(self%prsl) + if (allocated(self%temp)) deallocate(self%temp) + if (allocated(self%phii)) deallocate(self%phii) + if (allocated(self%airmass_tot)) deallocate(self%airmass_tot) + if (allocated(self%airmass_trop)) deallocate(self%airmass_trop) + if (allocated(self%troplev_obs)) deallocate(self%troplev_obs) +end subroutine avgkernel_tlad_cleanup_ + +! ------------------------------------------------------------------------------ + +end module ufo_avgkernel_tlad_mod diff --git a/src/ufo/avgkernel/ufo_satcolumn_mod.F90 b/src/ufo/avgkernel/ufo_satcolumn_mod.F90 index b338a686b..9a712d416 100644 --- a/src/ufo/avgkernel/ufo_satcolumn_mod.F90 +++ b/src/ufo/avgkernel/ufo_satcolumn_mod.F90 @@ -70,4 +70,142 @@ subroutine simulate_column_ob(nlayers_obs, nlayers_model, avgkernel_obs, & end subroutine simulate_column_ob +subroutine simulate_column_ob_tl(nlayers_obs, nlayers_model, avgkernel_obs, & + prsl_obs, prsl_model, temp_model, zi_model, & + profile_model, hofx, & + troplev_obs, airmass_tot, airmass_trop) + use kinds + use ufo_constants_mod, only: zero, gas_constant, avogadro + use vert_interp_mod, only: vert_interp_weights, vert_interp_apply_tl + implicit none + integer, intent(in ) :: nlayers_obs, nlayers_model + real(kind_real), intent(in ), dimension(nlayers_obs) :: avgkernel_obs + real(kind_real), intent(in ), dimension(nlayers_obs) :: prsl_obs + real(kind_real), intent(in ), dimension(nlayers_model) :: prsl_model + real(kind_real), intent(in ), dimension(nlayers_model) :: profile_model + real(kind_real), intent(in ), dimension(nlayers_model) :: temp_model + real(kind_real), intent(in ), dimension(nlayers_model+1) :: zi_model + real(kind_real), intent( out) :: hofx + real(kind_real), intent(in ), optional :: airmass_tot, airmass_trop + integer, intent(in ), optional :: troplev_obs + real(kind_real) :: airmass_ratio, lnp_ob, wf, dz + real(kind_real), dimension(nlayers_obs) :: avgkernel_use + real(kind_real), dimension(nlayers_model) :: lnp_model, profile_model_use + real(kind_real), dimension(nlayers_obs) :: profile_obslayers + integer :: k, wi + logical :: troposphere + + troposphere = .false. + ! determine if we are computing a total column or just tropospheric column + if (present(airmass_tot) .and. present(airmass_trop) .and. present(troplev_obs)) then + troposphere = .true. + end if + + if (troposphere) then + ! compute air mass ratio and apply it to the averaging kernel + airmass_ratio = airmass_tot / airmass_trop + avgkernel_use = avgkernel_obs * airmass_ratio + + ! set all layers to zero above tropopause layer + avgkernel_use(troplev_obs+1:nlayers_obs) = zero + else + avgkernel_use = avgkernel_obs + end if + + ! need to convert from kg/kg to molec/m3 for each layer's T and P + profile_model_use = profile_model * ((avogadro*prsl_model)/(temp_model*gas_constant)) + + ! need to convert from molec/m3 to molec/cm2 to sum later + do k=1,nlayers_model + dz = (zi_model(k+1) - zi_model(k)) + profile_model_use(k) = profile_model_use(k) * dz ! convert to molec/m2 + profile_model_use(k) = profile_model_use(k) / 1.0e4_kind_real ! to molec/cm2 + end do + + ! need to interpolate model profile layers to observation layers + lnp_model = log(prsl_model) + do k=1,nlayers_obs + lnp_ob = log(prsl_obs(k)) + call vert_interp_weights(nlayers_model, lnp_ob, lnp_model, wi, wf) + call vert_interp_apply_tl(nlayers_model, profile_model_use, & + profile_obslayers(k), wi, wf) + end do + + ! compute hofx as column integral + hofx = zero + do k=1,nlayers_obs + hofx = hofx + (avgkernel_use(k) * profile_obslayers(k)) + end do + + end subroutine simulate_column_ob_tl + +subroutine simulate_column_ob_ad(nlayers_obs, nlayers_model, avgkernel_obs, & + prsl_obs, prsl_model, temp_model, zi_model, & + profile_model_ad, hofx_ad, & + troplev_obs, airmass_tot, airmass_trop) + use kinds + use ufo_constants_mod, only: zero, gas_constant, avogadro + use vert_interp_mod, only: vert_interp_weights, vert_interp_apply_ad, vert_interp_apply_tl + implicit none + integer, intent(in ) :: nlayers_obs, nlayers_model + real(kind_real), intent(in ), dimension(nlayers_obs) :: avgkernel_obs + real(kind_real), intent(in ), dimension(nlayers_obs) :: prsl_obs + real(kind_real), intent(in ), dimension(nlayers_model) :: prsl_model + real(kind_real), intent(inout), dimension(nlayers_model) :: profile_model_ad + real(kind_real), intent(in ), dimension(nlayers_model) :: temp_model + real(kind_real), intent(in ), dimension(nlayers_model+1) :: zi_model + real(kind_real), intent(in ) :: hofx_ad + real(kind_real), intent(in ), optional :: airmass_tot, airmass_trop + integer, intent(in ), optional :: troplev_obs + real(kind_real) :: airmass_ratio, lnp_ob, wf, dz + real(kind_real), dimension(nlayers_obs) :: avgkernel_use + real(kind_real), dimension(nlayers_model) :: lnp_model, profile_model_use_ad + real(kind_real), dimension(nlayers_obs) :: profile_obslayers_ad + integer :: k, wi + logical :: troposphere + integer :: nlevs + + troposphere = .false. + ! determine if we are computing a total column or just tropospheric column + if (present(airmass_tot) .and. present(airmass_trop) .and. present(troplev_obs)) then + troposphere = .true. + end if + + if (troposphere) then + ! compute air mass ratio and apply it to the averaging kernel + airmass_ratio = airmass_tot / airmass_trop + avgkernel_use = avgkernel_obs * airmass_ratio + + ! set all layers to zero above tropopause layer + avgkernel_use(troplev_obs+1:nlayers_obs) = zero + else + avgkernel_use = avgkernel_obs + end if + + profile_obslayers_ad = zero + do k=nlayers_obs,1,-1 + profile_obslayers_ad(k) = profile_obslayers_ad(k) + avgkernel_use(k) * hofx_ad + end do + + profile_model_use_ad = zero + ! need to interpolate model profile layers to observation layers + lnp_model = log(prsl_model) + do k=nlayers_obs,1,-1 + lnp_ob = log(prsl_obs(k)) + call vert_interp_weights(nlayers_model, lnp_ob, lnp_model, wi, wf) + call vert_interp_apply_ad(nlayers_model, profile_model_use_ad, & + profile_obslayers_ad(k), wi, wf) + end do + + ! adjoint of unit conversions + do k=nlayers_model,1,-1 + dz = (zi_model(k+1) - zi_model(k)) + profile_model_use_ad(k) = profile_model_use_ad(k) / 1.0e4_kind_real ! to molec/m2 + profile_model_use_ad(k) = profile_model_use_ad(k) * dz ! convert to molec/m3 + end do + + profile_model_ad = profile_model_use_ad * ((avogadro*prsl_model)/(temp_model*gas_constant)) + + end subroutine simulate_column_ob_ad + end module satcolumn_mod diff --git a/src/ufo/backgrounderroridentity/CMakeLists.txt b/src/ufo/backgrounderroridentity/CMakeLists.txt new file mode 100644 index 000000000..8605c76c0 --- /dev/null +++ b/src/ufo/backgrounderroridentity/CMakeLists.txt @@ -0,0 +1,22 @@ +# (C) Copyright 2021 UK Met Office +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( backgrounderroridentity_files + ObsBackgroundErrorIdentity.cc + ObsBackgroundErrorIdentity.h + ObsBackgroundErrorIdentity.interface.F90 + ObsBackgroundErrorIdentity.interface.h + ufo_backgrounderroridentity_mod.F90 + ) + +PREPEND( _p_backgrounderroridentity_files + "backgrounderroridentity" + ${backgrounderroridentity_files} + ) + +set ( backgrounderroridentity_src_files + ${_p_backgrounderroridentity_files} + PARENT_SCOPE + ) diff --git a/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.cc b/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.cc new file mode 100644 index 000000000..b454945a6 --- /dev/null +++ b/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.cc @@ -0,0 +1,81 @@ +/* + * (C) Copyright 2021 UK Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.h" + +#include + +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/DateTime.h" +#include "oops/util/Duration.h" +#include "oops/util/Logger.h" + +#include "ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.interface.h" +#include "ufo/GeoVaLs.h" +#include "ufo/Locations.h" +#include "ufo/ObsDiagnostics.h" +#include "ufo/ObsOperatorBase.h" + +namespace ufo { + +static ObsOperatorMaker maker("BackgroundErrorIdentity"); + +ObsBackgroundErrorIdentity::ObsBackgroundErrorIdentity(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : ObsOperatorBase(odb, config), + odb_(odb) +{ + oops::Log::trace() << "ObsBackgroundErrorIdentity constructor entered" << std::endl; + + parameters_.validateAndDeserialize(config); + + // simulateObs() may be asked to interpolate the background errors of any simulated variables. + // We need to assume the worst, i.e. that we'll need to interpolate all of them. + const oops::Variables &obsvars = odb.obsvariables(); + for (size_t ivar = 0; ivar < obsvars.size(); ++ivar) + requiredVars_.push_back(obsvars[ivar] + "_background_error"); + + oops::Log::trace() << "ObsBackgroundErrorIdentity created" << std::endl; +} + +ObsBackgroundErrorIdentity::~ObsBackgroundErrorIdentity() { + oops::Log::trace() << "ObsBackgroundErrorIdentity destructed" << std::endl; +} + +void ObsBackgroundErrorIdentity::simulateObs(const GeoVaLs & geovals, ioda::ObsVector & hofx, + ObsDiagnostics & ydiags) const { + oops::Log::trace() << "ObsBackgroundErrorIdentity: simulateObs entered" << std::endl; + + oops::Variables variables; + if (parameters_.variables.value() != boost::none) + for (const Variable &variable : *parameters_.variables.value()) + variables += variable.toOopsVariables(); + else + variables = odb_.obsvariables(); + + ufo_backgrounderroridentity_fillobsdiags_f90(geovals.toFortran(), hofx.nlocs(), variables, + ydiags.toFortran()); + + oops::Log::trace() << "ObsBackgroundErrorIdentity: simulateObs exit" << std::endl; +} + +const oops::Variables & ObsBackgroundErrorIdentity::requiredVars() const { + return requiredVars_; +} + +oops::Variables ObsBackgroundErrorIdentity::simulatedVars() const { + // This operator doesn't simulate any variables -- it only produces diagnostics. + return oops::Variables(); +} + +void ObsBackgroundErrorIdentity::print(std::ostream & os) const { + os << "ObsBackgroundErrorIdentity: config = " << parameters_ << std::endl; +} + +} // namespace ufo diff --git a/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.h b/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.h new file mode 100644 index 000000000..4e9441dd4 --- /dev/null +++ b/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.h @@ -0,0 +1,108 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_BACKGROUNDERRORIDENTITY_OBSBACKGROUNDERRORIDENTITY_H_ +#define UFO_BACKGROUNDERRORIDENTITY_OBSBACKGROUNDERRORIDENTITY_H_ + +#include +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" + +#include "ufo/filters/Variable.h" +#include "ufo/ObsOperatorBase.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +/// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class Locations; + class ObsDiagnostics; + +/// \brief Options controlling the ObsBackgroundErrorIdentity observation operator. +class ObsBackgroundErrorIdentityParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsBackgroundErrorIdentityParameters, Parameters) + + public: + /// Name of the ObsOperator. Must be BackgroundErrorIdentity. + /// + /// TODO(wsmigaj): create an ObsOperatorParametersBase class, move this parameter there and + /// derive ObsBackgroundErrorIdentityParameters from that class. + oops::Parameter name{"name", "", this}; + + /// Simulated variables whose background errors may be calculated by this operator. + /// If not specified, defaults to the list of all simulated variables in the ObsSpace. + oops::OptionalParameter> variables{"variables", this}; +}; + +/// \brief An observation operator calculating ObsDiagnostics representing single-level +/// background errors of simulated variables. +/// +/// It should be used as a component of the `Composite` observation operator (with another +/// component handling the calculation of model equivalents of observation). It populates all +/// requested ObsDiagnostics called `_background_error`, where `` is the name of a +/// simulated variable, by copying the `_background_error` GeoVaL at the observation +/// locations. +/// +/// If the `variables` option is present, the operator does not calculate the background errors +/// of all simulated variables in the ObsSpace, but only those listed in the `variables` option. +/// +/// See ObsBackgroundErrorIdentityParameters for the description of YAML configuration options +/// accepted by this operator. +/// +/// Example configuration: +/// +/// obs operator: +/// name: Composite +/// components: +/// # operator used to evaluate H(x) +/// - name: identity +/// vertical coordinate: geopotential_height # coordinate used for obs value interpolation +/// # operator used to evaluate background errors +/// - name: BackgroundErrorIdentity +class ObsBackgroundErrorIdentity : public ObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsBackgroundErrorIdentity";} + + ObsBackgroundErrorIdentity(const ioda::ObsSpace &, const eckit::Configuration &); + + virtual ~ObsBackgroundErrorIdentity(); + + void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; + + const oops::Variables & requiredVars() const override; + + oops::Variables simulatedVars() const override; + + private: + void print(std::ostream &) const override; + + private: + const ioda::ObsSpace& odb_; + ObsBackgroundErrorIdentityParameters parameters_; + oops::Variables requiredVars_; +}; + +} // namespace ufo + +#endif // UFO_BACKGROUNDERRORIDENTITY_OBSBACKGROUNDERRORIDENTITY_H_ diff --git a/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.interface.F90 b/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.interface.F90 new file mode 100644 index 000000000..48552d6fc --- /dev/null +++ b/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.interface.F90 @@ -0,0 +1,42 @@ +! (C) Copyright 2021 Met Office UK +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +module ufo_backgrounderroridentity_mod_c + +use iso_c_binding +use ufo_backgrounderroridentity_mod, only: ufo_backgrounderroridentity_fillobsdiags +implicit none + +contains + +! ------------------------------------------------------------------------------ + +subroutine ufo_backgrounderroridentity_fillobsdiags_c(c_key_geovals, c_nlocs, & + c_obsvars, c_key_obsdiags) & + bind(c, name='ufo_backgrounderroridentity_fillobsdiags_f90') + + use oops_variables_mod, only: oops_variables + use ufo_geovals_mod, only: ufo_geovals + use ufo_geovals_mod_c, only: ufo_geovals_registry + implicit none + + integer(c_int), intent(in) :: c_key_geovals + integer(c_int), intent(in) :: c_nlocs + type(c_ptr), value, intent(in) :: c_obsvars + integer(c_int), intent(in) :: c_key_obsdiags + + type(ufo_geovals), pointer :: geovals + type(oops_variables) :: obsvars + type(ufo_geovals), pointer :: obsdiags + + call ufo_geovals_registry%get(c_key_geovals, geovals) + obsvars = oops_variables(c_obsvars) + call ufo_geovals_registry%get(c_key_obsdiags, obsdiags) + + call ufo_backgrounderroridentity_fillobsdiags(geovals, c_nlocs, obsvars, obsdiags) + +end subroutine ufo_backgrounderroridentity_fillobsdiags_c + +end module ufo_backgrounderroridentity_mod_c diff --git a/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.interface.h b/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.interface.h new file mode 100644 index 000000000..eb0d569f6 --- /dev/null +++ b/src/ufo/backgrounderroridentity/ObsBackgroundErrorIdentity.interface.h @@ -0,0 +1,34 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_BACKGROUNDERRORIDENTITY_OBSBACKGROUNDERRORIDENTITY_INTERFACE_H_ +#define UFO_BACKGROUNDERRORIDENTITY_OBSBACKGROUNDERRORIDENTITY_INTERFACE_H_ + +#include "ufo/Fortran.h" + +namespace oops { +class Variables; +} // namespace oops + +namespace ioda { +class ObsSpace; +} // namespace ioda + +namespace ufo { + +extern "C" { + + void ufo_backgrounderroridentity_fillobsdiags_f90(const F90goms &geovals, + const int &nlocs, + const oops::Variables &obsvars, + const F90goms &obsdiags); + +} // extern C + +} // namespace ufo + +#endif // UFO_BACKGROUNDERRORIDENTITY_OBSBACKGROUNDERRORIDENTITY_INTERFACE_H_ diff --git a/src/ufo/backgrounderroridentity/ufo_backgrounderroridentity_mod.F90 b/src/ufo/backgrounderroridentity/ufo_backgrounderroridentity_mod.F90 new file mode 100644 index 000000000..76621295d --- /dev/null +++ b/src/ufo/backgrounderroridentity/ufo_backgrounderroridentity_mod.F90 @@ -0,0 +1,62 @@ +! (C) Copyright 2021 Met Office UK +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +module ufo_backgrounderroridentity_mod + +use iso_c_binding, only: c_ptr +use oops_variables_mod, only: oops_variables +use ufo_geovals_mod, only: ufo_geovals + +contains + +!> For each obs diagnostic called _background_error, where belongs to the set of variable +!> names @p obsvars, fill this diagnostic with estimates of the background error of variable +!> at observation locations. +subroutine ufo_backgrounderroridentity_fillobsdiags(geovals, nlocs, obsvars, obsdiags) + use kinds, only: kind_real + use ufo_geovals_mod, only: ufo_geoval, ufo_geovals, ufo_geovals_get_var + use ufo_vars_mod, only: MAXVARLEN + implicit none + + type(ufo_geovals), intent(in) :: geovals + integer, intent(in) :: nlocs + type(oops_variables), intent(in) :: obsvars + type(ufo_geovals), intent(inout) :: obsdiags + + type(ufo_geoval), pointer :: background_error + integer :: ivar + character(len=MAXVARLEN) :: varstr + integer :: lenvarstr + + character(len=*), parameter :: suffix = "_background_error" + + do ivar = 1, obsdiags%nvar + varstr = obsdiags%variables(ivar) + lenvarstr = len_trim(varstr) + + ! We need to fill this diagnostic if: + ! (a) its name is long enough to be of the form `_background_error`; + if (lenvarstr <= len(suffix)) cycle + ! (b) its name actually *is* of the form `_background_error`; + if (varstr(lenvarstr - len(suffix)+1:lenvarstr) /= suffix) cycle + ! (c) belongs to the list obsvars. + if (.not. obsvars%has(varstr(:lenvarstr - len(suffix)))) cycle + + ! All tests passed -- fill the diagnostic. + + ! Get the background error geoval. + call ufo_geovals_get_var(geovals, varstr, background_error) + + ! Allocate the background error diagnostic. + if (allocated(obsdiags%geovals(ivar)%vals)) deallocate(obsdiags%geovals(ivar)%vals) + obsdiags%geovals(ivar)%nval = 1 + allocate(obsdiags%geovals(ivar)%vals(obsdiags%geovals(ivar)%nval, nlocs)) + + ! Copy the geoval to the diagnostic. + obsdiags%geovals(ivar)%vals(1, 1:nlocs) = background_error%vals(1, 1:nlocs) + enddo +end subroutine ufo_backgrounderroridentity_fillobsdiags + +end module ufo_backgrounderroridentity_mod diff --git a/src/ufo/backgrounderrorvertinterp/CMakeLists.txt b/src/ufo/backgrounderrorvertinterp/CMakeLists.txt new file mode 100644 index 000000000..63ff9ed20 --- /dev/null +++ b/src/ufo/backgrounderrorvertinterp/CMakeLists.txt @@ -0,0 +1,22 @@ +# (C) Copyright 2021 UK Met Office +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( backgrounderrorvertinterp_files + ObsBackgroundErrorVertInterp.cc + ObsBackgroundErrorVertInterp.h + ObsBackgroundErrorVertInterp.interface.F90 + ObsBackgroundErrorVertInterp.interface.h + ufo_backgrounderrorvertinterp_mod.F90 + ) + +PREPEND( _p_backgrounderrorvertinterp_files + "backgrounderrorvertinterp" + ${backgrounderrorvertinterp_files} + ) + +set ( backgrounderrorvertinterp_src_files + ${_p_backgrounderrorvertinterp_files} + PARENT_SCOPE + ) diff --git a/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.cc b/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.cc new file mode 100644 index 000000000..4fbe25e0f --- /dev/null +++ b/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.cc @@ -0,0 +1,90 @@ +/* + * (C) Copyright 2021 UK Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.h" + +#include + +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/DateTime.h" +#include "oops/util/Duration.h" +#include "oops/util/Logger.h" + +#include "ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.interface.h" +#include "ufo/GeoVaLs.h" +#include "ufo/Locations.h" +#include "ufo/ObsDiagnostics.h" +#include "ufo/ObsOperatorBase.h" + +namespace ufo { + +static ObsOperatorMaker maker("BackgroundErrorVertInterp"); + +ObsBackgroundErrorVertInterp::ObsBackgroundErrorVertInterp(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : ObsOperatorBase(odb, config), + odb_(odb) +{ + oops::Log::trace() << "ObsBackgroundErrorVertInterp constructor entered" << std::endl; + + parameters_.validateAndDeserialize(config); + + requiredVars_.push_back(parameters_.verticalCoordinate); + // simulateObs() may be asked to interpolate the background errors of any simulated variables. + // We need to assume the worst, i.e. that we'll need to interpolate all of them. + const oops::Variables &obsvars = odb.obsvariables(); + for (size_t ivar = 0; ivar < obsvars.size(); ++ivar) + requiredVars_.push_back(obsvars[ivar] + "_background_error"); + + oops::Log::trace() << "ObsBackgroundErrorVertInterp created" << std::endl; +} + +ObsBackgroundErrorVertInterp::~ObsBackgroundErrorVertInterp() { + oops::Log::trace() << "ObsBackgroundErrorVertInterp destructed" << std::endl; +} + +void ObsBackgroundErrorVertInterp::simulateObs(const GeoVaLs & geovals, ioda::ObsVector & hofx, + ObsDiagnostics & ydiags) const { + oops::Log::trace() << "ObsBackgroundErrorVertInterp: simulateObs entered" << std::endl; + + const std::string &obsVerticalCoordinate = parameters_.observationVerticalCoordinate; + const std::string &verticalCoordinate = parameters_.verticalCoordinate; + + oops::Variables variables; + if (parameters_.variables.value() != boost::none) + for (const Variable &variable : *parameters_.variables.value()) + variables += variable.toOopsVariables(); + else + variables = odb_.obsvariables(); + + ufo_backgrounderrorvertinterp_fillobsdiags_f90(obsVerticalCoordinate.size(), + obsVerticalCoordinate.c_str(), + verticalCoordinate.size(), + verticalCoordinate.c_str(), + geovals.toFortran(), odb_, hofx.nlocs(), + variables, + ydiags.toFortran()); + + oops::Log::trace() << "ObsBackgroundErrorVertInterp: simulateObs exit" << std::endl; +} + +const oops::Variables & ObsBackgroundErrorVertInterp::requiredVars() const { + return requiredVars_; +} + +oops::Variables ObsBackgroundErrorVertInterp::simulatedVars() const { + // This operator doesn't simulate any variables -- it only produces diagnostics. + return oops::Variables(); +} + +void ObsBackgroundErrorVertInterp::print(std::ostream & os) const { + os << "ObsBackgroundErrorVertInterp: config = " << parameters_ << std::endl; +} + +} // namespace ufo diff --git a/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.h b/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.h new file mode 100644 index 000000000..3bbb43545 --- /dev/null +++ b/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.h @@ -0,0 +1,125 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_BACKGROUNDERRORVERTINTERP_OBSBACKGROUNDERRORVERTINTERP_H_ +#define UFO_BACKGROUNDERRORVERTINTERP_OBSBACKGROUNDERRORVERTINTERP_H_ + +#include +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/Variable.h" +#include "ufo/ObsOperatorBase.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +/// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class Locations; + class ObsDiagnostics; + +/// \brief Options controlling the ObsBackgroundErrorVertInterp observation operator. +class ObsBackgroundErrorVertInterpParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsBackgroundErrorVertInterpParameters, Parameters) + + public: + /// Name of the ObsOperator. Must be BackgroundErrorVertInterp. + /// + /// TODO(wsmigaj): create an ObsOperatorParametersBase class, move this parameter there and + /// derive ObsBackgroundErrorVertInterpParameters from that class. + oops::Parameter name{"name", "", this}; + + /// Simulated variables whose background errors may be calculated by this operator. + /// If not specified, defaults to the list of all simulated variables in the ObsSpace. + oops::OptionalParameter> variables{"variables", this}; + + /// Name of the ufo variable (from the `MetaData` group) storing the vertical coordinate of + /// observation locations. + oops::RequiredParameter observationVerticalCoordinate{ + "observation vertical coordinate", this}; + + /// Name of the GeoVaL storing the interpolation levels of background errors. + oops::RequiredParameter verticalCoordinate{"vertical coordinate", this}; +}; + +/// \brief An observation operator calculating ObsDiagnostics representing vertically interpolated +/// background errors of simulated variables. +/// +/// It should be used as a component of the `Composite` observation operator (with another +/// component handling the calculation of model equivalents of observations). It populates all +/// requested ObsDiagnostics called `_background_error`, where `` is the name of a +/// simulated variable, by vertically interpolating the `_background_error` GeoVaL at the +/// observation locations. Element (i, j) of this GeoVaL is interpreted as the background error +/// estimate of variable `` at the ith observation location and the vertical position read from +/// the (i, j)th element of the GeoVaL specified in the `interpolation level` option of the +/// ObsOperator. +/// +/// If the `variables` option is present, the operator does not calculate the background errors +/// of all simulated variables in the ObsSpace, but only those listed in the `variables` option. +/// +/// See ObsBackgroundErrorVertInterpParameters for the description of YAML configuration options +/// accepted by this operator. +/// +/// Example configuration: +/// +/// obs operator: +/// name: Composite +/// components: +/// # operator used to evaluate H(x) +/// - name: VertInterp +/// vertical coordinate: geopotential_height # coordinate used for obs value interpolation +/// # operator used to evaluate background errors +/// - name: BackgroundErrorVertInterp +/// # vertical coordinate of observation locations +/// observation vertical coordinate: geopotential_height +/// # GeoVaL storing interpolation levels of background errors +/// vertical coordinate: background_error_geopotential_height +/// +class ObsBackgroundErrorVertInterp : public ObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsBackgroundErrorVertInterp";} + + ObsBackgroundErrorVertInterp(const ioda::ObsSpace &, const eckit::Configuration &); + + virtual ~ObsBackgroundErrorVertInterp(); + + void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; + + const oops::Variables & requiredVars() const override; + + oops::Variables simulatedVars() const override; + + private: + void print(std::ostream &) const override; + + private: + const ioda::ObsSpace& odb_; + ObsBackgroundErrorVertInterpParameters parameters_; + oops::Variables requiredVars_; +}; + +} // namespace ufo + +#endif // UFO_BACKGROUNDERRORVERTINTERP_OBSBACKGROUNDERRORVERTINTERP_H_ diff --git a/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.interface.F90 b/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.interface.F90 new file mode 100644 index 000000000..5a56acfff --- /dev/null +++ b/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.interface.F90 @@ -0,0 +1,56 @@ +! (C) Copyright 2021 Met Office UK +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +module ufo_backgrounderrorvertinterp_mod_c + +use iso_c_binding +use ufo_backgrounderrorvertinterp_mod, only: ufo_backgrounderrorvertinterp_fillobsdiags +implicit none + +contains + +! ------------------------------------------------------------------------------ + +subroutine ufo_backgrounderrorvertinterp_fillobsdiags_c(len_obs_vcoord, c_obs_vcoord, & + len_vcoord, c_vcoord, & + c_key_geovals, c_obsspace, c_nlocs, & + c_obsvars, c_key_obsdiags) & + bind(c, name='ufo_backgrounderrorvertinterp_fillobsdiags_f90') + + use string_f_c_mod, only: c_f_string + use oops_variables_mod, only: oops_variables + use ufo_geovals_mod, only: ufo_geovals + use ufo_geovals_mod_c, only: ufo_geovals_registry + use ufo_vars_mod, only: MAXVARLEN + implicit none + + integer(c_int), intent(in) :: len_obs_vcoord + character(kind=c_char, len=1), intent(in) :: c_obs_vcoord(len_obs_vcoord + 1) + integer(c_int), intent(in) :: len_vcoord + character(kind=c_char, len=1), intent(in) :: c_vcoord(len_vcoord + 1) + integer(c_int), intent(in) :: c_key_geovals + type(c_ptr), value, intent(in) :: c_obsspace + integer(c_int), intent(in) :: c_nlocs + type(c_ptr), value, intent(in) :: c_obsvars + integer(c_int), intent(in) :: c_key_obsdiags + + character(len=MAXVARLEN) :: obs_vcoord + character(len=MAXVARLEN) :: vcoord + type(ufo_geovals), pointer :: geovals + type(oops_variables) :: obsvars + type(ufo_geovals), pointer :: obsdiags + + call c_f_string(c_obs_vcoord, obs_vcoord) + call c_f_string(c_vcoord, vcoord) + call ufo_geovals_registry%get(c_key_geovals, geovals) + obsvars = oops_variables(c_obsvars) + call ufo_geovals_registry%get(c_key_obsdiags, obsdiags) + + call ufo_backgrounderrorvertinterp_fillobsdiags(obs_vcoord, vcoord, & + geovals, c_obsspace, c_nlocs, obsvars, obsdiags) + +end subroutine ufo_backgrounderrorvertinterp_fillobsdiags_c + +end module ufo_backgrounderrorvertinterp_mod_c diff --git a/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.interface.h b/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.interface.h new file mode 100644 index 000000000..5a843a2df --- /dev/null +++ b/src/ufo/backgrounderrorvertinterp/ObsBackgroundErrorVertInterp.interface.h @@ -0,0 +1,39 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_BACKGROUNDERRORVERTINTERP_OBSBACKGROUNDERRORVERTINTERP_INTERFACE_H_ +#define UFO_BACKGROUNDERRORVERTINTERP_OBSBACKGROUNDERRORVERTINTERP_INTERFACE_H_ + +#include "ufo/Fortran.h" + +namespace oops { +class Variables; +} // namespace oops + +namespace ioda { +class ObsSpace; +} // namespace ioda + +namespace ufo { + +extern "C" { + + void ufo_backgrounderrorvertinterp_fillobsdiags_f90(const int &len_obs_vcoord, + const char *obs_vcoord, + const int &len_vcoord, + const char *vcoord, + const F90goms &geovals, + const ioda::ObsSpace &obsspace, + const int &nlocs, + const oops::Variables &obsvars, + const F90goms &obsdiags); + +} // extern C + +} // namespace ufo + +#endif // UFO_BACKGROUNDERRORVERTINTERP_OBSBACKGROUNDERRORVERTINTERP_INTERFACE_H_ diff --git a/src/ufo/backgrounderrorvertinterp/ufo_backgrounderrorvertinterp_mod.F90 b/src/ufo/backgrounderrorvertinterp/ufo_backgrounderrorvertinterp_mod.F90 new file mode 100644 index 000000000..cdf6af43c --- /dev/null +++ b/src/ufo/backgrounderrorvertinterp/ufo_backgrounderrorvertinterp_mod.F90 @@ -0,0 +1,109 @@ +! (C) Copyright 2021 Met Office UK +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +module ufo_backgrounderrorvertinterp_mod + +use iso_c_binding, only: c_ptr +use oops_variables_mod, only: oops_variables +use ufo_geovals_mod, only: ufo_geovals + +contains + +!> For each obs diagnostic called _background_error, where belongs to the set of variable +!> names @p obsvars, fill this diagnostic with estimates of the background error of variable +!> at observation locations. +subroutine ufo_backgrounderrorvertinterp_fillobsdiags(obs_vcoord_name, vcoord_name, & + geovals, obsspace, nlocs, obsvars, obsdiags) + use kinds, only: kind_real + use obsspace_mod, only: obsspace_get_db + use ufo_vars_mod, only: MAXVARLEN, var_prs, var_prsi + use ufo_geovals_mod, only: ufo_geoval, ufo_geovals, ufo_geovals_get_var + use vert_interp_mod, only: vert_interp_weights, vert_interp_apply + implicit none + + ! Name of the variable with vertical coordinates of observations + character(len=*), intent(in) :: obs_vcoord_name + ! Name of the GeoVaL with the vertical coordinate levels to use for + ! interpolation of background errors + character(len=*), intent(in) :: vcoord_name + type(ufo_geovals), intent(in) :: geovals + type(c_ptr), value, intent(in) :: obsspace + integer, intent(in) :: nlocs + type(oops_variables), intent(in) :: obsvars + type(ufo_geovals), intent(inout) :: obsdiags + + logical :: use_ln + integer :: iobs, ivar + real(kind_real) :: obs_vcoord(nlocs) + type(ufo_geoval), pointer :: vcoord_profile, background_error_profile + real(kind_real) :: wf(nlocs) + integer :: wi(nlocs) + character(len=MAXVARLEN) :: varstr + integer :: lenvarstr + real(kind_real), allocatable :: interp_nodes(:) + real(kind_real) :: interp_point + + character(len=*), parameter :: suffix = "_background_error" + + ! Get vertical coordinate profiles from geovals + call ufo_geovals_get_var(geovals, vcoord_name, vcoord_profile) + + ! Get the observation vertical coordinates + call obsspace_get_db(obsspace, "MetaData", obs_vcoord_name, obs_vcoord) + + ! Use logarithmic interpolation if the vertical coordinate is air_pressure + ! or air_pressure_levels + use_ln = (obs_vcoord_name .eq. var_prs) .or. (obs_vcoord_name .eq. var_prsi) + + ! Calculate the interpolation weights + allocate(interp_nodes(vcoord_profile%nval)) + do iobs = 1, nlocs + if (use_ln) then + interp_nodes = log(vcoord_profile%vals(:,iobs)) + interp_point = log(obs_vcoord(iobs)) + else + interp_nodes = vcoord_profile%vals(:,iobs) + interp_point = obs_vcoord(iobs) + end if + call vert_interp_weights(vcoord_profile%nval, interp_point, interp_nodes, & + wi(iobs), wf(iobs)) + enddo + + do ivar = 1, obsdiags%nvar + varstr = obsdiags%variables(ivar) + lenvarstr = len_trim(varstr) + + ! We need to fill this diagnostic if: + ! (a) its name is long enough to be of the form `_background_error`; + if (lenvarstr <= len(suffix)) cycle + ! (b) its name actually *is* of the form `_background_error`; + if (varstr(lenvarstr - len(suffix)+1:lenvarstr) /= suffix) cycle + ! (c) belongs to the list obsvars. + if (.not. obsvars%has(varstr(:lenvarstr - len(suffix)))) cycle + + ! All tests passed -- fill the diagnostic. + + ! Get background error profile from geovals + call ufo_geovals_get_var(geovals, varstr, background_error_profile) + + ! Allocate the background error diagnostic. + if (allocated(obsdiags%geovals(ivar)%vals)) deallocate(obsdiags%geovals(ivar)%vals) + obsdiags%geovals(ivar)%nval = 1 + allocate(obsdiags%geovals(ivar)%vals(obsdiags%geovals(ivar)%nval, nlocs)) + + ! Interpolate the profile at observation location into the obsdiag + do iobs = 1, nlocs + call vert_interp_apply(background_error_profile%nval, & + background_error_profile%vals(:,iobs), & + obsdiags%geovals(ivar)%vals(1,iobs), & + wi(iobs), wf(iobs)) + enddo + enddo + + ! Free memory + deallocate(interp_nodes) +end subroutine ufo_backgrounderrorvertinterp_fillobsdiags + +end module ufo_backgrounderrorvertinterp_mod diff --git a/src/ufo/categoricaloper/CMakeLists.txt b/src/ufo/categoricaloper/CMakeLists.txt new file mode 100644 index 000000000..16603f14d --- /dev/null +++ b/src/ufo/categoricaloper/CMakeLists.txt @@ -0,0 +1,16 @@ +# (C) Copyright 2021 UK Met Office +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( categoricaloper_files + ObsCategorical.h + ObsCategorical.cc + ObsCategoricalParameters.h +) +PREPEND( _p_categoricaloper_files "categoricaloper" ${categoricaloper_files} ) + +set ( categoricaloper_src_files + ${_p_categoricaloper_files} + PARENT_SCOPE +) diff --git a/src/ufo/categoricaloper/ObsCategorical.cc b/src/ufo/categoricaloper/ObsCategorical.cc new file mode 100644 index 000000000..60ad1adc6 --- /dev/null +++ b/src/ufo/categoricaloper/ObsCategorical.cc @@ -0,0 +1,144 @@ +/* + * (C) Copyright 2021 UK Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/categoricaloper/ObsCategorical.h" + +#include +#include +#include +#include + +#include "ioda/ObsVector.h" + +#include "oops/util/Logger.h" + +#include "ufo/categoricaloper/ObsCategoricalParameters.h" +#include "ufo/GeoVaLs.h" +#include "ufo/ObsDiagnostics.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static ObsOperatorMaker obsCategoricalMaker_("Categorical"); +// ----------------------------------------------------------------------------- + +ObsCategorical::ObsCategorical(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : ObsOperatorBase(odb, config), odb_(odb) +{ + oops::Log::trace() << "ObsCategorical constructor starting" << std::endl; + + ObsCategoricalParameters parameters; + parameters.validateAndDeserialize(config); + + // Get categorical variable from ObsSpace (and throw an exception if it is not present). + // In the ObsSpace, the categorical variable can be either a vector of strings or + // a vector of integers; if the latter, it is converted to a vector of strings here. + const std::string &categoricalVariableName = parameters.categoricalVariable.value(); + oops::Log::debug() << "categorical variable: " << categoricalVariableName << std::endl; + categoricalVariable_.assign(odb_.nlocs(), ""); + if (odb_.has("MetaData", categoricalVariableName)) { + const ioda::ObsDtype dtype = odb_.dtype("MetaData", categoricalVariableName); + if (dtype == ioda::ObsDtype::String) { + odb_.get_db("MetaData", categoricalVariableName, categoricalVariable_); + } else if (dtype == ioda::ObsDtype::Integer) { + std::vector categoricalVariableInt(odb_.nlocs()); + odb_.get_db("MetaData", categoricalVariableName, categoricalVariableInt); + std::transform(categoricalVariableInt.cbegin(), categoricalVariableInt.cend(), + categoricalVariable_.begin(), [](int i) {return std::to_string(i);}); + } else { + throw eckit::UserError("The categorical variable must be a vector of " + "either strings or integers", Here()); + } + } else { + throw eckit::UserError("The categorical variable " + categoricalVariableName + + " does not exist", Here()); + } + + // Name of fallback operator. + fallbackOperatorName_ = parameters.fallbackOperatorName.value(); + oops::Log::debug() << "Fallback operator: " << fallbackOperatorName_ << std::endl; + + // Map of categorised operator names. + categorisedOperatorNames_ = parameters.categorisedOperatorNames.value(); + oops::Log::debug() << "Categorised operators: " << categorisedOperatorNames_ << std::endl; + + // Create list of component operators. + for (const eckit::LocalConfiguration &operatorConfig : + parameters.operatorConfigurations.value()) { + std::unique_ptr op(ObsOperatorFactory::create(odb, operatorConfig)); + requiredVars_ += op->requiredVars(); + components_.emplace(std::make_pair(operatorConfig.getString("name"), std::move(op))); + } + + // Check the fallback operator has been configured. + if (components_.find(fallbackOperatorName_) == components_.end()) + throw eckit::UserError("The operator " + fallbackOperatorName_ + + " has not been configured", Here()); + + // Check the categorised operators have been configured. + for (const auto &operName : categorisedOperatorNames_) + if (components_.find(operName.second) == components_.end()) + throw eckit::UserError("The operator " + operName.second + + " has not been configured", Here()); + + oops::Log::trace() << "ObsCategorical constructor finished" << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsCategorical::~ObsCategorical() { + oops::Log::trace() << "ObsCategorical destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsCategorical::simulateObs(const GeoVaLs & gv, ioda::ObsVector & ovec, + ObsDiagnostics & ydiags) const { + oops::Log::trace() << "ObsCategorical: simulateObs entered" << std::endl; + + // Container of ObsVectors produced by each operator. + std::map ovecs; + oops::Log::debug() << "Running operators" << std::endl; + // Run each operator and store output in ovecs. + for (const auto& component : components_) { + ioda::ObsVector ovecTemp(ovec); + component.second->simulateObs(gv, ovecTemp, ydiags); + ovecs.insert({component.first, ovecTemp}); + } + + // Insert values into ovec according to the categorical variable. + // Use the fallback operator when necessary. + oops::Log::debug() << "Producing final ObsVector" << std::endl; + for (size_t jloc = 0; jloc < ovec.nlocs(); ++jloc) { + auto it_operName = categorisedOperatorNames_.find(categoricalVariable_[jloc]); + const auto &operName = (it_operName != categorisedOperatorNames_.end() ? + it_operName->second : + fallbackOperatorName_); + oops::Log::debug() << "Location " << jloc << ": operator name = " << operName << std::endl; + const auto &ovecloc = ovecs.at(operName); + // Loop over each variable at this location. + for (size_t jvar = 0; jvar < ovec.nvars(); ++jvar) { + const size_t idx = jloc * ovec.nvars() + jvar; + ovec[idx] = ovecloc[idx]; + } + } + + oops::Log::trace() << "ObsCategorical: simulateObs exit " << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsCategorical::print(std::ostream & os) const { + os << "ObsCategorical operator:" << std::endl; + os << "- Fallback operator: " << fallbackOperatorName_ << std::endl; + os << "- Categorised operators: " << categorisedOperatorNames_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/categoricaloper/ObsCategorical.h b/src/ufo/categoricaloper/ObsCategorical.h new file mode 100644 index 000000000..f9f15a371 --- /dev/null +++ b/src/ufo/categoricaloper/ObsCategorical.h @@ -0,0 +1,122 @@ +/* + * (C) Copyright 2021 UK Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_CATEGORICALOPER_OBSCATEGORICAL_H_ +#define UFO_CATEGORICALOPER_OBSCATEGORICAL_H_ + +#include +#include +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" + +#include "ufo/ObsOperatorBase.h" + +/// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class Locations; + class ObsDiagnostics; + +/// \brief Categorical observation operator. +/// +/// The Categorical operator can be used to run several observation operators, +/// each of which produces a vector of H(x) values. +/// The Categorical operator then creates a final H(x) vector by selecting the +/// observation operator at each location according to a categorical variable. +/// +/// The choice of observation operator at each location is governed by the `categorical variable` +/// parameter, which must be an integer or string variable in the MetaData group. +/// +/// The `categorised operators` map is used to produce a correspondence between values of the +/// categorical variable and the operator used. +/// +/// The `fallback operator` parameter governs the observation operator that will be used +/// whenever a particular value of the categorical variable does not exist in +/// `categorised operators`. +/// +/// The `operator configurations` parameter governs the configuration of each of the operators +/// to be used. +/// If either the fallback operator or one of the categorised operators have not been configured, +/// an exception will be thrown. +/// +/// An example yaml configuration is as follows: +/// obs operator: +/// name: Categorical +/// categorical variable: station_id +/// fallback operator: "Composite" +/// categorised operators: {"47418": "Composite", "54857": "Identity"} +/// operator configurations: +/// - name: Identity +/// - name: Composite +/// components: +/// - name: Identity +/// variables: +/// - name: air_temperature +/// - name: surface_pressure +/// - name: VertInterp +/// variables: +/// - name: northward_wind +/// - name: eastward_wind +/// +/// This operator uses station_id@MetaData as the categorical variable. +/// Both the Identity and Composite operators are used to produce H(x) vectors. +/// Then, at each location in the ObsSpace: +/// - if station_id@MetaData is equal to 47418 then the Composite H(x) is selected; +/// - if station_id@MetaData is equal to 54857 then the Identity H(x) is selected; +/// - otherwise, the fallback operator (also Composite in this case) H(x) is selected. +class ObsCategorical : public ObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsCategorical";} + + ObsCategorical(const ioda::ObsSpace &, const eckit::Configuration &); + ~ObsCategorical() override; + + void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; + + const oops::Variables & requiredVars() const override { return requiredVars_; } + + private: + void print(std::ostream &) const override; + + private: + /// ObsSpace. + const ioda::ObsSpace& odb_; + + /// Observation operators which are run by the Categorical operator. + std::map> components_; + + /// Required variables. + oops::Variables requiredVars_; + + /// Value of the categorical variable in the ObsSpace. + std::vector categoricalVariable_; + + /// Name of the fallback observation operator. + std::string fallbackOperatorName_; + + /// Names of the categorised observation operators. + std::map categorisedOperatorNames_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_CATEGORICALOPER_OBSCATEGORICAL_H_ diff --git a/src/ufo/categoricaloper/ObsCategoricalParameters.h b/src/ufo/categoricaloper/ObsCategoricalParameters.h new file mode 100644 index 000000000..d484c3d5f --- /dev/null +++ b/src/ufo/categoricaloper/ObsCategoricalParameters.h @@ -0,0 +1,50 @@ +/* + * (C) Copyright 2021 UK Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_CATEGORICALOPER_OBSCATEGORICALPARAMETERS_H_ +#define UFO_CATEGORICALOPER_OBSCATEGORICALPARAMETERS_H_ + +#include +#include +#include + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +namespace ufo { + +/// Configuration options recognized by the Categorical operator. +class ObsCategoricalParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsCategoricalParameters, Parameters) + + public: + /// Operator name. In future will be moved to a base class for parameters of all ObsOperators. + oops::OptionalParameter name{"name", this}; + + /// Categorical variable used to divide H(x) into sections. + oops::RequiredParameter categoricalVariable{"categorical variable", this}; + + /// Name of the fallback observation operator to use. This will be used to produce H(x) at + /// all locations whose value of the categorical variable does not appear in the + /// \p categorisedOperatorNames map. + oops::RequiredParameter fallbackOperatorName{"fallback operator", this}; + + /// Map between values of the categorical variable and the corresponding observation operators. + /// The fallback observation operator will be used for all values of the categorical variable + /// that are not represented in this map. + oops::RequiredParameter> + categorisedOperatorNames{"categorised operators", this}; + + /// A list of configuration options for each observation operator (i.e. the default operator + /// and any operators that have been specified in the \p categorisedOperatorNames map). + oops::RequiredParameter> + operatorConfigurations{"operator configurations", this}; +}; + +} // namespace ufo +#endif // UFO_CATEGORICALOPER_OBSCATEGORICALPARAMETERS_H_ diff --git a/src/ufo/compositeoper/CMakeLists.txt b/src/ufo/compositeoper/CMakeLists.txt new file mode 100644 index 000000000..60eb79d1a --- /dev/null +++ b/src/ufo/compositeoper/CMakeLists.txt @@ -0,0 +1,18 @@ +# (C) Copyright 2021 UK Met Office +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( compositeoper_files + ObsComposite.h + ObsComposite.cc + ObsCompositeParameters.h + ObsCompositeTLAD.h + ObsCompositeTLAD.cc +) +PREPEND( _p_compositeoper_files "compositeoper" ${compositeoper_files} ) + +set ( compositeoper_src_files + ${_p_compositeoper_files} + PARENT_SCOPE +) diff --git a/src/ufo/compositeoper/ObsComposite.cc b/src/ufo/compositeoper/ObsComposite.cc new file mode 100644 index 000000000..3bbdbd63d --- /dev/null +++ b/src/ufo/compositeoper/ObsComposite.cc @@ -0,0 +1,98 @@ +/* + * (C) Copyright 2021 UK Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/compositeoper/ObsComposite.h" + +#include +#include +#include +#include + +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +#include "ufo/compositeoper/ObsCompositeParameters.h" +#include "ufo/GeoVaLs.h" +#include "ufo/Locations.h" +#include "ufo/ObsDiagnostics.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static ObsOperatorMaker obsCompositeMaker_("Composite"); +// ----------------------------------------------------------------------------- + +ObsComposite::ObsComposite(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : ObsOperatorBase(odb, config), odb_(odb) +{ + oops::Log::trace() << "ObsComposite constructor starting" << std::endl; + + ObsCompositeParameters parameters; + parameters.validateAndDeserialize(config); + for (const eckit::LocalConfiguration &operatorConfig : parameters.components.value()) { + std::unique_ptr op(ObsOperatorFactory::create(odb, operatorConfig)); + requiredVars_ += op->requiredVars(); + components_.push_back(std::move(op)); + } + + oops::Log::trace() << "ObsComposite constructor finished" << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsComposite::~ObsComposite() { + oops::Log::trace() << "ObsComposite destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsComposite::simulateObs(const GeoVaLs & gv, ioda::ObsVector & ovec, + ObsDiagnostics & ydiags) const { + oops::Log::trace() << "ObsComposite: simulateObs entered" << std::endl; + + for (const std::unique_ptr &component : components_) + component->simulateObs(gv, ovec, ydiags); + + oops::Log::trace() << "ObsComposite: simulateObs exit " << std::endl; +} + +// ----------------------------------------------------------------------------- + +oops::Variables ObsComposite::simulatedVars() const { + // Merge the lists of variables simulated by all components, ensuring that there are + // no overlaps. + oops::Variables vars; + for (const std::unique_ptr &component : components_) { + oops::Variables componentVars; + // We use += rather than = to make sure componentVars contains no duplicate entries. + componentVars += component->simulatedVars(); + const size_t oldSize = vars.size(); + vars += componentVars; + if (vars.size() != oldSize + componentVars.size()) + // We don't want multiple components to write to the same row in the H(x) array. + throw eckit::UserError("Multiple components simulate the same variables", Here()); + } + return vars; +} + +// ----------------------------------------------------------------------------- + +void ObsComposite::print(std::ostream & os) const { + os << "ObsComposite with the following components:\n"; + for (size_t i = 0; i < components_.size(); ++i) { + os << " - " << *components_[i]; + if (i != components_.size() - 1) + os << '\n'; + } +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/compositeoper/ObsComposite.h b/src/ufo/compositeoper/ObsComposite.h new file mode 100644 index 000000000..20f5dd368 --- /dev/null +++ b/src/ufo/compositeoper/ObsComposite.h @@ -0,0 +1,84 @@ +/* + * (C) Copyright 2021 UK Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_COMPOSITEOPER_OBSCOMPOSITE_H_ +#define UFO_COMPOSITEOPER_OBSCOMPOSITE_H_ + +#include +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" + +#include "ufo/ObsOperatorBase.h" + +/// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class Locations; + class ObsDiagnostics; + +/// \brief A collection of observation operators used to simulate different variables. +/// +/// Use this operator to split the list of simulated variables in the ObsSpace into groups, +/// each of which should be simulated with a different operator. For example, if the list of +/// simulated variables contains some upper-air variables that need to be vertically interpolated +/// and some surface variables that don't, you can arrange the `obs operator` section in the input +/// YAML file as in the following example: +/// +/// obs operator: +/// name: Composite +/// components: +/// - name: VertInterp +/// variables: +/// - name: relative_humidity +/// name: northward_wind +/// name: eastward_wind +/// - name: Identity +/// variables: +/// - name: surface_pressure +/// +/// \note Only some operators (currently VertInterp and Identity) currently support the `variables` +/// option and thus can be used to simulate only a subset of variables. +class ObsComposite : public ObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsComposite";} + + ObsComposite(const ioda::ObsSpace &, const eckit::Configuration &); + ~ObsComposite() override; + + void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; + + const oops::Variables & requiredVars() const override { return requiredVars_; } + + oops::Variables simulatedVars() const override; + + private: + void print(std::ostream &) const override; + + private: + const ioda::ObsSpace& odb_; + std::vector> components_; + oops::Variables requiredVars_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_COMPOSITEOPER_OBSCOMPOSITE_H_ diff --git a/src/ufo/compositeoper/ObsCompositeParameters.h b/src/ufo/compositeoper/ObsCompositeParameters.h new file mode 100644 index 000000000..8e4e76ef6 --- /dev/null +++ b/src/ufo/compositeoper/ObsCompositeParameters.h @@ -0,0 +1,32 @@ +/* + * (C) Copyright 2021 UK Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_COMPOSITEOPER_OBSCOMPOSITEPARAMETERS_H_ +#define UFO_COMPOSITEOPER_OBSCOMPOSITEPARAMETERS_H_ + +#include +#include + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +namespace ufo { + +/// Configuration options recognized by the Composite operator. +class ObsCompositeParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsCompositeParameters, Parameters) + + public: + /// Operator name. In future will be moved to a base class for parameters of all ObsOperators. + oops::OptionalParameter name{"name", this}; + /// A list of configuration options for each operator used to simulate a subset of variables. + oops::RequiredParameter> components{"components", this}; +}; + +} // namespace ufo +#endif // UFO_COMPOSITEOPER_OBSCOMPOSITEPARAMETERS_H_ diff --git a/src/ufo/compositeoper/ObsCompositeTLAD.cc b/src/ufo/compositeoper/ObsCompositeTLAD.cc new file mode 100644 index 000000000..422a100b8 --- /dev/null +++ b/src/ufo/compositeoper/ObsCompositeTLAD.cc @@ -0,0 +1,119 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/compositeoper/ObsCompositeTLAD.h" + +#include +#include + +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +#include "ufo/compositeoper/ObsCompositeParameters.h" +#include "ufo/GeoVaLs.h" +#include "ufo/ObsBias.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static LinearObsOperatorMaker makerCompositeTL_("Composite"); +// ----------------------------------------------------------------------------- + +ObsCompositeTLAD::ObsCompositeTLAD(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : LinearObsOperatorBase(odb) +{ + oops::Log::trace() << "ObsCompositeTLAD constructor starting" << std::endl; + + ObsCompositeParameters parameters; + parameters.validateAndDeserialize(config); + for (const eckit::LocalConfiguration &operatorConfig : parameters.components.value()) { + std::unique_ptr op( + LinearObsOperatorFactory::create(odb, operatorConfig)); + requiredVars_ += op->requiredVars(); + components_.push_back(std::move(op)); + } + + oops::Log::trace() << "ObsCompositeTLAD created." << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsCompositeTLAD::~ObsCompositeTLAD() { + oops::Log::trace() << "ObsCompositeTLAD destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsCompositeTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, + ObsDiagnostics & ydiags) { + oops::Log::trace() << "ObsCompositeTLAD: setTrajectory entered" << std::endl; + + for (const std::unique_ptr &component : components_) + component->setTrajectory(geovals, bias, ydiags); + + oops::Log::trace() << "ObsCompositeTLAD: setTrajectory exit " << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsCompositeTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { + oops::Log::trace() << "ObsCompositeTLAD: simulateObsTL entered" << std::endl; + + for (const std::unique_ptr &component : components_) + component->simulateObsTL(geovals, ovec); + + oops::Log::trace() << "ObsCompositeTLAD: simulateObsTL exit " << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsCompositeTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { + oops::Log::trace() << "ObsCompositeTLAD: simulateObsAD entered" << std::endl; + + for (const std::unique_ptr &component : components_) + component->simulateObsAD(geovals, ovec); + + oops::Log::trace() << "ObsCompositeTLAD: simulateObsAD exit " << std::endl; +} + +// ----------------------------------------------------------------------------- + +oops::Variables ObsCompositeTLAD::simulatedVars() const { + // Merge the lists of variables simulated by all components, ensuring that there are + // no overlaps. + oops::Variables vars; + for (const std::unique_ptr &component : components_) { + oops::Variables componentVars; + // We use += rather than = to make sure componentVars contains no duplicate entries. + componentVars += component->simulatedVars(); + const size_t oldSize = vars.size(); + vars += componentVars; + if (vars.size() != oldSize + componentVars.size()) + // We don't want multiple components to write to the same row in the H(x) array. + throw eckit::UserError("Multiple components simulate the same variables", Here()); + } + return vars; +} + +// ----------------------------------------------------------------------------- + +void ObsCompositeTLAD::print(std::ostream & os) const { + os << "ObsComposite with the following components:\n"; + for (size_t i = 0; i < components_.size(); ++i) { + os << " - " << *components_[i]; + if (i != components_.size() - 1) + os << '\n'; + } +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/compositeoper/ObsCompositeTLAD.h b/src/ufo/compositeoper/ObsCompositeTLAD.h new file mode 100644 index 000000000..b49fe4553 --- /dev/null +++ b/src/ufo/compositeoper/ObsCompositeTLAD.h @@ -0,0 +1,65 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_COMPOSITEOPER_OBSCOMPOSITETLAD_H_ +#define UFO_COMPOSITEOPER_OBSCOMPOSITETLAD_H_ + +#include +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" + +#include "ufo/LinearObsOperatorBase.h" + +// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsBias; + class ObsDiagnostics; + +// ----------------------------------------------------------------------------- +/// Composite TL/AD observation operator class +class ObsCompositeTLAD : public LinearObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() { return "ufo::ObsCompositeTLAD"; } + + ObsCompositeTLAD(const ioda::ObsSpace &, const eckit::Configuration &); + ~ObsCompositeTLAD() override; + + void setTrajectory(const GeoVaLs &, const ObsBias &, ObsDiagnostics &) override; + void simulateObsTL(const GeoVaLs &, ioda::ObsVector &) const override; + void simulateObsAD(GeoVaLs &, const ioda::ObsVector &) const override; + + const oops::Variables & requiredVars() const override { return requiredVars_; } + + oops::Variables simulatedVars() const override; + + private: + void print(std::ostream &) const override; + + private: + std::vector> components_; + oops::Variables requiredVars_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_COMPOSITEOPER_OBSCOMPOSITETLAD_H_ diff --git a/src/ufo/crtm/CMakeLists.txt b/src/ufo/crtm/CMakeLists.txt index 02ffbb910..929e796e0 100644 --- a/src/ufo/crtm/CMakeLists.txt +++ b/src/ufo/crtm/CMakeLists.txt @@ -27,7 +27,7 @@ set ( crtm_files ufo_aodcrtm_tlad_mod.F90 ) -if( ${GEOS-AERO_FOUND} ) +if( ${geos-aero_FOUND} ) set ( lut_files ObsAodLUTs.h ObsAodLUTs.cc @@ -45,7 +45,7 @@ if( ${GEOS-AERO_FOUND} ) ${crtm_files} ${lut_files} ) -endif( ${GEOS-AERO_FOUND} ) +endif( ${geos-aero_FOUND} ) PREPEND( _p_crtm_files "crtm" ${crtm_files} ) diff --git a/src/ufo/crtm/ObsAodCRTMTLAD.cc b/src/ufo/crtm/ObsAodCRTMTLAD.cc index 1d967aeaf..5b45cf301 100644 --- a/src/ufo/crtm/ObsAodCRTMTLAD.cc +++ b/src/ufo/crtm/ObsAodCRTMTLAD.cc @@ -28,7 +28,7 @@ static LinearObsOperatorMaker makerAodTL_("AodCRTM"); ObsAodCRTMTLAD::ObsAodCRTMTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOperAodCRTM_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOperAodCRTM_(0), varin_() { // parse channels from the config and create variable names const oops::Variables & observed = odb.obsvariables(); @@ -52,20 +52,20 @@ ObsAodCRTMTLAD::~ObsAodCRTMTLAD() { void ObsAodCRTMTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_aodcrtm_tlad_settraj_f90(keyOperAodCRTM_, geovals.toFortran(), odb_); + ufo_aodcrtm_tlad_settraj_f90(keyOperAodCRTM_, geovals.toFortran(), obsspace()); } // ----------------------------------------------------------------------------- void ObsAodCRTMTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_aodcrtm_simobs_tl_f90(keyOperAodCRTM_, geovals.toFortran(), odb_, + ufo_aodcrtm_simobs_tl_f90(keyOperAodCRTM_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); } // ----------------------------------------------------------------------------- void ObsAodCRTMTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_aodcrtm_simobs_ad_f90(keyOperAodCRTM_, geovals.toFortran(), odb_, + ufo_aodcrtm_simobs_ad_f90(keyOperAodCRTM_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); } diff --git a/src/ufo/crtm/ObsAodCRTMTLAD.h b/src/ufo/crtm/ObsAodCRTMTLAD.h index ff1b199c5..262d4dbdd 100644 --- a/src/ufo/crtm/ObsAodCRTMTLAD.h +++ b/src/ufo/crtm/ObsAodCRTMTLAD.h @@ -56,7 +56,6 @@ class ObsAodCRTMTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOperAodCRTM_; - const ioda::ObsSpace& odb_; oops::Variables varin_; }; diff --git a/src/ufo/crtm/ObsAodLUTsTLAD.cc b/src/ufo/crtm/ObsAodLUTsTLAD.cc index 083208792..cd8b332e7 100644 --- a/src/ufo/crtm/ObsAodLUTsTLAD.cc +++ b/src/ufo/crtm/ObsAodLUTsTLAD.cc @@ -28,7 +28,7 @@ static LinearObsOperatorMaker makerAodTL_("AodLUTs"); ObsAodLUTsTLAD::ObsAodLUTsTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOperAodLUTs_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOperAodLUTs_(0), varin_() { // parse channels from the config and create variable names const oops::Variables & observed = odb.obsvariables(); @@ -52,20 +52,20 @@ ObsAodLUTsTLAD::~ObsAodLUTsTLAD() { void ObsAodLUTsTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_aodluts_tlad_settraj_f90(keyOperAodLUTs_, geovals.toFortran(), odb_); + ufo_aodluts_tlad_settraj_f90(keyOperAodLUTs_, geovals.toFortran(), obsspace()); } // ----------------------------------------------------------------------------- void ObsAodLUTsTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_aodluts_simobs_tl_f90(keyOperAodLUTs_, geovals.toFortran(), odb_, + ufo_aodluts_simobs_tl_f90(keyOperAodLUTs_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); } // ----------------------------------------------------------------------------- void ObsAodLUTsTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_aodluts_simobs_ad_f90(keyOperAodLUTs_, geovals.toFortran(), odb_, + ufo_aodluts_simobs_ad_f90(keyOperAodLUTs_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); } diff --git a/src/ufo/crtm/ObsAodLUTsTLAD.h b/src/ufo/crtm/ObsAodLUTsTLAD.h index b333d368c..14b36a010 100644 --- a/src/ufo/crtm/ObsAodLUTsTLAD.h +++ b/src/ufo/crtm/ObsAodLUTsTLAD.h @@ -56,7 +56,6 @@ class ObsAodLUTsTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOperAodLUTs_; - const ioda::ObsSpace& odb_; oops::Variables varin_; }; diff --git a/src/ufo/crtm/ObsRadianceCRTMTLAD.cc b/src/ufo/crtm/ObsRadianceCRTMTLAD.cc index ff28617f3..ed5aa2eeb 100644 --- a/src/ufo/crtm/ObsRadianceCRTMTLAD.cc +++ b/src/ufo/crtm/ObsRadianceCRTMTLAD.cc @@ -29,7 +29,7 @@ static LinearObsOperatorMaker makerCRTMTL_("CRTM"); ObsRadianceCRTMTLAD::ObsRadianceCRTMTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOperRadianceCRTM_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOperRadianceCRTM_(0), varin_() { // parse channels from the config and create variable names const oops::Variables & observed = odb.obsvariables(); @@ -53,7 +53,7 @@ ObsRadianceCRTMTLAD::~ObsRadianceCRTMTLAD() { void ObsRadianceCRTMTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics & ydiags) { - ufo_radiancecrtm_tlad_settraj_f90(keyOperRadianceCRTM_, geovals.toFortran(), odb_, + ufo_radiancecrtm_tlad_settraj_f90(keyOperRadianceCRTM_, geovals.toFortran(), obsspace(), ydiags.toFortran()); oops::Log::trace() << "ObsRadianceCRTMTLAD::setTrajectory done" << std::endl; } @@ -61,7 +61,7 @@ void ObsRadianceCRTMTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & // ----------------------------------------------------------------------------- void ObsRadianceCRTMTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_radiancecrtm_simobs_tl_f90(keyOperRadianceCRTM_, geovals.toFortran(), odb_, + ufo_radiancecrtm_simobs_tl_f90(keyOperRadianceCRTM_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); oops::Log::trace() << "ObsRadianceCRTMTLAD::simulateObsTL done" << std::endl; } @@ -69,7 +69,7 @@ void ObsRadianceCRTMTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector // ----------------------------------------------------------------------------- void ObsRadianceCRTMTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_radiancecrtm_simobs_ad_f90(keyOperRadianceCRTM_, geovals.toFortran(), odb_, + ufo_radiancecrtm_simobs_ad_f90(keyOperRadianceCRTM_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); oops::Log::trace() << "ObsRadianceCRTMTLAD::simulateObsAD done" << std::endl; } diff --git a/src/ufo/crtm/ObsRadianceCRTMTLAD.h b/src/ufo/crtm/ObsRadianceCRTMTLAD.h index 76a31a2ae..a97c508ba 100644 --- a/src/ufo/crtm/ObsRadianceCRTMTLAD.h +++ b/src/ufo/crtm/ObsRadianceCRTMTLAD.h @@ -56,7 +56,6 @@ class ObsRadianceCRTMTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOperRadianceCRTM_; - const ioda::ObsSpace& odb_; oops::Variables varin_; }; diff --git a/src/ufo/crtm/ufo_aodluts_mod.F90 b/src/ufo/crtm/ufo_aodluts_mod.F90 index 6c49358cd..5ea1b6986 100644 --- a/src/ufo/crtm/ufo_aodluts_mod.F90 +++ b/src/ufo/crtm/ufo_aodluts_mod.F90 @@ -21,7 +21,7 @@ MODULE ufo_aodluts_mod USE crtm_spccoeff, ONLY: sc USE obsspace_mod - USE fv3_mieobs_mod, ONLY: get_fv3_aod + USE cf_mieobs_mod, ONLY: get_cf_aod IMPLICIT NONE PRIVATE @@ -31,7 +31,7 @@ MODULE ufo_aodluts_mod PRIVATE CHARACTER(len=maxvarlen), PUBLIC, ALLOCATABLE :: varin(:) ! variablesrequested from the model INTEGER, ALLOCATABLE :: channels(:) - REAL(kind_real), ALLOCATABLE :: wavelenghts(:) + REAL(kind_real), ALLOCATABLE :: wavelengths(:) INTEGER :: n_aerosols TYPE(luts_conf) :: conf CONTAINS @@ -75,7 +75,7 @@ SUBROUTINE ufo_aodluts_setup(self, f_confoper, channels) self%varin(SIZE(varin_default)+1:) = var_aerosols ALLOCATE(self%channels(SIZE(channels))) - ALLOCATE(self%wavelenghts(SIZE(channels))) + ALLOCATE(self%wavelengths(SIZE(channels))) self%channels(:) = channels(:) @@ -94,7 +94,7 @@ SUBROUTINE ufo_aodluts_delete(self) IF (ALLOCATED(self%varin)) DEALLOCATE(self%varin) IF (ALLOCATED(self%channels)) DEALLOCATE(self%channels) - IF (ALLOCATED(self%wavelenghts)) DEALLOCATE(self%wavelenghts) + IF (ALLOCATED(self%wavelengths)) DEALLOCATE(self%wavelengths) END SUBROUTINE ufo_aodluts_delete @@ -125,7 +125,7 @@ SUBROUTINE ufo_aodluts_simobs(self, geovals, obss, nvars, nlocs, hofx) ! define the "non-demoninational" arguments TYPE(crtm_channelinfo_type) :: chinfo(self%conf%n_sensors) - REAL(kind_real), ALLOCATABLE :: wavelenghts_all(:) + REAL(kind_real), ALLOCATABLE :: wavelengths_all(:) REAL(kind_real), ALLOCATABLE :: aero_layers(:,:,:),rh(:,:) CHARACTER(len=maxvarlen), ALLOCATABLE :: var_aerosols(:) @@ -159,33 +159,34 @@ SUBROUTINE ufo_aodluts_simobs(self, geovals, obss, nvars, nlocs, hofx) n_channels = crtm_channelinfo_n_channels(chinfo(n)) - IF (ALLOCATED(wavelenghts_all)) DEALLOCATE(wavelenghts_all) + IF (ALLOCATED(wavelengths_all)) DEALLOCATE(wavelengths_all) - ALLOCATE(wavelenghts_all(n_channels), stat = alloc_stat) + ALLOCATE(wavelengths_all(n_channels), stat = alloc_stat) IF ( alloc_stat /= 0 ) THEN - message = 'error allocating wavelenghts_all' + message = 'error allocating wavelengths_all' CALL display_message( program_name, message, failure ) STOP END IF - wavelenghts_all=1.e7/sc(chinfo(n)%sensor_index)%wavenumber(:) + wavelengths_all=1.e7/sc(chinfo(n)%sensor_index)%wavenumber(:) - self%wavelenghts=wavelenghts_all(self%channels) + self%wavelengths=wavelengths_all(self%channels) CALL calculate_aero_layers(self%conf%aerosol_option,& &n_aerosols, n_profiles, n_layers,& &geovals, aero_layers=aero_layers, rh=rh) - CALL get_fv3_aod(n_layers, n_profiles, nvars, n_aerosols, & + CALL get_cf_aod(n_layers, n_profiles, nvars, n_aerosols, & &self%conf%rcfile, & - &self%wavelenghts, var_aerosols, aero_layers, rh, & + &self%wavelengths, var_aerosols, aero_layers, rh, & &aod_tot = hofx, rc = rc) - DEALLOCATE(aero_layers,rh,wavelenghts_all) + + DEALLOCATE(aero_layers,rh,wavelengths_all) IF (rc /= 0) THEN - message = 'error on exit from get_fv3_aod' + message = 'error on exit from get_cf_aod' CALL display_message( program_name, message, failure ) STOP END IF diff --git a/src/ufo/crtm/ufo_aodluts_tlad_mod.F90 b/src/ufo/crtm/ufo_aodluts_tlad_mod.F90 index f373e7bf4..066f08ef4 100644 --- a/src/ufo/crtm/ufo_aodluts_tlad_mod.F90 +++ b/src/ufo/crtm/ufo_aodluts_tlad_mod.F90 @@ -21,7 +21,7 @@ MODULE ufo_aodluts_tlad_mod USE crtm_spccoeff, ONLY: sc USE obsspace_mod - USE fv3_mieobs_mod, ONLY: get_fv3_aod + USE cf_mieobs_mod, ONLY: get_cf_aod USE geos_mieobs_mod, ONLY: get_geos_aod_tl, get_geos_aod_ad IMPLICIT NONE @@ -32,7 +32,7 @@ MODULE ufo_aodluts_tlad_mod PRIVATE CHARACTER(len=maxvarlen), PUBLIC, ALLOCATABLE :: varin(:) ! variablesrequested from the model INTEGER, ALLOCATABLE :: channels(:) - REAL(kind_real), ALLOCATABLE :: wavelenghts(:) + REAL(kind_real), ALLOCATABLE :: wavelengths(:) TYPE(luts_conf) :: conf INTEGER :: n_profiles INTEGER :: n_layers @@ -76,7 +76,7 @@ SUBROUTINE ufo_aodluts_tlad_setup(self, f_confoper, channels) self%varin(1:self%n_aerosols) = var_aerosols ALLOCATE(self%channels(SIZE(channels))) - ALLOCATE(self%wavelenghts(SIZE(channels))) + ALLOCATE(self%wavelengths(SIZE(channels))) self%channels(:) = channels(:) @@ -100,7 +100,7 @@ SUBROUTINE ufo_aodluts_tlad_delete(self) IF (ALLOCATED(self%layer_factors)) DEALLOCATE(self%layer_factors) IF (ALLOCATED(self%varin)) DEALLOCATE(self%varin) IF (ALLOCATED(self%channels)) DEALLOCATE(self%channels) - IF (ALLOCATED(self%wavelenghts)) DEALLOCATE(self%wavelenghts) + IF (ALLOCATED(self%wavelengths)) DEALLOCATE(self%wavelengths) END SUBROUTINE ufo_aodluts_tlad_delete @@ -126,7 +126,7 @@ SUBROUTINE ufo_aodluts_tlad_settraj(self, geovals, obss) CHARACTER(len=maxvarlen), ALLOCATABLE :: var_aerosols(:) REAL(kind_real), ALLOCATABLE :: aero_layers(:,:,:),rh(:,:) - REAL(kind_real), ALLOCATABLE :: wavelenghts_all(:) + REAL(kind_real), ALLOCATABLE :: wavelengths_all(:) INTEGER :: rc,nvars @@ -160,13 +160,13 @@ SUBROUTINE ufo_aodluts_tlad_settraj(self, geovals, obss) self%n_channels = crtm_channelinfo_n_channels(chinfo(n)) - IF (ALLOCATED(wavelenghts_all)) DEALLOCATE(wavelenghts_all) + IF (ALLOCATED(wavelengths_all)) DEALLOCATE(wavelengths_all) - ALLOCATE(wavelenghts_all(self%n_channels), stat = alloc_stat) + ALLOCATE(wavelengths_all(self%n_channels), stat = alloc_stat) - wavelenghts_all=1.e7/sc(chinfo(n)%sensor_index)%wavenumber(:) + wavelengths_all=1.e7/sc(chinfo(n)%sensor_index)%wavenumber(:) - self%wavelenghts=wavelenghts_all(self%channels) + self%wavelengths=wavelengths_all(self%channels) CALL calculate_aero_layers(self%conf%aerosol_option,& &self%n_aerosols, self%n_profiles, self%n_layers,& @@ -175,20 +175,20 @@ SUBROUTINE ufo_aodluts_tlad_settraj(self, geovals, obss) ALLOCATE(self%bext(self%n_layers, nvars, self%n_aerosols, self%n_profiles)) - CALL get_fv3_aod(self%n_layers, self%n_profiles, nvars, & + CALL get_cf_aod(self%n_layers, self%n_profiles, nvars, & &self%n_aerosols, self%conf%rcfile, & - &self%wavelenghts, var_aerosols, aero_layers, rh, & + &self%wavelengths, var_aerosols, aero_layers, rh, & &ext=self%bext, rc = rc) IF (rc /= 0) THEN - message = 'error on exit from get_fv3_aod' + message = 'error on exit from get_cf_aod' CALL display_message( program_name, message, failure ) STOP END IF DEALLOCATE(rh) DEALLOCATE(aero_layers) - DEALLOCATE(wavelenghts_all) + DEALLOCATE(wavelengths_all) END DO sensor_loop diff --git a/src/ufo/crtm/ufo_crtm_utils_mod.F90 b/src/ufo/crtm/ufo_crtm_utils_mod.F90 index b23def483..b24ec709a 100644 --- a/src/ufo/crtm/ufo_crtm_utils_mod.F90 +++ b/src/ufo/crtm/ufo_crtm_utils_mod.F90 @@ -13,9 +13,10 @@ MODULE ufo_crtm_utils_mod use crtm_module -use ufo_vars_mod use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var +use ufo_vars_mod use obsspace_mod +use ufo_utils_mod, only: cmp_strings implicit none private @@ -79,6 +80,7 @@ MODULE ufo_crtm_utils_mod integer :: inspect character(len=MAXVARLEN) :: aerosol_option character(len=255) :: salinity_option + character(len=MAXVARLEN) :: sfc_wind_geovars end type crtm_conf INTERFACE calculate_aero_layer_factor @@ -103,7 +105,7 @@ MODULE ufo_crtm_utils_mod [ var_mixr, var_co2, var_oz ] ! copy of ABSORBER_ID_NAME defined in CRTM_Atmosphere_Define - character(len=*), parameter :: & + character(len=MAXVARLEN), parameter :: & CRTM_Absorbers(N_VALID_ABSORBER_IDS) = & ABSORBER_ID_NAME(1:N_VALID_ABSORBER_IDS) integer, parameter :: & @@ -131,7 +133,7 @@ MODULE ufo_crtm_utils_mod , [N_VALID_CLOUD_CATEGORIES,2] ) ! copy of CLOUD_CATEGORY_NAME defined in CRTM_Cloud_Define - character(len=*), parameter :: & + character(len=MAXVARLEN), parameter :: & CRTM_Clouds(N_VALID_CLOUD_CATEGORIES) = & CLOUD_CATEGORY_NAME(1:N_VALID_CLOUD_CATEGORIES) integer, parameter :: & @@ -147,12 +149,15 @@ MODULE ufo_crtm_utils_mod character(len=MAXVARLEN), parameter :: & UFO_Surfaces(4) = & - [ var_sfc_wtmp, var_sfc_wspeed,var_sfc_wdir, var_sfc_sss] + [ var_sfc_wtmp, var_sfc_wspeed, var_sfc_wdir, var_sfc_sss] character(len=MAXVARLEN), parameter :: & CRTM_Surfaces(4) = & [ character(len=MAXVARLEN):: 'Water_Temperature', 'Wind_Speed', 'Wind_Direction', 'Salinity' ] + character(len=MAXVARLEN), parameter :: & + ValidSurfaceWindGeoVars(2) = [character(len=MAXVARLEN) :: 'vector', 'uv'] + contains ! ------------------------------------------------------------------------------ @@ -315,6 +320,18 @@ subroutine crtm_conf_setup(conf, f_confOpts, f_confOper) end do + ! select between two surface wind geovals options + ! valid options: vector [default], uv + if (f_confOper%get('SurfaceWindGeoVars', str)) then + conf%sfc_wind_geovars = str + else + conf%sfc_wind_geovars = 'vector' + endif + if (ufo_vars_getindex(ValidSurfaceWindGeoVars, conf%sfc_wind_geovars) < 1) then + write(message,*) 'crtm_conf_setup error: invalid SurfaceWindGeoVars ',trim(str) + call abor1_ftn(message) + end if + ! Sea_Surface_Salinity !--------- IF (f_confOpts%get("Salinity",str)) THEN @@ -494,7 +511,6 @@ end subroutine ufo_crtm_skip_profiles ! ------------------------------------------------------------------------------ SUBROUTINE Load_Atm_Data(n_Profiles, n_Layers, geovals, atm, conf) - implicit none integer, intent(in) :: n_Profiles, n_Layers @@ -534,9 +550,9 @@ SUBROUTINE Load_Atm_Data(n_Profiles, n_Layers, geovals, atm, conf) do jspec = 1, conf%n_Absorbers ! O3 Absorber has special treatment for Aerosols - if ( trim(conf%Absorbers(jspec)) == trim(var_oz) .AND. & + if (cmp_strings(conf%Absorbers(jspec), var_oz) .AND. & ufo_vars_getindex(geovals%variables, var_oz) < 0 .AND. & - TRIM(conf%aerosol_option) /= "" ) then + (.NOT. cmp_strings(conf%aerosol_option,""))) then do k1 = 1, n_Profiles atm(k1)%Absorber(1:n_Layers, jspec) = ozone_default_value end do @@ -568,16 +584,23 @@ SUBROUTINE Load_Atm_Data(n_Profiles, n_Layers, geovals, atm, conf) end do ! When n_Clouds>0, Cloud_Fraction must either be provided as geoval or in conf + ! If Cloud_Fraction is provided in conf,then use the Cloud_Fraction in conf + ! If Cloud_Fraction is not provided in conf , then use the Cloud_Fraction in geoval + ! and make sure the cloud fraction interpolated from background is in the valid range [0.0,1.0] if (conf%n_Clouds > 0) then - if ( ufo_vars_getindex(geovals%variables, var_cldfrac) > 0 ) then - CALL ufo_geovals_get_var(geovals, var_cldfrac, geoval) - do k1 = 1, n_Profiles - atm(k1)%Cloud_Fraction(:) = geoval%vals(:, k1) - end do - else + if ( conf%Cloud_Fraction >= 0.0 ) then do k1 = 1, n_Profiles atm(k1)%Cloud_Fraction(:) = conf%Cloud_Fraction end do + else + if ( ufo_vars_getindex(geovals%variables, var_cldfrac) > 0 ) then + CALL ufo_geovals_get_var(geovals, var_cldfrac, geoval) + do k1 = 1, n_Profiles + where( geoval%vals(:, k1) < 0_kind_real ) geoval%vals(:, k1) = 0_kind_real + where( geoval%vals(:, k1) > 1_kind_real ) geoval%vals(:, k1) = 1_kind_real + atm(k1)%Cloud_Fraction(:) = geoval%vals(:, k1) + end do + end if end if end if @@ -597,7 +620,7 @@ subroutine Load_Sfc_Data(n_Profiles, n_Channels, channels, geovals, sfc, chinfo, integer(c_int), intent(in) :: channels(:) type(crtm_conf), intent(in) :: conf -type(ufo_geoval), pointer :: geoval +type(ufo_geoval), pointer :: geoval, u, v integer :: k1, n1 integer :: iLand @@ -641,17 +664,38 @@ subroutine Load_Sfc_Data(n_Profiles, n_Channels, channels, geovals, sfc, chinfo, end do deallocate(ObsTb) - !Wind_Speed - call ufo_geovals_get_var(geovals, var_sfc_wspeed, geoval) - do k1 = 1, n_Profiles - sfc(k1)%Wind_Speed = geoval%vals(1, k1) - end do + if (ufo_vars_getindex(geovals%variables, var_sfc_wspeed) > 0 .and. & + ufo_vars_getindex(geovals%variables, var_sfc_wdir) > 0) then + ! Directly use model-provided wind speed and direction + !Wind_Speed + call ufo_geovals_get_var(geovals, var_sfc_wspeed, geoval) + do k1 = 1, n_Profiles + sfc(k1)%Wind_Speed = geoval%vals(1, k1) + end do - !Wind_Direction - call ufo_geovals_get_var(geovals, var_sfc_wdir, geoval) - do k1 = 1, n_Profiles - sfc(k1)%Wind_Direction = geoval%vals(1, k1) - end do + !Wind_Direction + call ufo_geovals_get_var(geovals, var_sfc_wdir, geoval) + do k1 = 1, n_Profiles + sfc(k1)%Wind_Direction = geoval%vals(1, k1) + end do + else if (ufo_vars_getindex(geovals%variables, var_sfc_u) > 0 .and. & + ufo_vars_getindex(geovals%variables, var_sfc_v) > 0) then + ! Convert 2d wind components to speed and direction + call ufo_geovals_get_var(geovals, var_sfc_u, u) + call ufo_geovals_get_var(geovals, var_sfc_v, v) + + !Wind_Speed + do k1 = 1, n_Profiles + sfc(k1)%Wind_Speed = sqrt(u%vals(1, k1)**2 + v%vals(1, k1)**2) + end do + + !Wind_Direction + do k1 = 1, n_Profiles + sfc(k1)%Wind_Direction = uv_to_wdir(u%vals(1, k1), v%vals(1, k1)) + end do + else + call abor1_ftn('Load_Sfc_Data error: missing surface wind geovals') + end if !Water_Coverage call ufo_geovals_get_var(geovals, var_sfc_wfrac, geoval) @@ -755,25 +799,38 @@ subroutine Load_Sfc_Data(n_Profiles, n_Channels, channels, geovals, sfc, chinfo, end do !Sea_Surface_Salinity - if (TRIM(conf%salinity_option) == "on") THEN + if (cmp_strings(conf%salinity_option, "on")) THEN call ufo_geovals_get_var(geovals, var_sfc_sss, geoval) do k1 = 1, n_Profiles sfc(k1)%Salinity = geoval%vals(1, k1) end do end if + ! Update Ice_Coverage and Land_Coverage for + ! Soil_Type or Vegetation_Type corresponding to Glacial land ice + do k1 = 1, n_Profiles + if (sfc(k1)%Land_Coverage > ZERO .and. & + (sfc(k1)%Soil_Type == 9 .or. sfc(k1)%Vegetation_Type == 13)) then + sfc(k1)%Ice_Coverage = min(sfc(k1)%Ice_Coverage + sfc(k1)%Land_Coverage, ONE) + sfc(k1)%Land_Coverage = ZERO + end if + end do + end subroutine Load_Sfc_Data ! ------------------------------------------------------------------------------ -subroutine Load_Geom_Data(obss,geo) +subroutine Load_Geom_Data(obss,geo,geo_hf) implicit none type(c_ptr), value, intent(in) :: obss type(CRTM_Geometry_type), intent(inout) :: geo(:) +type(CRTM_Geometry_type), intent(inout), optional :: geo_hf(:) real(kind_real), allocatable :: TmpVar(:) integer :: nlocs +character(kind=c_char,len=101) :: obsname + call obsspace_obsname(obss, obsname) nlocs = obsspace_get_nlocs(obss) allocate(TmpVar(nlocs)) @@ -810,6 +867,33 @@ subroutine Load_Geom_Data(obss,geo) where (abs(geo(:)%Sensor_Scan_Angle) > 80.0_kind_real) & geo(:)%Sensor_Scan_Angle = 0.0_kind_real +!read geophysical values for gmi high frequency channels 10-13. + if (cmp_strings(trim(obsname),'GMI-GPM') .or. cmp_strings(trim(obsname),'gmi_gpm')) then + if ( present(geo_hf) ) then + geo_hf = geo + if (obsspace_has(obss, "MetaData", "sensor_zenith_angle1")) then + call obsspace_get_db(obss, "MetaData", "sensor_zenith_angle1", TmpVar) + geo_hf(:)%Sensor_Zenith_Angle = abs(TmpVar(:)) ! needs to be absolute value + endif + if (obsspace_has(obss, "MetaData", "solar_zenith_angle1")) then + call obsspace_get_db(obss, "MetaData", "solar_zenith_angle1", TmpVar) + geo_hf(:)%Source_Zenith_Angle = TmpVar(:) + endif + if (obsspace_has(obss, "MetaData", "sensor_azimuth_angle1")) then + call obsspace_get_db(obss, "MetaData", "sensor_azimuth_angle1", TmpVar) + geo_hf(:)%Sensor_Azimuth_Angle = TmpVar(:) + endif + if (obsspace_has(obss, "MetaData", "solar_azimuth_angle1")) then + call obsspace_get_db(obss, "MetaData", "solar_azimuth_angle1", TmpVar) + geo_hf(:)%Source_Azimuth_Angle = TmpVar(:) + endif + if (obsspace_has(obss, "MetaData", "sensor_view_angle1")) then + call obsspace_get_db(obss, "MetaData", "sensor_view_angle1", TmpVar) + geo_hf(:)%Sensor_Scan_Angle = TmpVar(:) + endif + endif + endif + deallocate(TmpVar) end subroutine Load_Geom_Data @@ -830,6 +914,51 @@ end subroutine get_var_name ! ----------------------------------------------------------------------------- +!> \brief Determines the wind direction from U and V components +!! +!! \details **uv_to_wdir** Calculates the wind direction, as measured clockwise +!! from north, similar to an azimuth angle. Takes the eastward and northward +!! wind component magnitudes, respectively, as arguments. +!! Due to the azimuthal convention used here, the inverse equations are: +!! u = w * cos(wdir * deg2rad) +!! v = w * sin(wdir * deg2rad) +!! where w is the wind speed +function uv_to_wdir(u, v) result(wdir) + +use ufo_constants_mod, only: zero, one, two, pi, rad2deg + +implicit none + +real (kind=kind_real), intent(in) :: u !< eastward_wind +real (kind=kind_real), intent(in) :: v !< northward_wind +real (kind=kind_real) :: wdir +real (kind=kind_real) :: windratio, windangle +integer :: iquadrant +real(kind=kind_real),parameter:: windscale = 999999.0_kind_real +real(kind=kind_real),parameter:: windlimit = 0.0001_kind_real +real(kind=kind_real),parameter:: quadcof(4,2) = & + reshape((/zero, one, one, two, & + one, -one, one, -one/), (/4,2/)) + + if (u >= zero .and. v >= zero) iquadrant = 1 + if (u >= zero .and. v < zero) iquadrant = 2 + if (u < zero .and. v >= zero) iquadrant = 4 + if (u < zero .and. v < zero) iquadrant = 3 + + if (abs(v) >= windlimit) then + windratio = u / v + else + windratio = zero + if (abs(u) > windlimit) then + windratio = windscale * u + endif + endif + windangle = atan(abs(windratio)) ! wind azimuth is in radians + wdir = ( quadcof(iquadrant, 1) * pi + windangle * quadcof(iquadrant, 2) ) * rad2deg + +end function uv_to_wdir + +! ----------------------------------------------------------------------------- SUBROUTINE load_aerosol_data(n_profiles,n_layers,geovals,& &aerosol_option,atm) @@ -851,19 +980,19 @@ SUBROUTINE load_aerosol_data(n_profiles,n_layers,geovals,& REAL(kind_real), DIMENSION(n_layers,n_profiles) :: rh INTEGER :: ivar - IF (TRIM(aerosol_option) == "aerosols_gocart_default") THEN + IF (cmp_strings(aerosol_option, "aerosols_gocart_default")) THEN varname=var_rh CALL ufo_geovals_get_var(geovals, varname, geoval) rh(1:n_layers,1:n_profiles)=geoval%vals(1:n_layers,1:n_profiles) WHERE (rh > 1_kind_real) rh=1_kind_real CALL assign_gocart_default - ELSEIF (TRIM(aerosol_option) == "aerosols_gocart_merra_2") THEN + ELSEIF (cmp_strings(aerosol_option, "aerosols_gocart_merra_2")) THEN varname=var_rh CALL ufo_geovals_get_var(geovals, varname, geoval) rh(1:n_layers,1:n_profiles)=geoval%vals(1:n_layers,1:n_profiles) WHERE (rh > 1_kind_real) rh=1_kind_real CALL assign_gocart_merra_2 - ELSEIF (TRIM(aerosol_option) == "aerosols_other") THEN + ELSEIF (cmp_strings(aerosol_option, "aerosols_other")) THEN CALL assign_other ELSE message = 'this aerosol not implemented - check later' @@ -882,7 +1011,7 @@ SUBROUTINE assign_gocart_default INTEGER, DIMENSION(nseas_bins), PARAMETER :: seas_types=[& SEASALT_SSAM_AEROSOL,SEASALT_SSCM1_AEROSOL,SEASALT_SSCM2_AEROSOL,SEASALT_SSCM3_AEROSOL] - REAL(kind_real), DIMENSION(n_layers) :: ugkg_kgm2 + REAL(kind_real), DIMENSION(n_layers) :: layer_factors INTEGER :: i,k,m @@ -890,7 +1019,7 @@ SUBROUTINE assign_gocart_default DO m=1,n_profiles - CALL calculate_aero_layer_factor(atm(m),ugkg_kgm2) + CALL calculate_aero_layer_factor(atm(m),layer_factors) DO i=1,n_aerosols_gocart_default @@ -898,10 +1027,10 @@ SUBROUTINE assign_gocart_default CALL ufo_geovals_get_var(geovals,varname, geoval) atm(m)%aerosol(i)%Concentration(1:n_layers)=& - &MAX(geoval%vals(:,m)*ugkg_kgm2,aerosol_concentration_minvalue_layer) + &MAX(geoval%vals(:,m)*layer_factors,aerosol_concentration_minvalue_layer) - SELECT CASE ( TRIM(varname)) - CASE ('sulf') + SELECT CASE (TRIM(varname)) + CASE (var_sulfate) atm(m)%aerosol(i)%type = SULFATE_AEROSOL DO k=1,n_layers atm(m)%aerosol(i)%effective_radius(k)=& @@ -909,11 +1038,11 @@ SUBROUTINE assign_gocart_default &rh(k,m)) ENDDO - CASE ('bc1') + CASE (var_bcphobic) atm(m)%aerosol(i)%type = BLACK_CARBON_AEROSOL atm(m)%aerosol(i)%effective_radius(:)=& &AeroC%Reff(1,atm(m)%aerosol(i)%type) - CASE ('bc2') + CASE (var_bcphilic) atm(m)%aerosol(i)%type = BLACK_CARBON_AEROSOL DO k=1,n_layers atm(m)%aerosol(i)%effective_radius(k)=& @@ -921,11 +1050,11 @@ SUBROUTINE assign_gocart_default &rh(k,m)) ENDDO - CASE ('oc1') + CASE (var_ocphobic) atm(m)%aerosol(i)%type = ORGANIC_CARBON_AEROSOL atm(m)%aerosol(i)%effective_radius(:)=& &AeroC%Reff(1,atm(m)%aerosol(i)%type) - CASE ('oc2') + CASE (var_ocphilic) atm(m)%aerosol(i)%type = ORGANIC_CARBON_AEROSOL DO k=1,n_layers atm(m)%aerosol(i)%effective_radius(k)=& @@ -933,44 +1062,44 @@ SUBROUTINE assign_gocart_default &rh(k,m)) ENDDO - CASE ('dust1') + CASE (var_du001) atm(m)%aerosol(i)%type = DUST_AEROSOL atm(m)%aerosol(i)%effective_radius(:)=dust_radii(1) - CASE ('dust2') + CASE (var_du002) atm(m)%aerosol(i)%type = DUST_AEROSOL atm(m)%aerosol(i)%effective_radius(:)=dust_radii(2) - CASE ('dust3') + CASE (var_du003) atm(m)%aerosol(i)%type = DUST_AEROSOL atm(m)%aerosol(i)%effective_radius(:)=dust_radii(3) - CASE ('dust4') + CASE (var_du004) atm(m)%aerosol(i)%type = DUST_AEROSOL atm(m)%aerosol(i)%effective_radius(:)=dust_radii(4) - CASE ('dust5') + CASE (var_du005) atm(m)%aerosol(i)%type = DUST_AEROSOL atm(m)%aerosol(i)%effective_radius(:)=dust_radii(5) - CASE ('seas1') + CASE (var_ss001) atm(m)%aerosol(i)%type = seas_types(1) DO k=1,n_layers atm(m)%aerosol(i)%effective_radius(k)=& &gocart_aerosol_size(atm(m)%aerosol(i)%type, & &rh(k,m)) ENDDO - CASE ('seas2') + CASE (var_ss002) atm(m)%aerosol(i)%type = seas_types(2) DO k=1,n_layers atm(m)%aerosol(i)%effective_radius(k)=& &gocart_aerosol_size(atm(m)%aerosol(i)%type, & &rh(k,m)) ENDDO - CASE ('seas3') + CASE (var_ss003) atm(m)%aerosol(i)%type = seas_types(3) DO k=1,n_layers atm(m)%aerosol(i)%effective_radius(k)=& &gocart_aerosol_size(atm(m)%aerosol(i)%type, & &rh(k,m)) ENDDO - CASE ('seas4') + CASE (var_ss004) atm(m)%aerosol(i)%type = seas_types(4) DO k=1,n_layers atm(m)%aerosol(i)%effective_radius(k)=& @@ -1027,41 +1156,37 @@ SUBROUTINE assign_aerosol_names(aerosol_option,var_aerosols) END SUBROUTINE assign_aerosol_names - SUBROUTINE calculate_aero_layer_factor_atm_profile(atm,ugkg_kgm2) + SUBROUTINE calculate_aero_layer_factor_atm_profile(atm,layer_factors) TYPE(CRTM_atmosphere_type), INTENT(in) :: atm - REAL(kind_real), INTENT(out) :: ugkg_kgm2(:) + REAL(kind_real), INTENT(out) :: layer_factors(:) INTEGER :: k -!rh, ug2kg need to be from top to bottom -!ug2kg && hPa2Pa - DO k=1,SIZE(ugkg_kgm2) -!correct for mixing ratio factor ugkg_kgm2 + DO k=1,SIZE(layer_factors) +!correct for mixing ratio factor layer_factors !being calculated from dry pressure, cotton eq. (2.4) !p_dry=p_total/(1+1.61*mixing_ratio) - ugkg_kgm2(k)=1.0e-9_kind_real*(atm%Level_Pressure(k)-& + layer_factors(k)=1e-9_kind_real*(atm%Level_Pressure(k)-& &atm%Level_Pressure(k-1))*100_kind_real/grav/& &(1_kind_real+rv_rd*atm%Absorber(k,1)*1e-3_kind_real) ENDDO END SUBROUTINE calculate_aero_layer_factor_atm_profile - SUBROUTINE calculate_aero_layer_factor_atm(atm,ugkg_kgm2) + SUBROUTINE calculate_aero_layer_factor_atm(atm,layer_factors) TYPE(CRTM_atmosphere_type), INTENT(in) :: atm(:) - REAL(kind_real), INTENT(out) :: ugkg_kgm2(:,:) + REAL(kind_real), INTENT(out) :: layer_factors(:,:) INTEGER :: k,m -!rh, ug2kg need to be from top to bottom -!ug2kg && hPa2Pa - DO k=1,SIZE(ugkg_kgm2,1) - DO m=1,SIZE(ugkg_kgm2,2) -!correct for mixing ratio factor ugkg_kgm2 + DO k=1,SIZE(layer_factors,1) + DO m=1,SIZE(layer_factors,2) +!correct for mixing ratio factor layer_factors !being calculated from dry pressure, cotton eq. (2.4) !p_dry=p_total/(1+1.61*mixing_ratio) - ugkg_kgm2(k,m)=1.0e-9_kind_real*(atm(m)%Level_Pressure(k)-& + layer_factors(k,m)=1e-9_kind_real*(atm(m)%Level_Pressure(k)-& &atm(m)%Level_Pressure(k-1))*100_kind_real/grav/& &(1_kind_real+rv_rd*atm(m)%Absorber(k,1)*1.e-3_kind_real) ENDDO @@ -1139,7 +1264,7 @@ INTEGER FUNCTION getindex(names,usrname) INTEGER i getindex=-1 DO i=1,SIZE(names) - IF(TRIM(usrname)==TRIM(names(i))) THEN + IF(cmp_strings(usrname, names(i))) THEN getindex=i EXIT ENDIF diff --git a/src/ufo/crtm/ufo_luts_utils_mod.F90 b/src/ufo/crtm/ufo_luts_utils_mod.F90 index 87bc4f345..df90560cc 100644 --- a/src/ufo/crtm/ufo_luts_utils_mod.F90 +++ b/src/ufo/crtm/ufo_luts_utils_mod.F90 @@ -141,7 +141,7 @@ SUBROUTINE calculate_aero_layers(aerosol_option,& !correct for mixing ratio factor ugkg_kgm2 !being calculated from dry pressure, cotton eq. (2.4) !p_dry=p_total/(1+r_v/r_d*mixing_ratio) - factors(k,m)=1.e-9_kind_real*(pint(k+1,m)-pint(k,m))/grav/& + factors(k,m)=1e-9_kind_real*(pint(k+1,m)-pint(k,m))/grav/& &(1_kind_real+rv_rd*sphum(k,m)/(1_kind_real-sphum(k,m))) ENDDO ENDDO diff --git a/src/ufo/crtm/ufo_radiancecrtm_mod.F90 b/src/ufo/crtm/ufo_radiancecrtm_mod.F90 index 41a19ed3e..36fab0042 100644 --- a/src/ufo/crtm/ufo_radiancecrtm_mod.F90 +++ b/src/ufo/crtm/ufo_radiancecrtm_mod.F90 @@ -37,11 +37,11 @@ module ufo_radiancecrtm_mod procedure :: simobs => ufo_radiancecrtm_simobs end type ufo_radiancecrtm - character(len=maxvarlen), dimension(21), parameter :: varin_default = & + character(len=maxvarlen), dimension(19), parameter :: varin_default = & (/var_ts, var_prs, var_prsi, & var_sfc_wfrac, var_sfc_lfrac, var_sfc_ifrac, var_sfc_sfrac, & var_sfc_wtmp, var_sfc_ltmp, var_sfc_itmp, var_sfc_stmp, & - var_sfc_vegfrac, var_sfc_wspeed, var_sfc_wdir, var_sfc_lai, & + var_sfc_vegfrac, var_sfc_lai, & var_sfc_soilm, var_sfc_soilt, var_sfc_landtyp, & var_sfc_vegtyp, var_sfc_soiltyp, var_sfc_sdepth/) @@ -50,6 +50,7 @@ module ufo_radiancecrtm_mod ! ------------------------------------------------------------------------------ subroutine ufo_radiancecrtm_setup(self, f_confOper, channels) +use ufo_utils_mod, only: cmp_strings implicit none class(ufo_radiancecrtm), intent(inout) :: self @@ -77,7 +78,8 @@ subroutine ufo_radiancecrtm_setup(self, f_confOper, channels) ! request from the model all the hardcoded atmospheric & surface variables + ! 1 * n_Absorbers ! 2 * n_Clouds (mass content and effective radius) - nvars_in = size(varin_default) + self%conf%n_Absorbers + 2 * self%conf%n_Clouds + ! 2 for sfc_wind_geovars parsing + nvars_in = size(varin_default) + self%conf%n_Absorbers + 2 * self%conf%n_Clouds + 2 request_cldfrac = & self%conf%n_Clouds > 0 .and. & self%conf%Cloud_Fraction < 0.0 @@ -85,7 +87,7 @@ subroutine ufo_radiancecrtm_setup(self, f_confOper, channels) nvars_in = nvars_in + 1 end if ! if sss is in obs options + sss - if (TRIM(self%conf%salinity_option) == "on") then + if (cmp_strings(self%conf%salinity_option, "on")) then nvars_in = nvars_in + 1 end if @@ -107,10 +109,21 @@ subroutine ufo_radiancecrtm_setup(self, f_confOper, channels) self%varin(ind) = var_cldfrac ind = ind + 1 end if - if (TRIM(self%conf%salinity_option) == "on") then + if (cmp_strings(self%conf%salinity_option, "on")) then self%varin(ind) = var_sfc_sss ind = ind + 1 end if + if (trim(self%conf%sfc_wind_geovars) == 'vector') then + self%varin(ind) = var_sfc_wspeed + ind = ind + 1 + self%varin(ind) = var_sfc_wdir + ind = ind + 1 + else if (trim(self%conf%sfc_wind_geovars) == 'uv') then + self%varin(ind) = var_sfc_u + ind = ind + 1 + self%varin(ind) = var_sfc_v + ind = ind + 1 + end if ! save channels allocate(self%channels(size(channels))) @@ -133,6 +146,7 @@ end subroutine ufo_radiancecrtm_delete subroutine ufo_radiancecrtm_simobs(self, geovals, obss, nvars, nlocs, hofx, hofxdiags) use fckit_mpi_module, only: fckit_mpi_comm +use ufo_utils_mod, only: cmp_strings implicit none @@ -176,6 +190,13 @@ subroutine ufo_radiancecrtm_simobs(self, geovals, obss, nvars, nlocs, hofx, hofx type(CRTM_RTSolution_type), allocatable :: rts_K(:,:) type(CRTM_Options_type), allocatable :: Options(:) +!for gmi +type(CRTM_Geometry_type), allocatable :: geo_hf(:) +type(CRTM_Atmosphere_type), allocatable :: atm_Ka(:,:) +type(CRTM_Surface_type), allocatable :: sfc_Ka(:,:) +type(CRTM_RTSolution_type), allocatable :: rts_Ka(:,:) +type(CRTM_RTSolution_type), allocatable :: rtsa(:,:) + ! Used to parse hofxdiags character(len=MAXVARLEN) :: varstr character(len=MAXVARLEN), dimension(hofxdiags%nvar) :: & @@ -277,8 +298,12 @@ subroutine ufo_radiancecrtm_simobs(self, geovals, obss, nvars, nlocs, hofx, hofx !-------------------------------- call Load_Atm_Data(n_Profiles,n_Layers,geovals,atm,self%conf) call Load_Sfc_Data(n_Profiles,n_Channels,self%channels,geovals,sfc,chinfo,obss,self%conf) - call Load_Geom_Data(obss,geo) - + if (cmp_strings(self%conf%SENSOR_ID(n),'gmi_gpm')) then + allocate( geo_hf( n_Profiles )) + call Load_Geom_Data(obss,geo,geo_hf) + else + call Load_Geom_Data(obss,geo) + endif ! Call THE CRTM inspection ! ------------------------ if (self%conf%inspect > 0) then @@ -389,6 +414,49 @@ subroutine ufo_radiancecrtm_simobs(self, geovals, obss, nvars, nlocs, hofx, hofx Options ) ! Input message = 'Error calling CRTM (setTraj) K-Matrix Model for '//TRIM(self%conf%SENSOR_ID(n)) call crtm_comm_stat_check(err_stat, PROGRAM_NAME, message, f_comm) + if (cmp_strings(self%conf%SENSOR_ID(n),'gmi_gpm')) then + allocate( atm_Ka( n_Channels, n_Profiles ), & + sfc_Ka( n_Channels, n_Profiles ), & + rts_Ka( n_Channels, n_Profiles ), & + rtsa( n_Channels, n_Profiles ), & + STAT = alloc_stat ) + message = 'Error allocating K structure arrays rtsa, atm_Ka ......' + call crtm_comm_stat_check(alloc_stat, PROGRAM_NAME, message, f_comm) + !! save resutls for gmi channels 1-9. + atm_Ka = atm_K + sfc_Ka = sfc_K + rts_Ka = rts_K + rtsa = rts + !! call CRTM_K_Matrix again for geo_hf which has view angle for gmi channels 10-13. + call CRTM_Atmosphere_Zero( atm_K ) + call CRTM_Surface_Zero( sfc_K ) + rts_K%Radiance = ZERO + rts_K%Brightness_Temperature = ONE + ! Call the K-matrix model + ! ----------------------- + err_stat = CRTM_K_Matrix( atm , & ! FORWARD Input + sfc , & ! FORWARD Input + rts_K , & ! K-MATRIX Input + geo_hf , & ! Input + chinfo(n:n) , & ! Input + atm_K , & ! K-MATRIX Output + sfc_K , & ! K-MATRIX Output + rts , & ! FORWARD Output + Options ) ! Input + message = 'Error calling CRTM (setTraj, geo_hf) K-Matrix Model for ' & + //TRIM(self%conf%SENSOR_ID(n)) + call crtm_comm_stat_check(err_stat, PROGRAM_NAME, message, f_comm) + !! replace data for gmi channels 1-9 by early results calculated with geo. + do l = 1, size(self%channels) + if ( self%channels(l) <= 9 ) then + atm_K(l,:) = atm_Ka(l,:) + sfc_K(l,:) = sfc_Ka(l,:) + rts_K(l,:) = rts_Ka(l,:) + rts(l,:) = rtsa(l,:) + endif + enddo + deallocate(atm_Ka,sfc_Ka,rts_Ka,rtsa) + endif ! cmp_strings(self%conf%SENSOR_ID(n),'gmi_gpm') else ! Call the forward model call for each sensor ! ------------------------------------------- @@ -400,6 +468,31 @@ subroutine ufo_radiancecrtm_simobs(self, geovals, obss, nvars, nlocs, hofx, hofx Options ) ! Input message = 'Error calling CRTM Forward Model for '//TRIM(self%conf%SENSOR_ID(n)) call crtm_comm_stat_check(err_stat, PROGRAM_NAME, message, f_comm) + if (cmp_strings(self%conf%SENSOR_ID(n),'gmi_gpm')) then + allocate( rtsa( n_Channels, n_Profiles ), & + STAT = alloc_stat ) + message = 'Error allocating K structure arrays rtsa.' + call crtm_comm_stat_check(alloc_stat, PROGRAM_NAME, message, f_comm) + !! save resutls for gmi channels 1-9. + rtsa = rts + !! call crtm again for gmi channels 10-13 with geo_hf. + ! ----------------------- + err_stat = CRTM_Forward( atm , & ! Input + sfc , & ! Input + geo_hf , & ! Input + chinfo(n:n) , & ! Input + rts , & ! Output + Options ) ! Input + message = 'Error calling CRTM Forward Model for gmi_gpm channels 10-13' + call crtm_comm_stat_check(err_stat, PROGRAM_NAME, message, f_comm) + !! replace data for gmi channels 1-9 by results calculated with geo. + do l = 1, size(self%channels) + if ( self%channels(l) <= 9 ) then + rts(l,:) = rtsa(l,:) + endif + enddo + deallocate(rtsa) + endif ! cmp_strings(self%conf%SENSOR_ID(n),'gmi_gpm') end if ! jacobian_needed !call CRTM_RTSolution_Inspect(rts) @@ -448,7 +541,7 @@ subroutine ufo_radiancecrtm_simobs(self, geovals, obss, nvars, nlocs, hofx, hofx !============================================ ! Diagnostics used for QC and bias correction !============================================ - if (trim(xstr_diags(jvar)) == "") then + if (cmp_strings(xstr_diags(jvar), "")) then ! forward h(x) diags select case(ystr_diags(jvar)) ! variable: optical_thickness_of_atmosphere_layer_CH @@ -685,6 +778,7 @@ subroutine ufo_radiancecrtm_simobs(self, geovals, obss, nvars, nlocs, hofx, hofx ! Deallocate all arrays ! --------------------- deallocate(geo, atm, sfc, rts, Options, Skip_Profiles, STAT = alloc_stat) + if(allocated(geo_hf)) deallocate(geo_hf) message = 'Error deallocating structure arrays' call crtm_comm_stat_check(alloc_stat, PROGRAM_NAME, message, f_comm) diff --git a/src/ufo/crtm/ufo_radiancecrtm_tlad_mod.F90 b/src/ufo/crtm/ufo_radiancecrtm_tlad_mod.F90 index 010cec353..992cf581d 100644 --- a/src/ufo/crtm/ufo_radiancecrtm_tlad_mod.F90 +++ b/src/ufo/crtm/ufo_radiancecrtm_tlad_mod.F90 @@ -64,6 +64,7 @@ subroutine ufo_radiancecrtm_tlad_setup(self, f_confOper, channels) integer :: nvars_in integer :: ind, jspec type(fckit_configuration) :: f_confOpts,f_confLinOper +character(max_string) :: err_msg call f_confOper%get_or_die("obs options",f_confOpts) call crtm_conf_setup(self%conf_traj, f_confOpts, f_confOper) @@ -96,6 +97,12 @@ subroutine ufo_radiancecrtm_tlad_setup(self, f_confOper, channels) self%varin(ind) = self%conf%Surfaces(jspec) ind = ind + 1 end do + if ( (ufo_vars_getindex(self%varin, var_sfc_wspeed) > 0 .or. & + ufo_vars_getindex(self%varin, var_sfc_wdir) > 0) .and. & + trim(self%conf_traj%sfc_wind_geovars) /= "vector") then + write(err_msg,*) 'ufo_radiancecrtm_tlad_setup error: sfc_wind_geovars not supported in tlad --> ', self%conf_traj%sfc_wind_geovars + call abor1_ftn(err_msg) + end if ! save channels allocate(self%channels(size(channels))) @@ -133,6 +140,9 @@ end subroutine ufo_radiancecrtm_tlad_delete subroutine ufo_radiancecrtm_tlad_settraj(self, geovals, obss, hofxdiags) use fckit_mpi_module, only: fckit_mpi_comm +use fckit_log_module, only: fckit_log +use ieee_arithmetic, only: ieee_is_nan +use ufo_utils_mod, only: cmp_strings implicit none @@ -166,12 +176,20 @@ subroutine ufo_radiancecrtm_tlad_settraj(self, geovals, obss, hofxdiags) ! Define the K-MATRIX variables type(CRTM_RTSolution_type), allocatable :: rts_K(:,:) +!for gmi +type(CRTM_Geometry_type), allocatable :: geo_hf(:) +type(CRTM_Atmosphere_type), allocatable :: atm_Ka(:,:) +type(CRTM_Surface_type), allocatable :: sfc_Ka(:,:) +type(CRTM_RTSolution_type), allocatable :: rtsa(:,:) +type(CRTM_RTSolution_type), allocatable :: rts_Ka(:,:) +integer :: lch + ! Used to parse hofxdiags character(len=MAXVARLEN) :: varstr character(len=MAXVARLEN), dimension(hofxdiags%nvar) :: & ystr_diags, xstr_diags character(10), parameter :: jacobianstr = "_jacobian_" -integer :: str_pos(4), ch_diags(hofxdiags%nvar) +integer :: str_pos(4), ch_diags(hofxdiags%nvar),numNaN real(kind_real) :: total_od, secant_term, wfunc_max real(kind_real), allocatable :: TmpVar(:) @@ -292,7 +310,12 @@ subroutine ufo_radiancecrtm_tlad_settraj(self, geovals, obss, hofxdiags) !-------------------------------- call Load_Atm_Data(self%N_PROFILES,self%N_LAYERS,geovals,atm,self%conf_traj) call Load_Sfc_Data(self%N_PROFILES,self%n_Channels,self%channels,geovals,sfc,chinfo,obss,self%conf_traj) - call Load_Geom_Data(obss,geo) + if (cmp_strings(self%conf%SENSOR_ID(n),'gmi_gpm')) then + allocate( geo_hf( self%n_Profiles )) + call Load_Geom_Data(obss,geo,geo_hf) + else + call Load_Geom_Data(obss,geo) + endif ! Zero the K-matrix OUTPUT structures ! ----------------------------------- @@ -332,9 +355,70 @@ subroutine ufo_radiancecrtm_tlad_settraj(self, geovals, obss, hofxdiags) Options ) ! Input message = 'Error calling CRTM (setTraj) K-Matrix Model for '//TRIM(self%conf_traj%SENSOR_ID(n)) call crtm_comm_stat_check(err_stat, PROGRAM_NAME, message, f_comm) + if (cmp_strings(self%conf%SENSOR_ID(n),'gmi_gpm')) then + allocate( atm_Ka( self%n_Channels, self%n_Profiles ), & + sfc_Ka( self%n_Channels, self%n_Profiles ), & + rts_Ka( self%n_Channels, self%n_Profiles ), & + rtsa( self%n_Channels, self%n_Profiles ), & + STAT = alloc_stat ) + message = 'Error allocating K structure arrays rtsa, atm_Ka ......' + call crtm_comm_stat_check(alloc_stat, PROGRAM_NAME, message, f_comm) + !! save resutls for gmi channels 1-9. + atm_Ka = self%atm_K + sfc_Ka = self%sfc_K + rts_Ka = rts_K + rtsa = rts + ! Zero the K-matrix OUTPUT structures + ! ----------------------------------- + call CRTM_Atmosphere_Zero( self%atm_K ) + call CRTM_Surface_Zero( self%sfc_K ) + ! Inintialize the K-matrix INPUT so that the results are dTb/dx + ! ------------------------------------------------------------- + rts_K%Radiance = ZERO + rts_K%Brightness_Temperature = ONE + ! Call the K-matrix model + ! ----------------------- + err_stat = CRTM_K_Matrix( atm , & ! FORWARD Input + sfc , & ! FORWARD Input + rts_K , & ! K-MATRIX Input + geo_hf , & ! Input + chinfo(n:n) , & ! Input + self%atm_K , & ! K-MATRIX Output + self%sfc_K , & ! K-MATRIX Output + rts , & ! FORWARD Output + Options ) ! Input + message = 'Error calling CRTM (setTraj, geo_hf) K-Matrix Model for '& + //TRIM(self%conf_traj%SENSOR_ID(n)) + call crtm_comm_stat_check(err_stat, PROGRAM_NAME, message, f_comm) + !! replace data for gmi channels 1-9 by early results calculated with geo. + do lch = 1, size(self%channels) + if ( self%channels(lch) <= 9 ) then + self%atm_K(lch,:) = atm_Ka(lch,:) + self%sfc_K(lch,:) = sfc_Ka(lch,:) + rts_K(lch,:) = rts_Ka(lch,:) + rts(lch,:) = rtsa(lch,:) + endif + enddo + deallocate(atm_Ka,sfc_Ka,rts_Ka,rtsa) + endif ! cmp_strings(self%conf%SENSOR_ID(n),'gmi_gpm') !call CRTM_RTSolution_Inspect(rts) + ! check for NaN values in atm_k + numNaN = 0 + do jprofile = 1, self%n_Profiles + do jchannel = 1, size(self%channels) + do jlevel = 1, self%atm_K(jchannel,jprofile)%n_layers + if (ieee_is_nan(self%atm_K(jchannel,jprofile)%Temperature(jlevel))) then + self%Skip_Profiles(jprofile) = .TRUE. + numNaN = numNaN + 1 + write(message,*) numNaN, 'th NaN in Jacobian Profiles' + call fckit_log%info(message) + cycle + end if + end do + end do + end do !! Parse hofxdiags%variables into independent/dependent variables and channel !! assumed formats: @@ -401,7 +485,7 @@ subroutine ufo_radiancecrtm_tlad_settraj(self, geovals, obss, hofxdiags) !============================================ ! Diagnostics used for QC and bias correction !============================================ - if (trim(xstr_diags(jvar)) == "") then + if (cmp_strings(xstr_diags(jvar), "")) then ! forward h(x) diags select case(ystr_diags(jvar)) ! variable: optical_thickness_of_atmosphere_layer_CH @@ -570,6 +654,7 @@ subroutine ufo_radiancecrtm_tlad_settraj(self, geovals, obss, hofxdiags) ! Deallocate all arrays ! --------------------- deallocate(geo, atm, sfc, rts, rts_K, Options, STAT = alloc_stat) + if(allocated(geo_hf)) deallocate(geo_hf) message = 'Error deallocating structure arrays (setTraj)' call crtm_comm_stat_check(alloc_stat, PROGRAM_NAME, message, f_comm) diff --git a/src/ufo/filters/AcceptList.cc b/src/ufo/filters/AcceptList.cc new file mode 100644 index 000000000..88404e789 --- /dev/null +++ b/src/ufo/filters/AcceptList.cc @@ -0,0 +1,53 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/AcceptList.h" + +#include +#include + +#include "eckit/config/Configuration.h" + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- + +AcceptList::AcceptList(ioda::ObsSpace & obsdb, const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) +{ + oops::Log::debug() << "AcceptList: config = " << parameters_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +void AcceptList::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const { + for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { + for (size_t jobs = 0; jobs < obsdb_.nlocs(); ++jobs) { + flagged[jv][jobs] = apply[jobs]; + } + } +} + +// ----------------------------------------------------------------------------- + +void AcceptList::print(std::ostream & os) const { + os << "AcceptList: config = " << parameters_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/AcceptList.h b/src/ufo/filters/AcceptList.h new file mode 100644 index 000000000..286ad3322 --- /dev/null +++ b/src/ufo/filters/AcceptList.h @@ -0,0 +1,78 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_ACCEPTLIST_H_ +#define UFO_FILTERS_ACCEPTLIST_H_ + +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/QCflags.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} + +namespace ufo { + +/// \brief Parameters controlling the AcceptList filter. +class AcceptListParameters : public FilterParametersBase { + // We call this macro instead of "plain" OOPS_CONCRETE_PARAMETERS() because we want to customize + // the constructor definition (passing "accept" as the default action name to the base class + // constructor). + OOPS_CONCRETE_PARAMETERS_ENABLE_COPY_AND_MOVE(AcceptListParameters, FilterParametersBase) + + public: + /// Set the default action to `accept`. + explicit AcceptListParameters(oops::Parameters* parent = nullptr) + : FilterParametersBase(parent, "accept") + {} + + private: + // This filter doesn't take any extra parameters. +}; + +/// \brief A filter that, by default, performs the `accept` action on observations selected by the +/// `where` clause, i.e. resets the QC flags of these observations to `pass` unless they are +/// currently set to 'missing', 'preQC' or 'Hfailed'. +class AcceptList : public FilterBase, + private util::ObjectCounter { + public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef AcceptListParameters Parameters_; + + static const std::string classname() {return "ufo::AcceptList";} + + AcceptList(ioda::ObsSpace &, const Parameters_ &, + std::shared_ptr >, + std::shared_ptr >); + + private: + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + // "black" seems the right flag to use in the unlikely case of this filter's action being set to + // "reject" (which is currently the only action that looks at the value returned by + // this function). + int qcFlag() const override {return QCflags::black;} + + Parameters_ parameters_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_ACCEPTLIST_H_ diff --git a/src/ufo/filters/BackgroundCheck.cc b/src/ufo/filters/BackgroundCheck.cc index 6ea36904c..a927748c1 100644 --- a/src/ufo/filters/BackgroundCheck.cc +++ b/src/ufo/filters/BackgroundCheck.cc @@ -32,24 +32,29 @@ namespace ufo { // ----------------------------------------------------------------------------- -BackgroundCheck::BackgroundCheck(ioda::ObsSpace & obsdb, const eckit::Configuration & config, +BackgroundCheck::BackgroundCheck(ioda::ObsSpace & obsdb, const Parameters_ & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr), - abs_threshold_(config_.getString("absolute threshold", "")), - threshold_(config_.getString("threshold", "")), - function_abs_threshold_() + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) { oops::Log::trace() << "BackgroundCheck constructor" << std::endl; - if (config_.has("function absolute threshold")) { - std::vector threshconfs; - config_.get("function absolute threshold", threshconfs); - function_abs_threshold_ = threshconfs[0].getString("name"); - allvars_ += ufo::Variables(threshconfs); + // Typical use would be HofX group, but during testing, we include option for GsiHofX + std::string test_hofx = parameters_.test_hofx.value(); + + if (parameters_.functionAbsoluteThreshold.value()) { + for (const Variable & var : *(parameters_.functionAbsoluteThreshold.value())) + allvars_ += var; + } + allvars_ += Variables(filtervars_, test_hofx); + ASSERT(parameters_.threshold.value() || + parameters_.absoluteThreshold.value() || + parameters_.functionAbsoluteThreshold.value()); + if (parameters_.functionAbsoluteThreshold.value()) { + ASSERT(!parameters_.threshold.value() && + !parameters_.absoluteThreshold.value()); + ASSERT(!parameters_.functionAbsoluteThreshold.value()->empty()); } - allvars_ += Variables(filtervars_, "HofX"); - ASSERT(abs_threshold_ != "" || threshold_ != "" || function_abs_threshold_ != ""); } // ----------------------------------------------------------------------------- @@ -66,23 +71,21 @@ void BackgroundCheck::applyFilter(const std::vector & apply, oops::Log::trace() << "BackgroundCheck postFilter" << std::endl; const oops::Variables observed = obsdb_.obsvariables(); const float missing = util::missingValue(missing); - oops::Log::debug() << "BackgroundCheck obserr: " << *obserr_; ioda::ObsDataVector obs(obsdb_, filtervars.toOopsVariables(), "ObsValue"); - ioda::ObsDataVector bias(obsdb_, filtervars.toOopsVariables(), "ObsBias", false); + ioda::ObsDataVector obsbias(obsdb_, filtervars.toOopsVariables(), "ObsBias", false); + std::string test_hofx = parameters_.test_hofx.value(); // Get function absolute threshold - if (function_abs_threshold_ != "") { - ASSERT(abs_threshold_ == "" && threshold_ == ""); + + if (parameters_.functionAbsoluteThreshold.value()) { // Get function absolute threshold info from configuration - std::vector threshconfs; - config_.get("function absolute threshold", threshconfs); - Variable rtvar(threshconfs[0]); + const Variable &rtvar = parameters_.functionAbsoluteThreshold.value()->front(); ioda::ObsDataVector function_abs_threshold(obsdb_, rtvar.toOopsVariables()); data_.get(rtvar, function_abs_threshold); - Variables varhofx(filtervars_, "HofX"); + Variables varhofx(filtervars_, test_hofx); for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { size_t iv = observed.find(filtervars.variable(jv).variable()); // H(x) @@ -96,18 +99,15 @@ void BackgroundCheck::applyFilter(const std::vector & apply, // Threshold for current observation float zz = function_abs_threshold[jv][jobs]; - -// Apply bias correction - float yy = obs[jv][jobs] - bias[jv][jobs]; - // Check distance from background - if (std::abs(static_cast(hofx[jobs]) - yy) > zz) flagged[jv][jobs] = true; + if (std::abs(static_cast(hofx[jobs]) - obs[jv][jobs]) > zz) { + flagged[jv][jobs] = true; + } } } } } else { - ASSERT(function_abs_threshold_ == ""); - Variables varhofx(filtervars_, "HofX"); + Variables varhofx(filtervars_, test_hofx); for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { size_t iv = observed.find(filtervars.variable(jv).variable()); // H(x) @@ -117,14 +117,25 @@ void BackgroundCheck::applyFilter(const std::vector & apply, // Threshold for current variable std::vector abs_thr(obsdb_.nlocs(), std::numeric_limits::max()); std::vector thr(obsdb_.nlocs(), std::numeric_limits::max()); - if (abs_threshold_ != "") abs_thr = getScalarOrFilterData(abs_threshold_, data_); - if (threshold_ != "") thr = getScalarOrFilterData(threshold_, data_); + std::vector bc_factor(obsdb_.nlocs(), 0.0); + + if (parameters_.absoluteThreshold.value()) + abs_thr = getScalarOrFilterData(*parameters_.absoluteThreshold.value(), data_); + if (parameters_.threshold.value()) + thr = getScalarOrFilterData(*parameters_.threshold.value(), data_); + +// Bias Correction parameter + if (parameters_.BiasCorrectionFactor.value()) + bc_factor = getScalarOrFilterData(*parameters_.BiasCorrectionFactor.value(), data_); for (size_t jobs = 0; jobs < obsdb_.nlocs(); ++jobs) { if (apply[jobs] && (*flags_)[iv][jobs] == QCflags::pass) { - size_t iobs = observed.size() * jobs + iv; ASSERT((*obserr_)[iv][jobs] != util::missingValue((*obserr_)[iv][jobs])); ASSERT(obs[jv][jobs] != util::missingValue(obs[jv][jobs])); + if (parameters_.BiasCorrectionFactor.value()) { + ASSERT(obsbias[jv][jobs] != util::missingValue(obsbias[jv][jobs])); + bc_factor[jobs] = bc_factor[jobs]*obsbias[jv][jobs]; + } ASSERT(hofx[jobs] != util::missingValue(hofx[jobs])); // Threshold for current observation @@ -132,11 +143,10 @@ void BackgroundCheck::applyFilter(const std::vector & apply, std::min(abs_thr[jobs], thr[jobs] * (*obserr_)[iv][jobs]); ASSERT(zz < std::numeric_limits::max() && zz > 0.0); -// Apply bias correction - float yy = obs[jv][jobs] - bias[jv][jobs]; - // Check distance from background - if (std::abs(static_cast(hofx[jobs]) - yy) > zz) flagged[jv][jobs] = true; + if (std::abs(static_cast(hofx[jobs]) - obs[jv][jobs] - bc_factor[jobs]) > zz) { + flagged[jv][jobs] = true; + } } } } diff --git a/src/ufo/filters/BackgroundCheck.h b/src/ufo/filters/BackgroundCheck.h index f3ff8df1d..7a6feb72c 100644 --- a/src/ufo/filters/BackgroundCheck.h +++ b/src/ufo/filters/BackgroundCheck.h @@ -14,9 +14,12 @@ #include #include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/OptionalParameter.h" #include "oops/util/Printable.h" #include "ufo/filters/FilterBase.h" #include "ufo/filters/QCflags.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" namespace eckit { class Configuration; @@ -29,14 +32,55 @@ namespace ioda { namespace ufo { -/// BackgroundCheck: check observation closeness to background +/// Parameters controlling the operation of the BackgroundCheck filter. +class BackgroundCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(BackgroundCheckParameters, FilterParametersBase) + public: + /// The filter will flag observations whose bias-corrected value differs from its model equivalent + /// by more than `threshold` times the current estimate of the observation error. + /// + /// `threshold` can be a real number or the name of a variable. + oops::OptionalParameter threshold{"threshold", this}; + + /// The filter will flag observations whose bias-corrected value differs from its model equivalent + /// by more than `absolute threshold` + /// + /// `absolute threshold` can be a real number or the name of a variable. + oops::OptionalParameter absoluteThreshold{"absolute threshold", this}; + + /// The filter will flag observations whose bias-corrected value differs from its model equivalent + /// by more than `function absolute threshold` + /// + /// `function absolute threshold` should be a list of ObsFunctions (but only the first element + /// of this list is currently taken into account). The ObsFunctions may return multiple values + /// per observation, which is especially useful for quality checking data with multiple channels. + /// + /// If `function absolute threshold` is set, neither `threshold` nor `absolute threshold` should + /// be set. + oops::OptionalParameter> functionAbsoluteThreshold{ + "function absolute threshold", this}; + + /// add option parameter accounting for with or without bias correction (GSI ssmis case) + oops::OptionalParameter BiasCorrectionFactor{"bias correction parameter", this}; + + /// Name of the HofX group used to replace the default group (default is HofX) + oops::Parameter test_hofx{"test_hofx", "HofX", this}; +}; + +/// BackgroundCheck: check observation closeness to background. +/// +/// See BackgroundCheckParameters for the documentation of the parameters controlling this filter. class BackgroundCheck : public FilterBase, private util::ObjectCounter { public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef BackgroundCheckParameters Parameters_; + static const std::string classname() {return "ufo::BackgroundCheck";} - BackgroundCheck(ioda::ObsSpace &, const eckit::Configuration &, + BackgroundCheck(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr >, std::shared_ptr >); ~BackgroundCheck(); @@ -46,9 +90,8 @@ class BackgroundCheck : public FilterBase, void applyFilter(const std::vector &, const Variables &, std::vector> &) const override; int qcFlag() const override {return QCflags::fguess;} - const std::string abs_threshold_; - const std::string threshold_; - std::string function_abs_threshold_; + + Parameters_ parameters_; }; } // namespace ufo diff --git a/src/ufo/filters/BayesianBackgroundCheck.cc b/src/ufo/filters/BayesianBackgroundCheck.cc new file mode 100644 index 000000000..a367485bd --- /dev/null +++ b/src/ufo/filters/BayesianBackgroundCheck.cc @@ -0,0 +1,273 @@ +/* * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/BayesianBackgroundCheck.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" + +#include "oops/interface/ObsFilter.h" +#include "oops/util/Logger.h" + +#include "ufo/filters/getScalarOrFilterData.h" +#include "ufo/filters/QCflags.h" + +#include "ufo/utils/ProbabilityOfGrossError.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +/// BayesianBackgroundCheck: check observation closeness to background, accounting +/// for probability of gross error, i.e. that observation is bad. + +BayesianBackgroundCheck::BayesianBackgroundCheck( + ioda::ObsSpace & obsdb, + const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) +{ + oops::Log::trace() << "BayesianBackgroundCheck constructor" << std::endl; + allvars_ += Variables(filtervars_, "HofX"); + allvars_ += Variables(filtervars_, "QCFlags"); + for (size_t i = 0; i < filtervars_.size(); ++i) { + allvars_ += backgrErrVariable(filtervars_[i]); + } +} + +// ----------------------------------------------------------------------------- + +BayesianBackgroundCheck::~BayesianBackgroundCheck() { + oops::Log::trace() << "BayesianBackgroundCheck destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- +/// Return the name of the variable containing the background error estimate of the +/// specified filter variable. + +Variable BayesianBackgroundCheck::backgrErrVariable(const Variable &filterVariable) const { + return Variable(filterVariable.variable() + "_background_error@ObsDiag"); +} + +// ----------------------------------------------------------------------------- +/// Reduce a vector to only the elements for which j_reduced=true. + +template +std::vector BayesianBackgroundCheck::reduceVector(const std::vector &vector_full, + const std::vector &j_reduced) const { + size_t reduced_size = j_reduced.size(); + std::vector vector_reduced(reduced_size); + for (size_t i_reduced=0; i_reduced < reduced_size; ++i_reduced) { + vector_reduced[i_reduced] = vector_full[j_reduced[i_reduced]]; + } + return vector_reduced; +} + +// ----------------------------------------------------------------------------- +/// Copy a reduced vector back into the correct indices of the full vector. + +template +void BayesianBackgroundCheck::unreduceVector(const std::vector &vector_reduced, + std::vector &vector_full, + const std::vector &j_reduced) const { + size_t reduced_size = vector_reduced.size(); + for (size_t i_reduced=0; i_reduced < reduced_size; ++i_reduced) { + vector_full[j_reduced[i_reduced]] = vector_reduced[i_reduced]; + } +} + +// ----------------------------------------------------------------------------- +/// Apply the Bayesian background check filter. + +void BayesianBackgroundCheck::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const { + oops::Log::trace() << "BayesianBackgroundCheck postFilter" << std::endl; + const oops::Variables observed = obsdb_.obsvariables(); + + oops::Log::debug() << "BayesianBackgroundCheck obserr: " << *obserr_ << std::endl; + + ioda::ObsDataVector obs(obsdb_, filtervars.toOopsVariables(), "ObsValue"); + + Variables varhofx(filtervars_, "HofX"); + Variables varflags(filtervars_, "QCFlags"); + + // Probability density of bad observations, PdBad: + const std::vector PdBad(obsdb_.nlocs(), parameters_.PdBad.value()); + // NOT profiles averaged to model levels (single-level anyway): + const bool ModelLevels = false; + + bool previousVariableWasFirstComponentOfTwo = false; + bool thisVariableIsFirstComponentOfTwo = false; + // Loop through all filter variables. .yaml will say if it's 2-component. + // If so, it skips the first component and then processes both together when + // (previousVariableWasFirstComponentOfTwo=True). + // Otherwise it processes the scalar variable. + for (size_t filterVarIndex = 0; filterVarIndex < filtervars.size(); ++filterVarIndex) { + if (filtervars[filterVarIndex].options().getBool("first_component_of_two", false)) { + previousVariableWasFirstComponentOfTwo = true; + } else { + std::string varname1, varname2; + size_t iv1, iv2; + // H(x): + std::vector hofx1, hofx2; + // H(x) error: + std::vector hofxerr; + data_.get(backgrErrVariable(filtervars[filterVarIndex]), hofxerr); + // PGE: + std::vector PGE1(obsdb_.nlocs(), parameters_.PGE.value()); + // after-check PGE: + std::vector PGEBd1(obsdb_.nlocs()); + // QC flags: + std::vector qcflags1(obsdb_.nlocs()); + std::vector firstComponentObVal, secondComponentObVal; + // make note of conditions on apply and flags_: + std::vector applycondition(obsdb_.nlocs(), false); + // index mapping between full and reduced vectors: + std::vector j_reduced; + + if (previousVariableWasFirstComponentOfTwo) { + varname1 = filtervars.variable(filterVarIndex-1).variable(); + varname2 = filtervars.variable(filterVarIndex).variable(); + iv1 = observed.find(varname1); + iv2 = observed.find(varname2); + // H(x): + data_.get(varhofx.variable(filterVarIndex-1), hofx1); + data_.get(varhofx.variable(filterVarIndex), hofx2); + // observation values: + firstComponentObVal = obs[filterVarIndex-1]; + secondComponentObVal = obs[filterVarIndex]; + // QC flags (all zeros, or read from file if possible): + if (obsdb_.has("QCFlags", varname1)) { + data_.get(varflags.variable(filterVarIndex-1), qcflags1); + oops::Log::debug() << "Got qcflags1 from file: " << qcflags1 << std::endl; + } + for (size_t jobs=0; jobs < obsdb_.nlocs(); ++jobs) { + if (apply[jobs] && (*flags_)[iv1][jobs] == QCflags::pass + && (*flags_)[iv2][jobs] == QCflags::pass) { + applycondition[jobs] = true; + j_reduced.push_back(jobs); + } + } + } else { + varname1 = filtervars.variable(filterVarIndex).variable(); + iv1 = observed.find(varname1); + // H(x): + data_.get(varhofx.variable(filterVarIndex), hofx1); + // observation values: + firstComponentObVal = obs[filterVarIndex]; + // QC flags (all zeros, or read from file if possible): + if (obsdb_.has("QCFlags", varname1)) { + data_.get(varflags.variable(filterVarIndex), qcflags1); + oops::Log::debug() << "Got qcflags1 from file: " << qcflags1 << std::endl; + } + for (size_t jobs=0; jobs < obsdb_.nlocs(); ++jobs) { + if (apply[jobs] && (*flags_)[iv1][jobs] == QCflags::pass) { + applycondition[jobs] = true; + j_reduced.push_back(jobs); + } + } + } + + // create reduced vectors, copied from full ones, fulfilling applycondition: + std::vector firstComponentObVal_reduced = reduceVector(firstComponentObVal, + j_reduced); + std::vector ObsErr_reduced = reduceVector((*obserr_)[iv1], j_reduced); + std::vector hofx1_reduced = reduceVector(hofx1, j_reduced); + std::vector hofxerr_reduced = reduceVector(hofxerr, j_reduced); + std::vector PdBad_reduced = reduceVector(PdBad, j_reduced); + std::vector qcflags1_reduced = reduceVector(qcflags1, j_reduced); + std::vector PGE1_reduced = reduceVector(PGE1, j_reduced); + std::vector PGEBd1_reduced = reduceVector(PGEBd1, j_reduced); + std::vector secondComponentObVal_reduced; + std::vector hofx2_reduced; + if (previousVariableWasFirstComponentOfTwo) { + secondComponentObVal_reduced = reduceVector(secondComponentObVal, j_reduced); + hofx2_reduced = reduceVector(hofx2, j_reduced); + } + + ufo::BayesianPGEUpdate(parameters_.PGEParameters, + firstComponentObVal_reduced, + ObsErr_reduced, + hofx1_reduced, + hofxerr_reduced, + PdBad_reduced, + ModelLevels, + qcflags1_reduced, + PGE1_reduced, + PGEBd1_reduced, + -1, + previousVariableWasFirstComponentOfTwo? + &secondComponentObVal_reduced : nullptr, + previousVariableWasFirstComponentOfTwo? + &hofx2_reduced : nullptr); + // write PGE-updated values from reduced vectors back into full ones: + unreduceVector(qcflags1_reduced, qcflags1, j_reduced); + unreduceVector(PGE1_reduced, PGE1, j_reduced); + unreduceVector(PGEBd1_reduced, PGEBd1, j_reduced); + + // Save PGE to obsdb + obsdb_.put_db("GrossErrorProbabilityTotal", varname1, PGE1); // 'packed' PGE + obsdb_.put_db("GrossErrorProbabilityBuddyCheck", varname1, PGEBd1); // for buddy check + // Save QC flags to obsdb + obsdb_.put_db("QCFlags", varname1, qcflags1); // Met Office QC flags, not flagged or *flags_ + + if (previousVariableWasFirstComponentOfTwo) { + // Save PGE to obsdb + std::vector &PGE2 = PGE1; // in old OPS, PGE same for both components of 2-vector + // after-check PGE: + std::vector &PGEBd2 = PGEBd1; // may assign different to each component in future + obsdb_.put_db("GrossErrorProbabilityTotal", varname2, PGE2); + obsdb_.put_db("GrossErrorProbabilityBuddyCheck", varname2, PGEBd2); + // Save QC flags to obsdb + std::vector &qcflags2 = qcflags1; // in old OPS, flags same for both components + obsdb_.put_db("QCFlags", varname2, qcflags2); + // Set flagged, for 2nd component: + for (size_t jobs=0; jobs < obsdb_.nlocs(); ++jobs) { + if (qcflags1[jobs] & ufo::MetOfficeQCFlags::Elem::BackRejectFlag) { + flagged[filterVarIndex-1][jobs] = true; + } + if (qcflags2[jobs] & ufo::MetOfficeQCFlags::Elem::BackRejectFlag) { + flagged[filterVarIndex][jobs] = true; + } + oops::Log::debug() << "flagged(1)[" << jobs << "]: " + << flagged[filterVarIndex-1][jobs] << std::endl; + oops::Log::debug() << "flagged(2)[" << jobs << "]: " + << flagged[filterVarIndex][jobs] << std::endl; + } + } else { + // Set flagged, for scalar: + for (size_t jobs=0; jobs < obsdb_.nlocs(); ++jobs) { + if (qcflags1[jobs] & ufo::MetOfficeQCFlags::Elem::BackRejectFlag) { + flagged[filterVarIndex][jobs] = true; + } + oops::Log::debug() << "flagged[" << jobs << "]: " + << flagged[filterVarIndex][jobs] << std::endl; + } + } + previousVariableWasFirstComponentOfTwo = false; + } + } +} + +// ----------------------------------------------------------------------------- + +void BayesianBackgroundCheck::print(std::ostream & os) const { + os << "BayesianBackgroundCheck: config = " << parameters_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/BayesianBackgroundCheck.h b/src/ufo/filters/BayesianBackgroundCheck.h new file mode 100644 index 000000000..8718c2e7f --- /dev/null +++ b/src/ufo/filters/BayesianBackgroundCheck.h @@ -0,0 +1,124 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_BAYESIANBACKGROUNDCHECK_H_ +#define UFO_FILTERS_BAYESIANBACKGROUNDCHECK_H_ + +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/NumericConstraints.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "oops/util/Printable.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/QCflags.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" +#include "ufo/utils/ProbabilityOfGrossErrorParameters.h" + +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} + +namespace ufo { + +/// Parameters controlling the operation of the BayesianBackgroundCheck filter. +class BayesianBackgroundCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(BayesianBackgroundCheckParameters, FilterParametersBase) + + public: + /// Parameters related to PGE calculations + ProbabilityOfGrossErrorParameters PGEParameters{this}; + // Probability density of observation being bad: + oops::RequiredParameter PdBad{"prob density bad obs", this, + {oops::exclusiveMinConstraint(0.0f)}}; + // Initial probability of gross error: + oops::RequiredParameter PGE{"initial prob gross error", this, + {oops::exclusiveMinConstraint(0.0f)}}; +}; + +/// \brief BayesianBackgroundCheck: check observation closeness to background, accounting +/// for probability of gross error, i.e. that observation is bad. +/// \details Flag observations whose values differ from their model equivalents by too much. +/// "Too much" depends on the uncertainties of both observation and model +/// background, and the prior probability of the observation being "bad", or in +/// gross error. Can apply to scalar or vector observation types on single-level. +/// (Cannot handle profiles.) Calls function ufo::BayesianPGEUpdate. +/// +/// Requires the following be specified in .yaml, under +/// +/// obs filters: +/// - filter: Bayesian Background Check +/// +/// * prob density bad obs: uniform probability density that observation is "bad", +/// i.e. in gross error; +/// * initial prob gross error: initial PGE value of the uniform probability +/// distribution, before adjustment depending on obs-BG closeness; +/// +/// May also specify the following optional parameters (defaults in +/// ufo/utils/ProbabilityOfGrossErrorParameters.h): +/// * PGE threshold: fail if after-check PGE exceeds this value; +/// * obs minus BG threshold: fail if \f$([y-H(x)]/{\sigma})^2\f$ exceeds this; +/// * max exponent: maximum allowed value of the exponent in the 'good' +/// probability distribution; +/// * obs error multiplier: weight of observation error in combined error variance; +/// * BG error multiplier: weight of background error in combined error variance. +class BayesianBackgroundCheck : public FilterBase, + private util::ObjectCounter { + public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef BayesianBackgroundCheckParameters Parameters_; + + static const std::string classname() {return "ufo::BayesianBackgroundCheck";} + + BayesianBackgroundCheck(ioda::ObsSpace & obsdb, const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr); + ~BayesianBackgroundCheck(); + + private: + void print(std::ostream &) const override; + + /// \brief Apply Bayesian background check filter. Return flagged=true for rejected obs. + void applyFilter(const std::vector & apply, const Variables & filtervars, + std::vector> & flagged) const override; + + /// \brief Return bayesianQC flag for observations rejected by Bayesian BG check. + int qcFlag() const override {return QCflags::bayesianQC;} + + /// \brief Return the name of the variable containing the background error estimate of the + /// specified filter variable. + Variable backgrErrVariable(const Variable & filterVariable) const; + + /// \brief Reduce a vector to only the elements for which j_reduced=true. + /// \param[in] vector_full: full vector, + /// \param[in] j_reduced: indices of vector_full to be copied into... + /// \return vector_reduced. + template + std::vector reduceVector(const std::vector & vector_full, + const std::vector & j_reduced) const; + + /// \brief Copy a reduced vector back into the correct indices of the full vector. + /// \param[in] vector_reduced: reduced vector to restore to... + /// \param[inout] vector_full: full vector, + /// \param[in] j_reduced: indices of vector_full to be populated. + template + void unreduceVector(const std::vector & vector_reduced, + std::vector & vector_full, + const std::vector & j_reduced) const; + Parameters_ parameters_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_BAYESIANBACKGROUNDCHECK_H_ diff --git a/src/ufo/filters/BayesianBackgroundQCFlags.cc b/src/ufo/filters/BayesianBackgroundQCFlags.cc new file mode 100644 index 000000000..ac7e4cdc7 --- /dev/null +++ b/src/ufo/filters/BayesianBackgroundQCFlags.cc @@ -0,0 +1,133 @@ +/* + * (C) Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/BayesianBackgroundQCFlags.h" + +#include +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" + +#include "oops/util/Logger.h" +#include "ufo/filters/QCflags.h" +#include "ufo/utils/metoffice/MetOfficeQCFlags.h" + +namespace ufo { + +BayesianBackgroundQCFlags::BayesianBackgroundQCFlags +(ioda::ObsSpace & obsdb, + const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) +{ + oops::Log::trace() << "BayesianBackgroundQCFlags constructor" << std::endl; +} + +BayesianBackgroundQCFlags::~BayesianBackgroundQCFlags() { + oops::Log::trace() << "BayesianBackgroundQCFlags destructor" << std::endl; +} + +std::string BayesianBackgroundQCFlags::getPGEsubstituteName(const std::string &varname) const { + const auto PGEsubstituteNames = parameters_.PGEsubstituteNames.value(); + const auto it = PGEsubstituteNames.find(varname); + if (it != PGEsubstituteNames.end()) + return it->second; + else + return varname; +} + +void BayesianBackgroundQCFlags::setFlags(const std::string& varname, + const std::vector& apply, + std::vector& flagged) const { + const float missingValueFloat = util::missingValue(missingValueFloat); + const int missingValueInt = util::missingValue(missingValueInt); + const size_t nlocs = obsdb_.nlocs(); + // PGE multiplication factor used to store PGE values for later use. + const float PGEMult = 1000.0; + // Missing data indicator for packed PGEs (for compatibility with OPS). + const float PGEMDI = 1.111; + // PGE rejection limit. + const float PGECrit = parameters_.PGEParameters.PGE_PGECrit.value(); + // Packed PGE rejection limit. + const float packedPGECrit = PGEMult * PGECrit; + + // Sometimes the PGE of one variable is used to set the QC flags of another; + // this happens for (e.g.) wind u and v components. + // By default varPGE = varname. + const std::string varPGE = getPGEsubstituteName(varname); + + // Get QC flags. + std::vector QCflags(nlocs, missingValueInt); + if (obsdb_.has("QCFlags", varname)) + obsdb_.get_db("QCFlags", varname, QCflags); + else + throw eckit::BadValue(varname + "@QCFlags not present", Here()); + + // Get the PGE values that each observation had after + // the Bayesian background and buddy checks were applied. + // If the checks were not applied, default to using + // the existing PGE values. + if (obsdb_.has("GrossErrorProbabilityBuddyCheck", varPGE)) { + // Buddy check was applied. + std::vector buddyCheckPGEs(nlocs, missingValueFloat); + obsdb_.get_db("GrossErrorProbabilityBuddyCheck", varPGE, buddyCheckPGEs); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + if (!apply[iloc]) continue; + if (buddyCheckPGEs[iloc] == PGEMDI) { + QCflags[iloc] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + flagged[iloc] = true; + } else if (buddyCheckPGEs[iloc] >= PGECrit) { + QCflags[iloc] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + QCflags[iloc] |= ufo::MetOfficeQCFlags::Elem::BuddyRejectFlag; + flagged[iloc] = true; + } + } + } else if (obsdb_.has("GrossErrorProbability", varPGE)) { + // Get the PGE values that each observation had before + // the Bayesian background and buddy checks were applied. + std::vector PGEs(nlocs, missingValueFloat); + obsdb_.get_db("GrossErrorProbability", varPGE, PGEs); + // Buddy check was not applied. + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + if (apply[iloc] && PGEs[iloc] >= packedPGECrit) { + QCflags[iloc] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + flagged[iloc] = true; + } + } + } else { + std::stringstream errormessage; + errormessage << "At least one of " + << varname + "@GrossErrorProbability or " + << varname + "@GrossErrorProbabilityBuddyCheck must be present" + << std::endl; + throw eckit::BadValue(errormessage.str(), Here()); + } + + // Save modified flags. + obsdb_.put_db("QCFlags", varname, QCflags); +} + +void BayesianBackgroundQCFlags::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const { + print(oops::Log::trace()); + + for (size_t ivar = 0; ivar < filtervars.nvars(); ++ivar) { + const std::string varname = filtervars.variable(ivar).variable(); + setFlags(varname, apply, flagged[ivar]); + } +} + +void BayesianBackgroundQCFlags::print(std::ostream & os) const { + os << "BayesianBackgroundQCFlags: config = " << parameters_ << std::endl; +} + +} // namespace ufo diff --git a/src/ufo/filters/BayesianBackgroundQCFlags.h b/src/ufo/filters/BayesianBackgroundQCFlags.h new file mode 100644 index 000000000..f1145b420 --- /dev/null +++ b/src/ufo/filters/BayesianBackgroundQCFlags.h @@ -0,0 +1,95 @@ +/* + * (C) Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_BAYESIANBACKGROUNDQCFLAGS_H_ +#define UFO_FILTERS_BAYESIANBACKGROUNDQCFLAGS_H_ + +#include +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/Printable.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/QCflags.h" +#include "ufo/utils/ProbabilityOfGrossErrorParameters.h" + +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} + +namespace ufo { + +/// Parameters controlling the operation of the BayesianBackgroundQCFlags filter. +class BayesianBackgroundQCFlagsParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(BayesianBackgroundQCFlagsParameters, FilterParametersBase) + + public: + /// This parameter is a collection of PGE variable name substitutions. + /// The PGE of the second variable in each pair is used to set the QC flags + /// of the first variable; this happens for (e.g.) wind u and v components. + /// todo(UKMO): might need to add the variables known as + /// u10, v10 and uSuperob, vSuperob in OPS. + oops::Parameter> PGEsubstituteNames + {"PGE variable name substitutions", {{"northward_wind", "eastward_wind"}}, this}; + + /// Parameters related to PGE calculations. The value of \p PGECrit + /// is obtained from here. + ProbabilityOfGrossErrorParameters PGEParameters{this}; +}; + +/// BayesianBackgroundQCFlags: apply QC flags based on values of probability of +/// gross error (PGE). If the PGE is larger than the threshold \p PGEcrit then +/// the observation is rejected. +/// If the Bayesian background or buddy checks were applied, +/// use the PGE that was obtained from those checks. +/// Sometimes the PGE of one variable is used to set the QC flags of another; +/// this happens for (e.g.) wind u and v components. +/// +/// todo(UKMO): deal with Pstar/Pmsl and u10AmbWind/v10AmbWind (as they are known in OPS). +/// These are each treated slightly differently in the OPS code. +class BayesianBackgroundQCFlags : public FilterBase, + private util::ObjectCounter { + public: + typedef BayesianBackgroundQCFlagsParameters Parameters_; + + static const std::string classname() {return "ufo::BayesianBackgroundQCFlags";} + + BayesianBackgroundQCFlags(ioda::ObsSpace &, const Parameters_ &, + std::shared_ptr >, + std::shared_ptr >); + ~BayesianBackgroundQCFlags(); + + private: + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override {return QCflags::bayesianQC;} + + /// Get the name of the variable whose PGE is tested in order to + /// set the QC flags for the variable \p varname. + /// By default, \p varname is returned by this routine; any substitutions + /// are listed in the \p PGEsubstituteNames parameter. + std::string getPGEsubstituteName(const std::string& varname) const; + + /// Set flags for the variable \p varname given the \p apply vector. + /// Set both integer bitmap flags and an overall filter flag (bayesianQC). + void setFlags(const std::string& varname, + const std::vector& apply, + std::vector& flagged) const; + + /// Parameters used in this filter. + Parameters_ parameters_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_BAYESIANBACKGROUNDQCFLAGS_H_ diff --git a/src/ufo/filters/BlackList.cc b/src/ufo/filters/BlackList.cc index a597f472d..dbcb1d911 100644 --- a/src/ufo/filters/BlackList.cc +++ b/src/ufo/filters/BlackList.cc @@ -16,21 +16,18 @@ #include "ioda/ObsSpace.h" #include "oops/base/Variables.h" -#include "oops/util/abor1_cpp.h" #include "oops/util/Logger.h" -#include "ufo/filters/QCflags.h" - namespace ufo { // ----------------------------------------------------------------------------- -BlackList::BlackList(ioda::ObsSpace & obsdb, const eckit::Configuration & config, +BlackList::BlackList(ioda::ObsSpace & obsdb, const Parameters_ & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) { - oops::Log::debug() << "BlackList: config = " << config_ << std::endl; + oops::Log::debug() << "BlackList: config = " << parameters_ << std::endl; } // ----------------------------------------------------------------------------- @@ -42,16 +39,9 @@ BlackList::~BlackList() {} void BlackList::applyFilter(const std::vector & apply, const Variables & filtervars, std::vector> & flagged) const { -// Initialize map from filtervars to observed variables - const oops::Variables observed = obsdb_.obsvariables(); - std::vector filt2obs; - for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { - filt2obs.push_back(observed.find(filtervars.variable(jv).variable())); - } - for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { for (size_t jobs = 0; jobs < obsdb_.nlocs(); ++jobs) { - flagged[jv][jobs] = apply[jobs] && (*flags_)[filt2obs[jv]][jobs] == QCflags::pass; + flagged[jv][jobs] = apply[jobs]; } } } @@ -59,7 +49,7 @@ void BlackList::applyFilter(const std::vector & apply, // ----------------------------------------------------------------------------- void BlackList::print(std::ostream & os) const { - os << "BlackList: config = " << config_ << std::endl; + os << "BlackList: config = " << parameters_ << std::endl; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/BlackList.h b/src/ufo/filters/BlackList.h index dab281802..afe836cc2 100644 --- a/src/ufo/filters/BlackList.h +++ b/src/ufo/filters/BlackList.h @@ -28,18 +28,28 @@ namespace ioda { namespace ufo { -/// BlackList: generic black listing of observations +class BlackListParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(BlackListParameters, FilterParametersBase) -// Filters observations out regardless of obs value -// The same effect can be achieved with opposite criteria through the "Domain Check", -// the choice is a matter of convenience or which seems more natural. + // This filter doesn't take any extra parameters. +}; + +/// \brief Generic black listing of observations. +/// +/// Filters observations out regardless of obs value +/// The same effect can be achieved with opposite criteria through the "Domain Check", +/// the choice is a matter of convenience or which seems more natural. class BlackList : public FilterBase, private util::ObjectCounter { public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef BlackListParameters Parameters_; + static const std::string classname() {return "ufo::BlackList";} - BlackList(ioda::ObsSpace &, const eckit::Configuration &, + BlackList(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr >, std::shared_ptr >); ~BlackList(); @@ -49,6 +59,8 @@ class BlackList : public FilterBase, void applyFilter(const std::vector &, const Variables &, std::vector> &) const override; int qcFlag() const override {return QCflags::black;} + + Parameters_ parameters_; }; } // namespace ufo diff --git a/src/ufo/filters/CMakeLists.txt b/src/ufo/filters/CMakeLists.txt index 08ea65fde..becd199f4 100644 --- a/src/ufo/filters/CMakeLists.txt +++ b/src/ufo/filters/CMakeLists.txt @@ -4,28 +4,31 @@ # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. set ( filters_files - TrackCheckUtils.cc - TrackCheckUtils.h - TrackCheckUtilsParameters.h - TrackCheck.cc - TrackCheck.h - TrackCheckParameters.h - TrackCheckShip.cc - TrackCheckShip.h - TrackCheckShipDiagnostics.h - TrackCheckShipParameters.h BackgroundCheck.cc BackgroundCheck.h + BayesianBackgroundQCFlags.cc + BayesianBackgroundQCFlags.h + BayesianBackgroundCheck.cc + BayesianBackgroundCheck.h BlackList.cc BlackList.h DifferenceCheck.cc DifferenceCheck.h FilterBase.cc FilterBase.h + FilterParametersBase.h + GenericFilterParameters.h getScalarOrFilterData.cc getScalarOrFilterData.h + HistoryCheck.cc + HistoryCheck.h + HistoryCheckParameters.h + ImpactHeightCheck.cc + ImpactHeightCheck.h ObsBoundsCheck.cc ObsBoundsCheck.h + ObsProcessorBase.cc + ObsProcessorBase.h MetOfficeBuddyCheck.cc MetOfficeBuddyCheck.h MetOfficeBuddyCheckParameters.h @@ -38,6 +41,8 @@ set ( filters_files MetOfficeBuddyPair.h MetOfficeBuddyPairFinder.cc MetOfficeBuddyPairFinder.h + ModelObThreshold.cc + ModelObThreshold.h MWCLWCheck.cc MWCLWCheck.h ObsDiagnosticsWriter.cc @@ -55,18 +60,51 @@ set ( filters_files QCflags.h QCmanager.cc QCmanager.h + PerformAction.cc + PerformAction.h + ProfileBackgroundCheck.cc + ProfileBackgroundCheck.h ProfileConsistencyChecks.cc ProfileConsistencyChecks.h ProfileConsistencyCheckParameters.h + ProfileFewObsCheck.cc + ProfileFewObsCheck.h + SatName.h + SatName.cc + StuckCheck.cc + StuckCheck.h + StuckCheckParameters.h + obsfunctions/DrawObsErrorFromFile.cc + obsfunctions/DrawObsErrorFromFile.h + obsfunctions/DrawValueFromFile.cc + obsfunctions/DrawValueFromFile.h Thinning.cc Thinning.h TemporalThinning.cc TemporalThinning.h TemporalThinningParameters.h + TrackCheck.cc + TrackCheck.h + TrackCheckParameters.h + TrackCheckShip.cc + TrackCheckShip.h + TrackCheckShipDiagnostics.h + TrackCheckShipParameters.h + TrackCheckUtils.cc + TrackCheckUtils.h + TrackCheckUtilsParameters.cc + TrackCheckUtilsParameters.h Variable.cc Variable.h + VariableTransforms.cc + VariableTransforms.h + VariableTransformsParameters.h Variables.cc Variables.h + VariableAssignment.cc + VariableAssignment.h + AcceptList.cc + AcceptList.h Gaussian_Thinning.cc Gaussian_Thinning.h GaussianThinningParameters.cc @@ -77,8 +115,12 @@ set ( filters_files PoissonDiskThinningParameters.h processWhere.cc processWhere.h + ObsAccessor.cc + ObsAccessor.h actions/AssignError.cc actions/AssignError.h + actions/AcceptObs.cc + actions/AcceptObs.h actions/FilterAction.cc actions/FilterAction.h actions/FilterActionBase.cc @@ -87,14 +129,22 @@ set ( filters_files actions/InflateError.h actions/RejectObs.cc actions/RejectObs.h + obsfunctions/BennartzScatIndex.cc + obsfunctions/BennartzScatIndex.h obsfunctions/ChannelUseflagCheckRad.cc obsfunctions/ChannelUseflagCheckRad.h obsfunctions/CLWMatchIndexMW.cc obsfunctions/CLWMatchIndexMW.h obsfunctions/CLWRetMW.cc obsfunctions/CLWRetMW.h + obsfunctions/TotalColumnVaporGuess.cc + obsfunctions/TotalColumnVaporGuess.h + obsfunctions/CLWRetMW_SSMIS.cc + obsfunctions/CLWRetMW_SSMIS.h obsfunctions/CLWRetSymmetricMW.cc obsfunctions/CLWRetSymmetricMW.h + obsfunctions/Conditional.cc + obsfunctions/Conditional.h obsfunctions/SIRetMW.cc obsfunctions/SIRetMW.h obsfunctions/SIRetSymmetricMW.cc @@ -103,8 +153,14 @@ set ( filters_files obsfunctions/HydrometeorCheckAMSUA.h obsfunctions/HydrometeorCheckATMS.cc obsfunctions/HydrometeorCheckATMS.h + obsfunctions/ImpactHeight.cc + obsfunctions/ImpactHeight.h obsfunctions/InterChannelConsistencyCheck.cc obsfunctions/InterChannelConsistencyCheck.h + obsfunctions/BgdDepartureAnomaly.cc + obsfunctions/BgdDepartureAnomaly.h + obsfunctions/ObsErrorModelStepwiseLinear.cc + obsfunctions/ObsErrorModelStepwiseLinear.h obsfunctions/ObsErrorModelRamp.cc obsfunctions/ObsErrorModelRamp.h obsfunctions/ObsErrorModelQuad.cc @@ -113,6 +169,10 @@ set ( filters_files obsfunctions/ObsFunction.h obsfunctions/ObsFunctionBase.cc obsfunctions/ObsFunctionBase.h + obsfunctions/CloudCostFunction.cc + obsfunctions/CloudCostFunction.h + obsfunctions/CloudDetectMinResidualAVHRR.cc + obsfunctions/CloudDetectMinResidualAVHRR.h obsfunctions/CloudDetectMinResidualIR.cc obsfunctions/CloudDetectMinResidualIR.h obsfunctions/ObsErrorBoundIR.cc @@ -125,24 +185,57 @@ set ( filters_files obsfunctions/ObsErrorFactorSurfJacobianRad.h obsfunctions/ObsErrorFactorLatRad.cc obsfunctions/ObsErrorFactorLatRad.h + obsfunctions/ObsErrorFactorQuotient.cc + obsfunctions/ObsErrorFactorQuotient.h obsfunctions/ObsErrorFactorTopoRad.cc obsfunctions/ObsErrorFactorTopoRad.h obsfunctions/ObsErrorFactorTransmitTopRad.cc obsfunctions/ObsErrorFactorTransmitTopRad.h obsfunctions/ObsErrorFactorWavenumIR.cc obsfunctions/ObsErrorFactorWavenumIR.h + obsfunctions/ObsErrorFactorSfcPressure.cc + obsfunctions/ObsErrorFactorSfcPressure.h + obsfunctions/ObsErrorFactorConventional.cc + obsfunctions/ObsErrorFactorConventional.h obsfunctions/NearSSTRetCheckIR.cc obsfunctions/NearSSTRetCheckIR.h + obsfunctions/ObsFunctionLinearCombination.cc + obsfunctions/ObsFunctionLinearCombination.h obsfunctions/ObsFunctionScattering.cc obsfunctions/ObsFunctionScattering.h obsfunctions/ObsFunctionVelocity.cc obsfunctions/ObsFunctionVelocity.h + obsfunctions/SatwindIndivErrors.cc + obsfunctions/SatwindIndivErrors.h + obsfunctions/SatWindsLNVDCheck.cc + obsfunctions/SatWindsLNVDCheck.h + obsfunctions/SatWindsSPDBCheck.cc + obsfunctions/SatWindsSPDBCheck.h obsfunctions/SCATRetMW.cc obsfunctions/SCATRetMW.h obsfunctions/SymmCldImpactIR.cc obsfunctions/SymmCldImpactIR.h + obsfunctions/SunGlintAngle.cc + obsfunctions/SunGlintAngle.h + obsfunctions/TropopauseEstimate.cc + obsfunctions/TropopauseEstimate.h + obsfunctions/WindDirAngleDiff.cc + obsfunctions/WindDirAngleDiff.h + obsfunctions/LAMDomainCheck/LAMDomainCheck.cc + obsfunctions/LAMDomainCheck/LAMDomainCheck.h + obsfunctions/LAMDomainCheck/LAMDomainCheck.interface.h + obsfunctions/LAMDomainCheck/LAMDomainCheck.interface.F90 + obsfunctions/LAMDomainCheck/esg_grid_mod.F90 ) +if( ${rttov_FOUND} ) + add_subdirectory( rttovonedvarcheck ) + list( APPEND filters_files ${rttovonedvarcheck_src_files} ) +endif( ${rttov_FOUND} ) + +add_subdirectory( gnssroonedvarcheck ) +list( APPEND filters_files ${gnssroonedvarcheck_src_files} ) + PREPEND( _p_filters_files "filters" ${filters_files} ) set ( filters_src_files diff --git a/src/ufo/filters/DifferenceCheck.cc b/src/ufo/filters/DifferenceCheck.cc index 2cc48ad37..0c4281a17 100644 --- a/src/ufo/filters/DifferenceCheck.cc +++ b/src/ufo/filters/DifferenceCheck.cc @@ -21,15 +21,15 @@ namespace ufo { // ----------------------------------------------------------------------------- -DifferenceCheck::DifferenceCheck(ioda::ObsSpace & obsdb, const eckit::Configuration & config, +DifferenceCheck::DifferenceCheck(ioda::ObsSpace & obsdb, const Parameters_ & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr), - ref_(config_.getString("reference")), val_(config_.getString("value")) + : FilterBase(obsdb, parameters, flags, obserr), + parameters_(parameters) { oops::Log::trace() << "DifferenceCheck contructor starting" << std::endl; - allvars_ += ref_; - allvars_ += val_; + allvars_ += parameters_.ref; + allvars_ += parameters_.val; } // ----------------------------------------------------------------------------- @@ -49,8 +49,8 @@ void DifferenceCheck::applyFilter(const std::vector & apply, const size_t nlocs = obsdb_.nlocs(); // min/max value setup - float vmin = config_.getFloat("minvalue", missing); - float vmax = config_.getFloat("maxvalue", missing); + float vmin = parameters_.minvalue.value().value_or(missing); + float vmax = parameters_.maxvalue.value().value_or(missing); // check for threshold and if exists, set vmin and vmax appropriately const float thresh = config_.getFloat("threshold", missing); @@ -61,8 +61,8 @@ void DifferenceCheck::applyFilter(const std::vector & apply, // Get reference values and values to compare (as floats) std::vector ref, val; - data_.get(ref_, ref); - data_.get(val_, val); + data_.get(parameters_.ref, ref); + data_.get(parameters_.val, val); ASSERT(ref.size() == val.size()); // Loop over all obs diff --git a/src/ufo/filters/DifferenceCheck.h b/src/ufo/filters/DifferenceCheck.h index 5562ffe13..1482abc64 100644 --- a/src/ufo/filters/DifferenceCheck.h +++ b/src/ufo/filters/DifferenceCheck.h @@ -14,9 +14,12 @@ #include #include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/RequiredParameter.h" #include "ufo/filters/FilterBase.h" #include "ufo/filters/QCflags.h" #include "ufo/filters/Variable.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" namespace eckit { class Configuration; @@ -29,14 +32,43 @@ namespace ioda { namespace ufo { -/// DifferenceCheck filter +/// Parameters controlling the operation of the DifferenceCheck filter. +class DifferenceCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(DifferenceCheckParameters, FilterParametersBase) + public: + /// Name of the reference variable. + oops::RequiredParameter ref{"reference", this}; + /// Name of the test variable. + oops::RequiredParameter val{"value", this}; + + /// The filter will flag observations for which the difference `test - reference` is below + /// `minvalue`. + oops::OptionalParameter minvalue{"minvalue", this}; + /// The filter will flag observations for which the difference `test - reference` is above + /// `maxvalue`. + oops::OptionalParameter maxvalue{"maxvalue", this}; + + /// If the `threshold` option is specified, the filter behaves as if `minvalue` was set to + /// `-threshold` and `maxvalue` was set to `threshold` (overriding any values of these options + /// specified independently). + oops::OptionalParameter threshold{"threshold", this}; +}; + +/// A filter that compares the difference between a test variable and a reference variable and +/// flags observations for which this difference is outside of a prescribed range. +/// +/// See DifferenceCheckParameters for the documentation of the parameters controlling this filter. class DifferenceCheck : public FilterBase, private util::ObjectCounter { public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef DifferenceCheckParameters Parameters_; + static const std::string classname() {return "ufo::DifferenceCheck";} - DifferenceCheck(ioda::ObsSpace &, const eckit::Configuration &, + DifferenceCheck(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr >, std::shared_ptr >); ~DifferenceCheck(); @@ -46,8 +78,8 @@ class DifferenceCheck : public FilterBase, void applyFilter(const std::vector &, const Variables &, std::vector> &) const override; int qcFlag() const override {return QCflags::diffref;} - const Variable ref_; - const Variable val_; + + Parameters_ parameters_; }; } // namespace ufo diff --git a/src/ufo/filters/FilterBase.cc b/src/ufo/filters/FilterBase.cc index c4bb4b41e..097319738 100644 --- a/src/ufo/filters/FilterBase.cc +++ b/src/ufo/filters/FilterBase.cc @@ -7,6 +7,7 @@ #include "ufo/filters/FilterBase.h" +#include #include #include "eckit/config/Configuration.h" @@ -19,6 +20,7 @@ #include "oops/util/Logger.h" #include "ufo/filters/actions/FilterAction.h" +#include "ufo/filters/GenericFilterParameters.h" #include "ufo/filters/processWhere.h" #include "ufo/GeoVaLs.h" #include "ufo/ObsDiagnostics.h" @@ -27,78 +29,47 @@ namespace ufo { // ----------------------------------------------------------------------------- -FilterBase::FilterBase(ioda::ObsSpace & os, const eckit::Configuration & config, +FilterBase::FilterBase(ioda::ObsSpace & os, + const FilterParametersBaseWithAbstractAction & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : obsdb_(os), config_(config), flags_(flags), obserr_(obserr), - allvars_(getAllWhereVariables(config_)), - filtervars_(), data_(obsdb_), prior_(false), post_(false), - defer_to_post_(false) + : ObsProcessorBase(os, parameters.deferToPost, std::move(flags), std::move(obserr)), + config_(parameters.toConfiguration()), + filtervars_(), + whereParameters_(parameters.where), + actionParameters_(parameters.action().clone()) { - oops::Log::trace() << "FilterBase contructor" << std::endl; - ASSERT(flags); - ASSERT(obserr); - data_.associate(*flags_, "QCflagsData"); - data_.associate(*obserr_, "ObsErrorData"); - if (config_.has("filter variables")) { + oops::Log::trace() << "FilterBase constructor" << std::endl; + allvars_ += getAllWhereVariables(parameters.where); + + if (parameters.filterVariables.value() != boost::none) { // read filter variables - filtervars_ += Variables(config_.getSubConfigurations("filter variables")); + for (const Variable &var : *parameters.filterVariables.value()) + filtervars_ += var; } else { // if no filter variables explicitly specified, filter out all variables filtervars_ += Variables(obsdb_.obsvariables()); } - // Defer filter to run as a post filter even if hofx not needed. - defer_to_post_ = config_.getBool("defer to post", false); - eckit::LocalConfiguration aconf; - config_.get("action", aconf); - FilterAction action(aconf); - allvars_ += action.requiredVariables(); -} - -// ----------------------------------------------------------------------------- - -FilterBase::~FilterBase() { - oops::Log::trace() << "FilterBase destructed" << std::endl; -} - -// ----------------------------------------------------------------------------- -void FilterBase::preProcess() { - oops::Log::trace() << "FilterBase preProcess begin" << std::endl; -// Cannot determine earlier when to apply filter because subclass -// constructors add to allvars - if (allvars_.hasGroup("HofX") || allvars_.hasGroup("ObsDiag") || - defer_to_post_ ) { - post_ = true; - } else { - if (allvars_.hasGroup("GeoVaLs")) { - prior_ = true; - } else { - this->doFilter(); - } - } - oops::Log::trace() << "FilterBase preProcess end" << std::endl; + FilterAction action(*actionParameters_); + allvars_ += action.requiredVariables(); } // ----------------------------------------------------------------------------- -void FilterBase::priorFilter(const GeoVaLs & gv) { - oops::Log::trace() << "FilterBase priorFilter begin" << std::endl; - if (prior_ || post_) data_.associate(gv); - if (prior_) this->doFilter(); - oops::Log::trace() << "FilterBase priorFilter end" << std::endl; -} +FilterBase::FilterBase(ioda::ObsSpace & os, const eckit::Configuration & config, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(os, + oops::validateAndDeserialize(config), + std::move(flags), + std::move(obserr)) +{} // ----------------------------------------------------------------------------- -void FilterBase::postFilter(const ioda::ObsVector & hofx, const ObsDiagnostics & diags) { - oops::Log::trace() << "FilterBase postFilter begin" << std::endl; - if (post_) { - data_.associate(hofx, "HofX"); - data_.associate(diags); - this->doFilter(); - } - oops::Log::trace() << "FilterBase postFilter end" << std::endl; +FilterBase::~FilterBase() { + oops::Log::trace() << "FilterBase destructed" << std::endl; } // ----------------------------------------------------------------------------- @@ -106,8 +77,8 @@ void FilterBase::postFilter(const ioda::ObsVector & hofx, const ObsDiagnostics & void FilterBase::doFilter() const { oops::Log::trace() << "FilterBase doFilter begin" << std::endl; -// Select where the background check will apply - std::vector apply = processWhere(config_, data_); +// Select locations to which the filter will be applied + std::vector apply = processWhere(whereParameters_, data_); // Allocate flagged obs indicator (false by default) const size_t nvars = filtervars_.nvars(); @@ -118,11 +89,8 @@ void FilterBase::doFilter() const { this->applyFilter(apply, filtervars_, flagged); // Take action - eckit::LocalConfiguration aconf; - config_.get("action", aconf); - aconf.set("flag", this->qcFlag()); - FilterAction action(aconf); - action.apply(filtervars_, flagged, data_, *flags_, *obserr_); + FilterAction action(*actionParameters_); + action.apply(filtervars_, flagged, data_, this->qcFlag(), *flags_, *obserr_); // Done oops::Log::trace() << "FilterBase doFilter end" << std::endl; diff --git a/src/ufo/filters/FilterBase.h b/src/ufo/filters/FilterBase.h index 133f6b1e7..6c7a8892b 100644 --- a/src/ufo/filters/FilterBase.h +++ b/src/ufo/filters/FilterBase.h @@ -17,7 +17,10 @@ #include "oops/base/Variables.h" #include "oops/util/ObjectCounter.h" #include "oops/util/Printable.h" +#include "ufo/filters/FilterParametersBase.h" #include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/ObsProcessorBase.h" +#include "ufo/filters/processWhere.h" #include "ufo/filters/Variables.h" namespace eckit { @@ -34,45 +37,37 @@ namespace ufo { class GeoVaLs; class ObsDiagnostics; -/// FilterBase: Base class for UFO QC filters +/// \brief Base class for UFO QC filters +/// +/// Filters only need to implement the constructor and the applyFilter, print and qcFlag methods; +/// the base class takes care of applying the filter at the pre, prior or post stage. -// Filters only need to implement the constructor and the applyFilter method, -// the base class takes care of applying the filter at the pre, prior or post stage. - -class FilterBase : public util::Printable { +class FilterBase : public ObsProcessorBase { public: + FilterBase(ioda::ObsSpace &, const FilterParametersBaseWithAbstractAction ¶meters, + std::shared_ptr >, + std::shared_ptr >); FilterBase(ioda::ObsSpace &, const eckit::Configuration &, std::shared_ptr >, std::shared_ptr >); ~FilterBase(); - void preProcess(); - void priorFilter(const GeoVaLs &); - void postFilter(const ioda::ObsVector &, const ObsDiagnostics &); - - oops::Variables requiredVars() const { - return allvars_.allFromGroup("GeoVaLs").toOopsVariables();} - oops::Variables requiredHdiagnostics() const { - return allvars_.allFromGroup("ObsDiag").toOopsVariables();} - protected: - ioda::ObsSpace & obsdb_; + /// For backward compatibility, the full set of filter options (including those required only by + /// the concrete subclass, not by FilterBase) is stored in this LocalConfiguration object. + /// It will be removed once all filters have been converted to use Parameters. const eckit::LocalConfiguration config_; - std::shared_ptr> flags_; - std::shared_ptr> obserr_; - ufo::Variables allvars_; ufo::Variables filtervars_; - ObsFilterData data_; private: - void doFilter() const; - virtual void print(std::ostream &) const = 0; + void doFilter() const override; + void print(std::ostream &) const override = 0; virtual void applyFilter(const std::vector &, const Variables &, std::vector> &) const = 0; virtual int qcFlag() const = 0; - bool prior_; - bool post_; - bool defer_to_post_; + + std::vector whereParameters_; + std::unique_ptr actionParameters_; }; } // namespace ufo diff --git a/src/ufo/filters/FilterParametersBase.h b/src/ufo/filters/FilterParametersBase.h new file mode 100644 index 000000000..59cfccef2 --- /dev/null +++ b/src/ufo/filters/FilterParametersBase.h @@ -0,0 +1,100 @@ +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_FILTERPARAMETERSBASE_H_ +#define UFO_FILTERS_FILTERPARAMETERSBASE_H_ + +#include + +#include "eckit/config/LocalConfiguration.h" +#include "oops/base/ObsFilterParametersBase.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/PolymorphicParameter.h" +#include "ufo/filters/actions/FilterActionBase.h" // for FilterActionFactory +#include "ufo/filters/processWhere.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +/// \brief Parameters controlling the action performed on observations flagged by a filter. +class FilterActionParameters : public oops::Parameters { + // We call this macro instead of "plain" OOPS_CONCRETE_PARAMETERS() because we want to customize + // the constructor definition (adding an optional parameter `defaultAction`). + OOPS_CONCRETE_PARAMETERS_ENABLE_COPY_AND_MOVE(FilterActionParameters, Parameters) + + public: + /// Create an instance of this class that by default (i.e. unless overridden by settings loaded + /// from a Configuration object during deserialization) represents the action \p defaultAction. + explicit FilterActionParameters(Parameters *parent = nullptr, + const char *defaultAction = "reject") + : Parameters(parent), actionParameters("name", defaultAction, this) + {} + + /// After deserialization, holds an instance of a subclass of FilterActionParametersBase + /// controlling the behavior of a filter action. The type of the subclass is determined + /// by the value of the `name` key in the Configuration object from which this object + /// is deserialized. + oops::PolymorphicParameter actionParameters; +}; + +/// \brief Parameters shared by all filters derived from FilterBase. +class FilterParametersBaseWithAbstractAction : public oops::ObsFilterParametersBase { + OOPS_ABSTRACT_PARAMETERS(FilterParametersBaseWithAbstractAction, ObsFilterParametersBase) + + public: + /// Variables (and channels) to which the filter should be applied. These will be the only + /// variables whose QC flags or error estimates may be modified. If not specified, defaults to + /// all simulated variables in the ObsSpace. + oops::OptionalParameter> filterVariables{ + "filter variables", this}; + + /// Conditions used to select observations to which the filter should be applied. + /// If not specified, the filter will be applied to all observations. + /// + /// \note The QC flags of observations to which a filter is not applied won't normally be + /// modified (i.e. those observations will automatically "pass", unless of course they have + /// already been rejected by another filter). The only exception is the Domain Check filter, + /// which does the exact opposite: it rejects all observations that haven't been selected by the + /// `where` statement. + oops::Parameter> where{"where", {}, this}; + + /// If set to true, the filter will be executed only after the obs operator (even if it + /// doesn't require any variables from the GeoVaLs or HofX groups). + oops::Parameter deferToPost{"defer to post", false, this}; + + /// Return parameters defining the action performed on observations flagged by the filter. + virtual const FilterActionParametersBase &action() const = 0; +}; + +/// \brief Parameters shared by all filters having a default action (typically "reject"). +class FilterParametersBase : public FilterParametersBaseWithAbstractAction { + OOPS_ABSTRACT_PARAMETERS_ENABLE_COPY_AND_MOVE(FilterParametersBase, + FilterParametersBaseWithAbstractAction) + + protected: + /// \brief Create an instance of this class configured so that if the filter action is not + /// specified, it defaults to \p defaultAction. + explicit FilterParametersBase(Parameters* parent = nullptr, const char *defaultAction = "reject") + : FilterParametersBaseWithAbstractAction(parent), + action_("action", FilterActionParameters(nullptr, defaultAction), this) + {} + + public: + const FilterActionParametersBase &action() const override { + return action_.value().actionParameters.value(); + } + + private: + /// Parameters controlling the action performed on observations flagged by the filter. + oops::Parameter action_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_FILTERPARAMETERSBASE_H_ diff --git a/src/ufo/filters/GaussianThinningParameters.h b/src/ufo/filters/GaussianThinningParameters.h index 88dd61453..c423a42f7 100644 --- a/src/ufo/filters/GaussianThinningParameters.h +++ b/src/ufo/filters/GaussianThinningParameters.h @@ -15,6 +15,7 @@ #include "oops/util/parameters/OptionalParameter.h" #include "oops/util/parameters/Parameter.h" #include "oops/util/parameters/Parameters.h" +#include "ufo/filters/FilterParametersBase.h" #include "ufo/utils/Constants.h" #include "ufo/utils/parameters/ParameterTraitsVariable.h" @@ -51,8 +52,8 @@ struct ParameterTraits : namespace ufo { /// \brief Options controlling the operation of the Gaussian_Thinning filter. -class GaussianThinningParameters : public oops::Parameters { - OOPS_CONCRETE_PARAMETERS(GaussianThinningParameters, Parameters) +class GaussianThinningParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(GaussianThinningParameters, FilterParametersBase) public: // Horizontal grid @@ -103,8 +104,16 @@ class GaussianThinningParameters : public oops::Parameters { // Observation categories - /// Variable storing integer-valued IDs associated with observations. Observations belonging - /// to different categories are thinned separately. + /// A string-valued or integer-valued variable. Observations with different values of that + /// variable are thinned separately. + /// + /// Note: The filter will automatically detect if the chosen variable was also used to group + /// observations into records when the ObsSpace was constructed, and if so, avoid exchanging + /// data with other MPI processes, since in these circumstances each process can thin its + /// observations independently from others. + /// + /// The variable used to group observations into records can be set with the + /// `obs space.obsdatain.obsgrouping.group variable` YAML option. oops::OptionalParameter categoryVariable{"category_variable", this}; // Selection of observations to retain diff --git a/src/ufo/filters/Gaussian_Thinning.cc b/src/ufo/filters/Gaussian_Thinning.cc index f0ed891ce..b1c6bcbc9 100644 --- a/src/ufo/filters/Gaussian_Thinning.cc +++ b/src/ufo/filters/Gaussian_Thinning.cc @@ -22,76 +22,50 @@ #include "oops/util/Logger.h" #include "oops/util/missingValues.h" #include "ufo/filters/GaussianThinningParameters.h" +#include "ufo/filters/ObsAccessor.h" #include "ufo/utils/Constants.h" #include "ufo/utils/EquispacedBinSelector.h" #include "ufo/utils/GeodesicDistanceCalculator.h" #include "ufo/utils/MaxNormDistanceCalculator.h" -#include "ufo/utils/ParallelObsDistribution.h" #include "ufo/utils/RecursiveSplitter.h" #include "ufo/utils/SpatialBinSelector.h" namespace ufo { -namespace { - -/// -/// \brief Gather data from all tasks and deliver the combined data to all tasks. -/// -/// \returns A vector that contains the elements of \p v from process 0 followed by the elements -/// of \p v from process 1 etc. -/// -template -std::vector allGatherv(const eckit::mpi::Comm &comm, const std::vector &v) { - eckit::mpi::Buffer buffer(comm.size()); - comm.allGatherv(v.begin(), v.end(), buffer); - return buffer.buffer; -} - -} // namespace - // ----------------------------------------------------------------------------- -Gaussian_Thinning::Gaussian_Thinning(ioda::ObsSpace & obsdb, const eckit::Configuration & config, +Gaussian_Thinning::Gaussian_Thinning(ioda::ObsSpace & obsdb, + const GaussianThinningParameters & params, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr) + : FilterBase(obsdb, params, flags, obserr), options_(params) { - oops::Log::debug() << "Gaussian_Thinning: config = " << config_ << std::endl; - - options_.reset(new GaussianThinningParameters()); - options_->deserialize(config); + oops::Log::debug() << "Gaussian_Thinning: config = " << options_ << std::endl; } // ----------------------------------------------------------------------------- -// Required for the correct destruction of options_. -Gaussian_Thinning::~Gaussian_Thinning() -{} - -// ----------------------------------------------------------------------------- - void Gaussian_Thinning::applyFilter(const std::vector & apply, const Variables & filtervars, std::vector> & flagged) const { - ParallelObsDistribution obsDistribution(obsdb_); + ObsAccessor obsAccessor = createObsAccessor(); - std::vector validObsIds = getValidObservationIds(apply, obsDistribution); + const std::vector validObsIds = obsAccessor.getValidObservationIds(apply, *flags_); - RecursiveSplitter splitter(validObsIds.size()); std::vector distancesToBinCenter(validObsIds.size(), 0.f); - std::unique_ptr distanceCalculator = makeDistanceCalculator(*options_); + std::unique_ptr distanceCalculator = makeDistanceCalculator(options_); - groupObservationsByCategory(validObsIds, obsDistribution, splitter); - groupObservationsByPressure(validObsIds, *distanceCalculator, obsDistribution, + RecursiveSplitter splitter = obsAccessor.splitObservationsIntoIndependentGroups(validObsIds); + groupObservationsByPressure(validObsIds, *distanceCalculator, obsAccessor, splitter, distancesToBinCenter); - groupObservationsByTime(validObsIds, *distanceCalculator, obsDistribution, + groupObservationsByTime(validObsIds, *distanceCalculator, obsAccessor, splitter, distancesToBinCenter); - groupObservationsBySpatialLocation(validObsIds, *distanceCalculator, obsDistribution, + groupObservationsBySpatialLocation(validObsIds, *distanceCalculator, obsAccessor, splitter, distancesToBinCenter); const std::vector isThinned = identifyThinnedObservations( - validObsIds, obsDistribution, splitter, distancesToBinCenter); - flagThinnedObservations(isThinned, obsDistribution, flagged); + validObsIds, obsAccessor, splitter, distancesToBinCenter); + obsAccessor.flagRejectedObservations(isThinned, flagged); if (filtervars.size() != 0) { oops::Log::trace() << "Gaussian_Thinning: flagged? = " << flagged[0] << std::endl; @@ -100,16 +74,13 @@ void Gaussian_Thinning::applyFilter(const std::vector & apply, // ----------------------------------------------------------------------------- -std::vector Gaussian_Thinning::getValidObservationIds( - const std::vector & apply, const ParallelObsDistribution &obsDistribution) const { - const size_t rank = obsdb_.comm().rank(); - const size_t obsIdDisplacement = obsDistribution.localObsIdDisplacements()[rank]; - std::vector validObsIds; - for (size_t obsId = 0; obsId < apply.size(); ++obsId) - if (apply[obsId] && (*flags_)[0][obsId] == QCflags::pass) - validObsIds.push_back(obsIdDisplacement + obsId); - - return allGatherv(obsdb_.comm(), validObsIds); +ObsAccessor Gaussian_Thinning::createObsAccessor() const { + if (options_.categoryVariable.value() != boost::none) { + return ObsAccessor::toObservationsSplitIntoIndependentGroupsByVariable( + obsdb_, *options_.categoryVariable.value()); + } else { + return ObsAccessor::toAllObservations(obsdb_); + } } // ----------------------------------------------------------------------------- @@ -130,10 +101,10 @@ std::unique_ptr Gaussian_Thinning::makeDistanceCalculator( void Gaussian_Thinning::groupObservationsBySpatialLocation( const std::vector &validObsIds, const DistanceCalculator &distanceCalculator, - const ParallelObsDistribution &obsDistribution, + const ObsAccessor &obsAccessor, RecursiveSplitter &splitter, std::vector &distancesToBinCenter) const { - boost::optional binSelector = makeSpatialBinSelector(*options_); + boost::optional binSelector = makeSpatialBinSelector(options_); if (binSelector == boost::none) return; @@ -142,10 +113,8 @@ void Gaussian_Thinning::groupObservationsBySpatialLocation( oops::Log::debug() << "Gaussian_Thinning: number of horizontal bins = " << binSelector->totalNumBins() << std::endl; - const std::vector lat = getGlobalVariableValues( - obsdb_, obsDistribution, "latitude", "MetaData"); - std::vector lon = getGlobalVariableValues( - obsdb_, obsDistribution, "longitude", "MetaData"); + std::vector lat = obsAccessor.getFloatVariableFromObsSpace("MetaData", "latitude"); + std::vector lon = obsAccessor.getFloatVariableFromObsSpace("MetaData", "longitude"); // Longitudes will typically be either in the [-180, 180] degree range or in the [0, 360] // degree range. The spatial bin selector is written with the latter convention in mind, // so let's shift any negative longitudes up by 360 degrees. @@ -213,40 +182,20 @@ boost::optional Gaussian_Thinning::makeSpatialBinSelector( // ----------------------------------------------------------------------------- -void Gaussian_Thinning::groupObservationsByCategory( - const std::vector &validObsIds, - const ParallelObsDistribution &obsDistribution, - RecursiveSplitter &splitter) const { - boost::optional categoryVariable = options_->categoryVariable; - if (categoryVariable == boost::none) - return; - - const std::vector category = getGlobalVariableValues( - obsdb_, obsDistribution, categoryVariable.get().variable(), categoryVariable.get().group()); - - std::vector validObsCategories(validObsIds.size()); - for (size_t validObsIndex = 0; validObsIndex < validObsIds.size(); ++validObsIndex) - validObsCategories[validObsIndex] = category[validObsIds[validObsIndex]]; - splitter.groupBy(validObsCategories); -} - -// ----------------------------------------------------------------------------- - void Gaussian_Thinning::groupObservationsByPressure( const std::vector &validObsIds, const DistanceCalculator &distanceCalculator, - const ParallelObsDistribution &obsDistribution, + const ObsAccessor &obsAccessor, RecursiveSplitter &splitter, std::vector &distancesToBinCenter) const { - boost::optional binSelector = makePressureBinSelector(*options_); + boost::optional binSelector = makePressureBinSelector(options_); if (binSelector == boost::none) return; oops::Log::debug() << "Gaussian_Thinning: number of vertical bins = " << binSelector->numBins() << std::endl; - const std::vector pres = getGlobalVariableValues( - obsdb_, obsDistribution, "air_pressure", "MetaData"); + std::vector pres = obsAccessor.getFloatVariableFromObsSpace("MetaData", "air_pressure"); std::vector bins; bins.reserve(validObsIds.size()); @@ -294,19 +243,19 @@ boost::optional Gaussian_Thinning::makePressureBinSelecto void Gaussian_Thinning::groupObservationsByTime( const std::vector &validObsIds, const DistanceCalculator &distanceCalculator, - const ParallelObsDistribution &obsDistribution, + const ObsAccessor &obsAccessor, RecursiveSplitter &splitter, std::vector &distancesToBinCenter) const { util::DateTime timeOffset; - boost::optional binSelector = makeTimeBinSelector(*options_, timeOffset); + boost::optional binSelector = makeTimeBinSelector(options_, timeOffset); if (binSelector == boost::none) return; oops::Log::debug() << "Gaussian_Thinning: number of time bins = " << binSelector->numBins() << std::endl; - const std::vector times = getGlobalVariableValues( - obsdb_, obsDistribution, "datetime", "MetaData"); + std::vector times = obsAccessor.getDateTimeVariableFromObsSpace( + "MetaData", "datetime"); std::vector bins; bins.reserve(validObsIds.size()); @@ -368,14 +317,16 @@ boost::optional Gaussian_Thinning::makeTimeBinSelector( std::vector Gaussian_Thinning::identifyThinnedObservations( const std::vector &validObsIds, - const ParallelObsDistribution &obsDistribution, + const ObsAccessor &obsAccessor, const RecursiveSplitter &splitter, const std::vector &distancesToBinCenter) const { std::function comparator = makeObservationComparator( - validObsIds, distancesToBinCenter, obsDistribution); + validObsIds, distancesToBinCenter, obsAccessor); - std::vector isThinned(obsDistribution.globalObsCount(), false); + size_t totalNumObs = obsAccessor.totalNumObservations(); + + std::vector isThinned(totalNumObs, false); for (auto group : splitter.multiElementGroups()) { const size_t bestValidObsIndex = *std::min_element( std::begin(group), std::end(group), comparator); @@ -392,19 +343,19 @@ std::vector Gaussian_Thinning::identifyThinnedObservations( std::function Gaussian_Thinning::makeObservationComparator( const std::vector &validObsIds, const std::vector &distancesToBinCenter, - const ParallelObsDistribution &obsDistribution) const + const ObsAccessor &obsAccessor) const { - if (options_->priorityVariable.value() == boost::none) { + if (options_.priorityVariable.value() == boost::none) { oops::Log::debug() << "priority_variable not found" << std::endl; return [&distancesToBinCenter](size_t validObsIndexA, size_t validObsIndexB) { return distancesToBinCenter[validObsIndexA] < distancesToBinCenter[validObsIndexB]; }; } - const ufo::Variable priorityVariable = options_->priorityVariable.value().get(); + const ufo::Variable priorityVariable = options_.priorityVariable.value().get(); - const std::vector priorities = getGlobalVariableValues( - obsdb_, obsDistribution, priorityVariable.variable(), priorityVariable.group()); + std::vector priorities = obsAccessor.getIntVariableFromObsSpace( + priorityVariable.group(), priorityVariable.variable()); oops::Log::debug() << "priorities = " << priorities << std::endl; @@ -421,26 +372,8 @@ std::function Gaussian_Thinning::makeObservationComparator // ----------------------------------------------------------------------------- -void Gaussian_Thinning::flagThinnedObservations( - const std::vector & isThinned, - const ParallelObsDistribution &obsDistribution, - std::vector> & flagged) const { - const size_t rank = obsdb_.comm().rank(); - const size_t displacement = obsDistribution.localObsIdDisplacements()[rank]; - for (std::vector & variableFlagged : flagged) { - ASSERT(obsDistribution.localObsCounts()[rank] == variableFlagged.size()); - for (size_t localObsId = 0; localObsId < variableFlagged.size(); ++localObsId) { - const size_t globalObsId = displacement + localObsId; - if (isThinned[globalObsId]) - variableFlagged[localObsId] = true; - } - } -} - -// ----------------------------------------------------------------------------- - void Gaussian_Thinning::print(std::ostream & os) const { - os << "Gaussian_Thinning: config = " << config_ << std::endl; + os << "Gaussian_Thinning: config = " << options_ << std::endl; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/Gaussian_Thinning.h b/src/ufo/filters/Gaussian_Thinning.h index 8d36de1e8..2bbfcd48e 100644 --- a/src/ufo/filters/Gaussian_Thinning.h +++ b/src/ufo/filters/Gaussian_Thinning.h @@ -18,6 +18,7 @@ #include "ioda/ObsDataVector.h" #include "oops/util/ObjectCounter.h" #include "ufo/filters/FilterBase.h" +#include "ufo/filters/GaussianThinningParameters.h" #include "ufo/filters/QCflags.h" namespace eckit { @@ -38,9 +39,9 @@ namespace ufo { class DistanceCalculator; class EquispacedBinSelector; class GaussianThinningParameters; +class ObsAccessor; class RecursiveSplitter; class SpatialBinSelector; -class ParallelObsDistribution; /// \brief Group observations into grid cells and preserve only one observation in each cell. /// @@ -58,59 +59,52 @@ class ParallelObsDistribution; class Gaussian_Thinning : public FilterBase, private util::ObjectCounter { public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef GaussianThinningParameters Parameters_; + static const std::string classname() {return "ufo::Gaussian_Thinning";} - Gaussian_Thinning(ioda::ObsSpace &obsdb, const eckit::Configuration &config, + Gaussian_Thinning(ioda::ObsSpace &obsdb, const GaussianThinningParameters ¶ms, std::shared_ptr > flags, std::shared_ptr > obserr); - ~Gaussian_Thinning() override; - private: void print(std::ostream &) const override; void applyFilter(const std::vector &, const Variables &, std::vector> &) const override; int qcFlag() const override {return QCflags::thinned;} - std::vector getValidObservationIds(const std::vector &apply, - const ParallelObsDistribution &obsDistribution) const; - - void groupObservationsByCategory(const std::vector &validObsIds, - const ParallelObsDistribution &obsDistribution, - RecursiveSplitter &splitter) const; + ObsAccessor createObsAccessor() const; void groupObservationsBySpatialLocation(const std::vector &validObsIds, const DistanceCalculator &distanceCalculator, - const ParallelObsDistribution &obsDistribution, + const ObsAccessor &obsAccessor, RecursiveSplitter &splitter, std::vector &distancesToBinCenter) const; void groupObservationsByPressure(const std::vector &validObsIds, const DistanceCalculator &distanceCalculator, - const ParallelObsDistribution &obsDistribution, + const ObsAccessor &obsAccessor, RecursiveSplitter &splitter, std::vector &distancesToBinCenter) const; void groupObservationsByTime(const std::vector &validObsIds, const DistanceCalculator &distanceCalculator, - const ParallelObsDistribution &obsDistribution, + const ObsAccessor &obsAccessor, RecursiveSplitter &splitter, std::vector &distancesToBinCenter) const; std::vector identifyThinnedObservations( const std::vector &validObsIds, - const ParallelObsDistribution &obsDistribution, + const ObsAccessor &obsAccessor, const RecursiveSplitter &splitter, const std::vector &distancesToBinCenter) const; std::function makeObservationComparator( const std::vector &validObsIds, const std::vector &distancesToBinCenter, - const ParallelObsDistribution &obsDistribution) const; - - void flagThinnedObservations(const std::vector &isThinned, - const ParallelObsDistribution &obsDistribution, - std::vector > &flagged) const; + const ObsAccessor &obsAccessor) const; static boost::optional makeSpatialBinSelector( const GaussianThinningParameters &options); @@ -125,7 +119,7 @@ class Gaussian_Thinning : public FilterBase, const GaussianThinningParameters &options); private: - std::unique_ptr options_; + GaussianThinningParameters options_; }; } // namespace ufo diff --git a/src/ufo/filters/GenericFilterParameters.h b/src/ufo/filters/GenericFilterParameters.h new file mode 100644 index 000000000..e6f260c6d --- /dev/null +++ b/src/ufo/filters/GenericFilterParameters.h @@ -0,0 +1,34 @@ +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_GENERICFILTERPARAMETERS_H_ +#define UFO_FILTERS_GENERICFILTERPARAMETERS_H_ + +#include "oops/util/parameters/ConfigurationParameter.h" +#include "ufo/filters/FilterParametersBase.h" + +namespace ufo { + +/// \brief A subclass of FilterParametersBase storing the values of all filter options in a +/// single Configuration object. +/// +/// This object can be accessed by calling the value() method of the \p config member variable. +/// +/// \note **Do not use this class if you're writing a new filter** because the +/// ConfigurationParameter class does not perform any parameter validation. Instead, define a new +/// subclass of FilterParametersBase storing each parameter accepted by your filter in a separate +/// (Optional/Required)Parameter object. Existing filters using GenericFilterParameters should also +/// be refactored in this way when time allows. +class GenericFilterParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(GenericFilterParameters, FilterParametersBase) + public: + oops::ConfigurationParameter config{this}; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_GENERICFILTERPARAMETERS_H_ diff --git a/src/ufo/filters/HistoryCheck.cc b/src/ufo/filters/HistoryCheck.cc new file mode 100644 index 000000000..230dc960c --- /dev/null +++ b/src/ufo/filters/HistoryCheck.cc @@ -0,0 +1,296 @@ +/* + * (C) 2021 Crown Copyright Met Office. All rights reserved. + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/HistoryCheck.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "eckit/config/Configuration.h" +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" +#include "oops/base/Variables.h" +#include "oops/mpi/mpi.h" +#include "oops/util/DateTime.h" +#include "oops/util/Duration.h" +#include "oops/util/Logger.h" +#include "ufo/filters/HistoryCheckParameters.h" +#include "ufo/filters/ObsAccessor.h" +#include "ufo/filters/QCflags.h" +#include "ufo/filters/StuckCheck.h" +#include "ufo/filters/TrackCheckShip.h" +#include "ufo/filters/TrackCheckUtils.h" +#include "ufo/utils/Constants.h" +#include "ufo/utils/RecursiveSplitter.h" + +namespace ufo { + +HistoryCheck::HistoryCheck(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), options_(parameters) +{ + oops::Log::debug() << "HistoryCheck: config = " << options_ << "\n"; +} + +HistoryCheck::HistoryCheck(ioda::ObsSpace &obsdb, + const ufo::HistoryCheck::Parameters_ ¶meters, + std::shared_ptr > flags, + std::shared_ptr > obserr, + const eckit::LocalConfiguration &conf) + : HistoryCheck(obsdb, parameters, flags, obserr) +{ + unitTestConfig_ = conf; +} + +/// This filter runs the ship track check and stuck check filters consecutively over an auxiliary +/// obs space (which is assumed to be a superset of \p obsdb_ with an earlier starting time), +/// before checking which observations have both been (1) flagged by either of the sub-filters +/// from the superset obs space and (2) are located within \p obsdb_ +void HistoryCheck::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector > & flagged) const { + util::DateTime widerWindowStart = obsdb_.windowStart() - options_.timeBeforeStartOfWindow.value(); + // In order to prevent the MPI from distributing the aux spaces's observations to different + // ranks from the distribution used for obsdb_, widerObsSpace uses the myself communicator + // for both time and spatial communicators, ensuring that all observations in widerObsSpace + // are saved to all ranks + ioda::ObsSpace widerObsSpace(options_.largerObsSpace, oops::mpi::myself(), widerWindowStart, + obsdb_.windowEnd(), oops::mpi::myself()); + if (options_.resetLargerObsSpaceVariables) { // used for unit testing + if (unitTestConfig_.has("station_ids_wide")) { + const std::vector stationIds = unitTestConfig_.getIntVector("station_ids_wide"); + widerObsSpace.put_db("MetaData", "station_id", stationIds); + } else if (unitTestConfig_.has("station_ids_wide_string")) { + const std::vector stationIds = + unitTestConfig_.getStringVector("station_ids_wide_string"); + widerObsSpace.put_db("MetaData", "station_id", stationIds); + } + if (unitTestConfig_.has("air_temperatures_wide")) { + const std::vector airTemperatures = + unitTestConfig_.getFloatVector("air_temperatures_wide"); + widerObsSpace.put_db("ObsValue", "air_temperature", airTemperatures); + } + } // end of manual data entry section used for unit testing + std::shared_ptr> obserrWide( + new ioda::ObsDataVector(widerObsSpace, widerObsSpace.obsvariables(), "ObsError")); + std::shared_ptr> qcflagsWide( + new ioda::ObsDataVector(widerObsSpace, widerObsSpace.obsvariables())); + const oops::RequiredParameter &subtype = + options_.surfaceObservationSubtype; + const boost::optional &trackOptions = + options_.trackCheckShipParameters.value(); + // If the observation type is one which the track check ship filter should be run on and + // the necessary filter parameters were set within the configuration file + if (subtype != SurfaceObservationSubtype::LNDSYB && + subtype != SurfaceObservationSubtype::LNDSYN && + trackOptions) { + // Collecting parameters relevant for running the track check ship filter on the wider obs space + eckit::LocalConfiguration configTrackCheckShip = + trackOptions->toConfiguration(); + subtype.serialize(configTrackCheckShip); + // Importing the base parameters + options_.TrackCheckUtilsParameters::serialize(configTrackCheckShip); + ufo::TrackCheckShipParameters trackParams; + trackParams.deserialize(configTrackCheckShip); + // Setting up and running the track check ship filter on the wider obs space + ufo::TrackCheckShip trackCheck(widerObsSpace, trackParams, + qcflagsWide, obserrWide); + trackCheck.preProcess(); + } + const boost::optional &stuckOptions = + options_.stuckCheckParameters; + // If the stuck check filter parameters were set and if the stuck check filter has the potential + // to flag observations (number of observations is greater than the numberStuckTolerance value) + if (stuckOptions && + widerObsSpace.index().size() > + (stuckOptions)->numberStuckTolerance) { + // If the observation subtype is one which the stuck check filter should be run on + if (subtype != SurfaceObservationSubtype::TEMP && + subtype != SurfaceObservationSubtype::BATHY && + subtype != SurfaceObservationSubtype::TESAC && + subtype != SurfaceObservationSubtype::BUOYPROF) { + // Collecting the relevant parameters for running the stuck check filter + eckit::LocalConfiguration configStuckCheck = + stuckOptions->toConfiguration(); + options_.TrackCheckUtilsParameters::serialize(configStuckCheck); + ufo::StuckCheckParameters stuckParams; + stuckParams.deserialize(configStuckCheck); + // Setting up and running the stuck check filter on the wider obs space + ufo::StuckCheck stuckCheck(widerObsSpace, stuckParams, + qcflagsWide, obserrWide); + stuckCheck.preProcess(); + } + } + // Creating obs accessors for both obs spaces, assuming the same variable is used for grouping + // into stations on both obs spaces + ObsAccessor historicalObsAccessor = TrackCheckUtils::createObsAccessor(options_.stationIdVariable, + widerObsSpace); + ObsAccessor windowObsAccessor = + TrackCheckUtils::createObsAccessor(options_.stationIdVariable, obsdb_); + + std::vector wideDts = historicalObsAccessor.getDateTimeVariableFromObsSpace( + "MetaData", "datetime"); + std::vector wideLats = historicalObsAccessor.getFloatVariableFromObsSpace( + "MetaData", "latitude"); + std::vector wideLons = historicalObsAccessor.getFloatVariableFromObsSpace( + "MetaData", "longitude"); + + std::map stationIdMap; + + // Create a "central source of truth" map of integer index values for each + // string-valued station id across both obs spaces. + const boost::optional &statIdVar = options_.stationIdVariable.value(); + // If the station id variable is in use, and the underlying ids are string-formatted. + if (statIdVar != boost::none && + obsdb_.dtype(statIdVar->group(), statIdVar->variable()) == ioda::ObsDtype::String) { + // retrieve the station ids from both obs spaces + std::vector wideIds = historicalObsAccessor.getStringVariableFromObsSpace( + statIdVar->group(), statIdVar->variable()); + std::vector windowIds = windowObsAccessor.getStringVariableFromObsSpace( + statIdVar->group(), statIdVar->variable()); + // append the assimilation window ids to the end of the historical obs space's ids + std::move(windowIds.begin(), windowIds.end(), std::back_inserter(wideIds)); + // remove all non-unique station ids from the collection of ids + std::sort(wideIds.begin(), wideIds.end()); + const auto endOfUnique = std::unique(wideIds.begin(), wideIds.end()); + wideIds.erase(endOfUnique, wideIds.end()); + // map all unique station ids across both obs spaces to an integer index + int index = 0; + for (const std::string& value : wideIds) + stationIdMap[value] = index++; + } + + std::vector wideRecordIds = getStationIds(stationIdMap, + options_.stationIdVariable.value(), + widerObsSpace, historicalObsAccessor); + + // obsIdentifierData: all of the MetaData needed to uniquely identify each observation + // MetaData in use: time stamp, lat/lon coordinates, the station id + // (actual or integer equivalent), and an additional number for differentiating identical + // observations + typedef std::tuple obsIdentifierData; + + // Collect identifiers of each flagged observation from the wider obs space (which was used to + // run the stuck check and/or the track check ship filters. Increment "differentiator + // counter" until the full identifier to-be-added is unique within the set. + std::set wideFlaggedLocationIds; + // qc flags are the same across all variables for these filters + const std::vector &wideFlags = (*qcflagsWide)[0]; + for (size_t i = 0; i < wideFlags.size(); i++) { + if (wideFlags[i] == QCflags::track) { + obsIdentifierData obsLabel = { + wideDts.at(i), wideLats.at(i), wideLons.at(i), wideRecordIds.at(i), 0 + }; + while (wideFlaggedLocationIds.find(obsLabel) != wideFlaggedLocationIds.end()) { + (std::get<4>(obsLabel))++; + } + wideFlaggedLocationIds.insert(obsLabel); + } + } + // Retrieve relevant identifier information for all observations in assimilation window. + std::vector windowDts = windowObsAccessor.getDateTimeVariableFromObsSpace( + "MetaData", "datetime"); + std::vector windowLats = windowObsAccessor.getFloatVariableFromObsSpace( + "MetaData", "latitude"); + std::vector windowLons = windowObsAccessor.getFloatVariableFromObsSpace( + "MetaData", "longitude"); + std::vector windowStationIds = getStationIds(stationIdMap, + options_.stationIdVariable.value(), + obsdb_, windowObsAccessor); + + // Map obsIdentifierData for every assimilation window observation to its index within the + // window's full observation accessor. + std::map locationIdToIndex; + for (size_t i = 0; i < windowObsAccessor.totalNumObservations(); i++) { + // Set up all observation labels with differentiator counter initially set to 0 + obsIdentifierData obsLabel = {windowDts.at(i), windowLats.at(i), windowLons.at(i), + windowStationIds.at(i), 0}; + // If the observation label has already been added to the map, increment the differentiator + // counter until the label is not present within the location id map. + while (locationIdToIndex.find(obsLabel) != locationIdToIndex.end()) { + (std::get<4>(obsLabel))++; + } + locationIdToIndex.insert(std::pair(obsLabel, i)); + } + // Comparator defined in order to use set intersection operation for map of indexed + // assimilated observation ids and set of flagged observations from wider observation spaces + struct setIntersectionComparator { + bool operator()(const obsIdentifierData &lhs, + const std::pair &rhs) { + return lhs < rhs.first; + } + bool operator()(const std::pair &lhs, + const obsIdentifierData &rhs) { + return lhs.first < rhs; + } + }; + + // Iterate through flagged observations in the historical obs space, + // finding the observations which are also in the assimilation obs space, and + // marking the associated indices to flag using the ObsAccessor flagRejectedObservations method + std::vector globalObsToFlag(windowObsAccessor.totalNumObservations(), false); + for (const obsIdentifierData &id : wideFlaggedLocationIds) { + if (locationIdToIndex.find(id) != locationIdToIndex.end()) { + size_t locToFlag = locationIdToIndex.at(id); + globalObsToFlag.at(locToFlag) = true; + } + } + windowObsAccessor.flagRejectedObservations(globalObsToFlag, flagged); +} + +std::vector HistoryCheck::getStationIds(const std::map &stringMap, + const boost::optional &stationIdVar, + const ioda::ObsSpace &obsdb, + const ObsAccessor &obsacc) const { + if (stationIdVar == boost::none) { + if (obsdb.obs_group_vars().empty()) { + // Observations were not grouped into records. + // Assume all observations were taken by the same station. + return std::vector(obsacc.totalNumObservations(), 0); + } else { + const std::vector &recordNumbers = obsacc.getRecordIds(); + return std::vector(recordNumbers.begin(), recordNumbers.end()); + } + } else { + switch (obsdb.dtype(stationIdVar->group(), stationIdVar->variable())) { + case ioda::ObsDtype::Integer: + { + return obsacc.getIntVariableFromObsSpace(stationIdVar->group(), + stationIdVar->variable()); + } + + case ioda::ObsDtype::String: + { + std::vector stringIds = + obsacc.getStringVariableFromObsSpace(stationIdVar->group(), + stationIdVar->variable()); + std::vector ints; + ints.reserve(stringIds.size()); + std::transform(stringIds.begin(), stringIds.end(), std::back_inserter(ints), + [&stringMap](const std::string& value) { return stringMap.at(value); }); + return ints; + } + + default: + throw eckit::UserError("Only integer and string variables may be used as station IDs", + Here()); + } + } +} + +void HistoryCheck::print(std::ostream & os) const { + os << "HistoryCheck: config = " << config_ << '\n'; +} +} // namespace ufo diff --git a/src/ufo/filters/HistoryCheck.h b/src/ufo/filters/HistoryCheck.h new file mode 100644 index 000000000..837c727c5 --- /dev/null +++ b/src/ufo/filters/HistoryCheck.h @@ -0,0 +1,81 @@ +/* + * (C) Crown copyright 2021 Met Office. All rights reserved. + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ +#ifndef UFO_FILTERS_HISTORYCHECK_H_ +#define UFO_FILTERS_HISTORYCHECK_H_ + +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/HistoryCheckParameters.h" +#include "ufo/filters/QCflags.h" +#include "ufo/filters/TrackCheckUtils.h" + +namespace eckit { +class Configuration; +} + +namespace ioda { +template class ObsDataVector; +class ObsSpace; +} + +namespace ufo { +class ObsAccessor; +class HistoryCheck: public FilterBase, + private util::ObjectCounter { + public: + typedef HistoryCheckParameters Parameters_; + + static const std::string classname() {return "ufo::HistoryCheck";} + +/// Track checks and stuck value checks. +/// +/// 1. Read in wider window of observations +/// 2. Apply track check and stuck value check over wider window +/// 3. Apply flags over wider window to observations in main window. + HistoryCheck(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, + std::shared_ptr > flags, + std::shared_ptr > obserr); + + // This constructor is needed for unit testing + HistoryCheck(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, + std::shared_ptr > flags, + std::shared_ptr > obserr, + const eckit::LocalConfiguration &conf); + + private: + Parameters_ options_; + eckit::LocalConfiguration unitTestConfig_; + + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override { + return QCflags::history; + } + + /// \brief Retrieve all station ids from the ObsAccessor. If string-labelled, ids will be + /// converted to integers. + /// + /// \p stringMap A previously-created map of all unique string station ids across obs spaces, + /// used as a "central source-of-truth" for the integer values the strings should be mapped to. + /// \p stationIdVar The parameter used to specify which variable is used to store station ids. + /// \p obsdb The ObsSpace which station ids will be needed for. + /// \p obsacc The ObsAccessor used to access observations from the associated ObsSpace. + std::vector getStationIds(const std::map &stringMap, + const boost::optional &stationIdVar, + const ioda::ObsSpace &obsdb, + const ObsAccessor &obsacc) const; +}; + + +} // namespace ufo + +#endif // UFO_FILTERS_HISTORYCHECK_H_ diff --git a/src/ufo/filters/HistoryCheckParameters.h b/src/ufo/filters/HistoryCheckParameters.h new file mode 100644 index 000000000..e3ce62c5a --- /dev/null +++ b/src/ufo/filters/HistoryCheckParameters.h @@ -0,0 +1,74 @@ +/* + * (C) Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ +#ifndef UFO_FILTERS_HISTORYCHECKPARAMETERS_H_ +#define UFO_FILTERS_HISTORYCHECKPARAMETERS_H_ + +#include + +#include "oops/util/Duration.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/ParameterTraits.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/StuckCheckParameters.h" +#include "ufo/filters/TrackCheckShipParameters.h" +#include "ufo/filters/TrackCheckUtilsParameters.h" + +namespace ufo { + +/// \brief Options controlling the operation of history check filter. +class HistoryCheckParameters : public TrackCheckUtilsParameters { + OOPS_CONCRETE_PARAMETERS(HistoryCheckParameters, TrackCheckUtilsParameters) + + public: + /// Surface observation subtype determining if the track and/or stuck check could be run + /// \todo It is possible to use this to decide on the window length. Should the manual entry + /// method be kept? + oops::RequiredParameter surfaceObservationSubtype{ + "input category", this}; + + /// Amount of time before start of assimilation window to collect for the history check + oops::RequiredParameter timeBeforeStartOfWindow { + "time before start of window", this + }; + + /// The options for running the ship track check filter which can be optionally run, should the + /// subtype not be LNDSYN/LNDSYB. These must be filled in in order for the track filter to run. + oops::OptionalParameter trackCheckShipParameters { + "ship track check parameters", this + }; + + /// The options for running the stuck check filter which can be optionally run, should the + /// subtype not be TEMP/BATHY/TESAC/BUOYPROF. These must be filled in in order for the stuck + /// filter to run. + oops::OptionalParameter stuckCheckParameters { + "stuck check parameters", this + }; + + /// Creates a new obs space with the wider window that is determined by the observation subtype. + /// Needs: name (can be set with setValue), simulated variables, obsdatain.obsfile. + oops::RequiredParameter largerObsSpace { + "obs space", this + }; + + /// Controls whether all of the larger obs space's variables are reset to match the primary + /// obs space's when filter is run. Used for unit testing (esp. stuck check portions). + oops::Parameter resetLargerObsSpaceVariables { + "reset larger obs space variables", false, this + }; + + /// Maximum number of characters for a string-labelled station id. + /// This is used to ensure unique integer hashes if station ids are string labels. + oops::Parameter stationIdMaxStringLength { + "station id max string length", 24, this + }; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_HISTORYCHECKPARAMETERS_H_ diff --git a/src/ufo/filters/ImpactHeightCheck.cc b/src/ufo/filters/ImpactHeightCheck.cc new file mode 100644 index 000000000..9fd7c4855 --- /dev/null +++ b/src/ufo/filters/ImpactHeightCheck.cc @@ -0,0 +1,232 @@ +/* + * (C) British Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/ImpactHeightCheck.h" + +#include +#include +#include +#include +#include + + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" + +#include "oops/util/Logger.h" +#include "ufo/filters/QCflags.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +/// ImpactHeightCheck: Calculate the impact height for model profiles, and reject +/// any observations which are outside the range of model impact heights. Check +/// for any sharp refractivity gradients, and reject any observations below them. + +ImpactHeightCheck::ImpactHeightCheck( + ioda::ObsSpace & obsdb, + const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) +{ + oops::Log::trace() << "ImpactHeightCheck constructor" << std::endl; + allvars_ += Variable("refractivity@ObsDiag"); + allvars_ += Variable("model_heights@ObsDiag"); + allvars_ += Variable("impact_parameter@MetaData"); + allvars_ += Variable("earth_radius_of_curvature@MetaData"); +} + +// ----------------------------------------------------------------------------- + +ImpactHeightCheck::~ImpactHeightCheck() { + oops::Log::trace() << "ImpactHeightCheck destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- +/// Apply the filter: remove observations which are outside the range of +/// impact heights + +void ImpactHeightCheck::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const { + oops::Log::trace() << "ImpactHeightCheck post-filter" << std::endl; + const oops::Variables observed = obsdb_.obsvariables(); + const float missingFloat = util::missingValue(missingFloat); + + // Get the refractivity from the obs diagnostics, including the number of + // vertical levels on which the refractivity has been calculated (nRefLevels) + Variable refractivityVariable = Variable("refractivity@ObsDiag"); + oops::Log::debug() << data_.nlevs(refractivityVariable) << std::endl; + const size_t nRefLevels = data_.nlevs(refractivityVariable); + std::vector> refractivity; + + for (size_t iLevel = 1; iLevel < nRefLevels+1; ++iLevel) { + std::vector inputData; + data_.get(refractivityVariable, static_cast(iLevel), inputData); + refractivity.push_back(inputData); + } + + // For the benefits of debugging, output the refractivity for the first + // observation + oops::Log::debug() << "Refractivity(first ob) "; + for (size_t iLevel = 0; iLevel < nRefLevels; ++iLevel) { + oops::Log::debug() << refractivity[iLevel][0] << " "; + } + oops::Log::debug() << std::endl; + + // Get the height of the levels on which the refractivity has been calculated. + // Must be the same length as the array defining the refractivity. + Variable modelHeightsVariable = Variable("model_heights@ObsDiag"); + oops::Log::debug() << data_.nlevs(modelHeightsVariable) << std::endl; + if (data_.nlevs(modelHeightsVariable) != nRefLevels) { + throw eckit::BadValue("Model heights and refractivity must have the same number of levels", + Here()); + } + std::vector> modelHeights; + + // Read the heights of the refractivity levels + for (size_t iLevel = 1; iLevel < nRefLevels+1; ++iLevel) { + std::vector inputData; + data_.get(modelHeightsVariable, static_cast(iLevel), inputData); + modelHeights.push_back(inputData); + } + + // For debugging, output the heights of the refractivity levels for the first + // observation. + oops::Log::debug() << "Model heights (first ob) "; + for (size_t iLevel = 0; iLevel < nRefLevels; ++iLevel) { + oops::Log::debug() << modelHeights[iLevel][0] << " "; + } + oops::Log::debug() << std::endl; + + // Read in the observation impact parameter for each observation + Variable impactVariable = Variable("impact_parameter@MetaData"); + std::vector impactParameter; + data_.get(impactVariable, impactParameter); + + // Read in the earth's radius of curvature for each observation + Variable radiusCurvatureParameter = Variable("earth_radius_of_curvature@MetaData"); + std::vector radiusCurvature; + data_.get(radiusCurvatureParameter, radiusCurvature); + + // For each variable, perform the filter + for (size_t iFilterVar = 0; iFilterVar < filtervars.nvars(); ++iFilterVar) { + const size_t iVar = observed.find(filtervars.variable(iFilterVar).variable()); + + // Loop over the observations + for (size_t iObs = 0; iObs < obsdb_.nlocs(); ++iObs) { + if (apply[iObs] && (*flags_)[iVar][iObs] == QCflags::pass) { + // Load the refractivity profile and the associated model heights for this observation, + // cleansing any missing data + std::vector refracProfile; + std::vector heightProfile; + for (size_t iLevel = 0; iLevel < nRefLevels; ++iLevel) + if (refractivity[iLevel][iObs] != missingFloat && + modelHeights[iLevel][iObs] != missingFloat) { + refracProfile.push_back(refractivity[iLevel][iObs]); + heightProfile.push_back(modelHeights[iLevel][iObs]); + } + + if (refracProfile.size() < 2) { + oops::Log::error() << "Should have at least two valid points in every profile:" << + std::endl << "size = " << refracProfile.size() << " " << + "iObs = " << iObs << std::endl; + flagged[iFilterVar][iObs] = true; + continue; + } + + oops::Log::debug() << "Min and max refrac for profile " << iObs << + " is " << refracProfile[0] << " to " << refracProfile.back() << + std::endl; + oops::Log::debug() << "Impact height " << impactParameter[iObs] - radiusCurvature[iObs] << + std::endl; + + // Output the calculated refractivity gradient for the first observation + const std::vector & gradient = calcVerticalGradient(refracProfile, heightProfile); + if (iObs == 0) { + oops::Log::debug() << "Gradient found to be" << std::endl; + for (float grad : gradient) + oops::Log::debug() << grad << " "; + oops::Log::debug() << std::endl; + } + + float sharpGradientImpact = std::numeric_limits::lowest(); + // Search for sharp gradients (super-refraction) starting at the top of the profile + for (int iLevel = static_cast(nRefLevels)-2; iLevel >= 0; --iLevel) { + size_t thisLevel = static_cast(iLevel); + if (gradient[thisLevel] != missingFloat && + gradient[thisLevel] < parameters_.gradientThreshold.value()) { + sharpGradientImpact = calcImpactHeight(refracProfile[thisLevel], + heightProfile[thisLevel], + radiusCurvature[iObs]); + oops::Log::info() << "Sharp refractivity gradient of " << gradient[thisLevel] << + " found at " << thisLevel << " " << sharpGradientImpact << + std::endl; + oops::Log::debug() << thisLevel << " " << refracProfile[thisLevel] << " " << + heightProfile[thisLevel] << " " << + radiusCurvature[thisLevel] << std::endl; + break; + } + } + + // Reject observation if it is below the minimum (either surface or sharp gradient) + const float obsImpactHeight = impactParameter[iObs] - radiusCurvature[iObs]; + oops::Log::debug() << "Checking minimum height " << obsImpactHeight << " " << + sharpGradientImpact + parameters_.sharpGradientOffset.value() << + " " << calcImpactHeight(refracProfile[0], heightProfile[0], + radiusCurvature[iObs]) + + parameters_.surfaceOffset.value() << std::endl; + if (obsImpactHeight < sharpGradientImpact + parameters_.sharpGradientOffset.value() || + obsImpactHeight < calcImpactHeight(refracProfile[0], heightProfile[0], + radiusCurvature[iObs]) + + parameters_.surfaceOffset.value()) + flagged[iFilterVar][iObs] = true; + + // Reject observation if it is above the maximum + oops::Log::debug() << "Checking maximum height " << obsImpactHeight << " " << + calcImpactHeight(refracProfile.back(), heightProfile.back(), + radiusCurvature[iObs]) << std::endl; + if (obsImpactHeight > calcImpactHeight(refracProfile.back(), heightProfile.back(), + radiusCurvature[iObs])) + flagged[iFilterVar][iObs] = true; + } + } + } +} + +// ----------------------------------------------------------------------------- +/// Calculate the vertical gradient of refractivity, assuming that any missing +/// data has already been removed +std::vector ImpactHeightCheck::calcVerticalGradient( + const std::vector & refrac, + const std::vector & height) const { + std::vector gradient; + for (size_t iLevel = 0; iLevel < refrac.size()-1; ++iLevel) { + gradient.push_back((refrac[iLevel+1] - refrac[iLevel]) / (height[iLevel+1] - height[iLevel])); + } + return gradient; +} + +// ----------------------------------------------------------------------------- +/// Calculate the impact parameter from the refractivity, impact height and +/// radius of curvature +float ImpactHeightCheck::calcImpactHeight(float refractivity, + float modelHeight, + float radiusCurv) const { + return 1.0E-6f * refractivity * (radiusCurv + modelHeight) + modelHeight; +} + + +// ----------------------------------------------------------------------------- +void ImpactHeightCheck::print(std::ostream & os) const { + os << "ImpactHeightCheck: config = " << parameters_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/ImpactHeightCheck.h b/src/ufo/filters/ImpactHeightCheck.h new file mode 100644 index 000000000..4e42fdab2 --- /dev/null +++ b/src/ufo/filters/ImpactHeightCheck.h @@ -0,0 +1,85 @@ +/* + * (C) British Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_IMPACTHEIGHTCHECK_H_ +#define UFO_FILTERS_IMPACTHEIGHTCHECK_H_ + +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/NumericConstraints.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "oops/util/Printable.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/QCflags.h" + +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} + +namespace ufo { + +/// Parameters controlling the operation of the ImpactHeightCheck filter. +class ImpactHeightCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(ImpactHeightCheckParameters, FilterParametersBase) + + public: + /// The threshold used to define a sharp gradient in refractivity. + /// Units: N-units / m + /// Inversions, and possible ducting, are identified by looking for sharp gradients + /// in the refractivity. If the refractivity gradient is less than this, then any + /// data below this point are rejected. + oops::Parameter gradientThreshold{"gradient threshold", -0.08f, this}; + + /// The height (in m) of a buffer-zone for rejecting data above sharp gradients. + /// If a sharp gradient in refractivity is identified, then all data above + /// this point (plus sharpGradientOffset) is rejected. This parameter makes + /// sure that we don't use any data which is too close to the sharp gradient. + oops::Parameter sharpGradientOffset{"sharp gradient offset", 500, this}; + + /// Reject data within this height (in m) of the surface. + oops::Parameter surfaceOffset{"surface offset", 600, this}; +}; + +/// ImpactHeightCheck: Calculate the impact height for model profiles, and reject +/// any observations which are outside the range of model impact heights. Check +/// for any sharp refractivity gradients, and reject any observations below them. +/// Refractivity and model heights must have been saved into ObsDiagnostics by +/// the observation operator. + +class ImpactHeightCheck : public FilterBase, + private util::ObjectCounter { + public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef ImpactHeightCheckParameters Parameters_; + + static const std::string classname() {return "ufo::ImpactHeightCheck";} + + ImpactHeightCheck(ioda::ObsSpace &, const Parameters_ &, + std::shared_ptr >, + std::shared_ptr >); + ~ImpactHeightCheck(); + + private: + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override {return QCflags::domain;} + Parameters_ parameters_; + std::vector calcVerticalGradient(const std::vector &, + const std::vector &) const; + float calcImpactHeight(float, float, float) const; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_IMPACTHEIGHTCHECK_H_ diff --git a/src/ufo/filters/MWCLWCheck.cc b/src/ufo/filters/MWCLWCheck.cc index 4ef4dbc82..d2fb8c5eb 100644 --- a/src/ufo/filters/MWCLWCheck.cc +++ b/src/ufo/filters/MWCLWCheck.cc @@ -25,13 +25,16 @@ namespace ufo { // ----------------------------------------------------------------------------- -MWCLWCheck::MWCLWCheck(ioda::ObsSpace & obsdb, const eckit::Configuration & config, +MWCLWCheck::MWCLWCheck(ioda::ObsSpace & obsdb, const Parameters_ & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr), invars_(config_, "clw variables") { - oops::Log::debug() << "MWCLWCheck: config = " << config_ << std::endl; - const Variable var0(invars_[0] + "@HofX"); - const Variable var1(invars_[1] + "@HofX"); + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) { + oops::Log::debug() << "MWCLWCheck: config = " << parameters_ << std::endl; + + const oops::Variables &invars = parameters_.clwVariables.value(); + ASSERT(invars.size() == 2); + const Variable var0(invars[0] + "@HofX"); + const Variable var1(invars[1] + "@HofX"); allvars_ += var0; allvars_ += var1; } @@ -52,13 +55,14 @@ void MWCLWCheck::applyFilter(const std::vector & apply, float amsua_clw(float, float, float); // Get config - std::vector clw_thresholds = config_.getFloatVector("clw_thresholds"); + const std::vector &clw_thresholds = parameters_.clwThresholds; // clw_option controls how the clw is calculated: // 1) Use observed BTs // 2) Use calculated BTs // 3) Symmetric calculation - const int clw_option = config_.getInt("clw_option"); - oops::Log::debug() << "MWCLWCheck: config = " << config_ << std::endl; + const int clw_option = parameters_.clwOption; + const oops::Variables &invars = parameters_.clwVariables.value(); + oops::Log::debug() << "MWCLWCheck: config = " << parameters_ << std::endl; // Number of channels to be tested and number of thresholds needs to be the same ASSERT(clw_thresholds.size() == filtervars.nvars()); @@ -66,21 +70,17 @@ void MWCLWCheck::applyFilter(const std::vector & apply, for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { ASSERT(clw_thresholds[jv] != missing); } -// Check we have the correct number of channels to do the CLW calculation - ASSERT(invars_.size() == 2); -// Check clw_option is in range - ASSERT(clw_option >= 1 && clw_option <=3); ioda::ObsDataVector obs(obsdb_, filtervars.toOopsVariables(), "ObsValue"); - ioda::ObsDataVector obs_for_calc(obsdb_, invars_, "ObsValue"); + ioda::ObsDataVector obs_for_calc(obsdb_, invars, "ObsValue"); ioda::ObsDataVector sza(obsdb_, "sensor_zenith_angle", "MetaData"); ioda::ObsDataVector clw(obsdb_, "cloud_liquid_water", "Diagnostic", false); ioda::ObsDataVector clw_guess_out(obsdb_, "clws_guess", "Diagnostic", false); ioda::ObsDataVector clw_obs_out(obsdb_, "clw_obs", "Diagnostic", false); // H(x) - const Variable var0(invars_[0] + "@HofX"); - const Variable var1(invars_[1] + "@HofX"); + const Variable var0(invars[0] + "@HofX"); + const Variable var1(invars[1] + "@HofX"); std::vector hofx0; data_.get(var0, hofx0); std::vector hofx1; @@ -141,7 +141,7 @@ float amsua_clw(float tobs1, float tobs2, float sza) { tobs1 <= 284.0 && tobs2 <= 284.0 && tobs1 > 0.0 && tobs2 > 0.0) { float cossza = cos(M_PI * sza/180.0); float d0 = 8.240 - (2.622 - 1.846*cossza)*cossza; - clw = cossza*(d0 + d1*log(285.0-tobs1)) + d2*log(285.0-tobs2); + clw = cossza*(d0 + d1*std::log(285.0-tobs1)) + d2*std::log(285.0-tobs2); clw = std::max(0.0f, clw); } else { clw = missing; @@ -153,7 +153,7 @@ float amsua_clw(float tobs1, float tobs2, float sza) { // ----------------------------------------------------------------------------- void MWCLWCheck::print(std::ostream & os) const { - os << "MWCLWCheck: config = " << config_ << std::endl; + os << "MWCLWCheck: config = " << parameters_ << std::endl; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/MWCLWCheck.h b/src/ufo/filters/MWCLWCheck.h index f7f2bd4e8..dcffb4786 100644 --- a/src/ufo/filters/MWCLWCheck.h +++ b/src/ufo/filters/MWCLWCheck.h @@ -13,8 +13,11 @@ #include #include +#include "oops/base/ParameterTraitsVariables.h" #include "oops/base/Variables.h" #include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/NumericConstraints.h" +#include "oops/util/parameters/RequiredParameter.h" #include "ufo/filters/FilterBase.h" #include "ufo/filters/QCflags.h" @@ -29,16 +32,34 @@ namespace ioda { namespace ufo { -/// MWCLWCheck: generic quality control based on observation data only +/// Parameters controlling the operation of the MWCLWCheck filter. +class MWCLWCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(MWCLWCheckParameters, FilterParametersBase) -// Check that observations are within some bounds over some domain + public: + oops::RequiredParameter clwVariables{"clw variables", this}; + + oops::RequiredParameter> clwThresholds{"clw_thresholds", this}; + + /// Controls how the clw is calculated: + /// + /// 1. Use observed BTs. + /// 2. Use calculated BTs. + /// 3. Symmetric calculation. + oops::RequiredParameter clwOption{"clw_option", this, {oops::minConstraint(1), + oops::maxConstraint(3)}}; +}; class MWCLWCheck : public FilterBase, private util::ObjectCounter { public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef MWCLWCheckParameters Parameters_; + static const std::string classname() {return "ufo::MWCLWCheck";} - MWCLWCheck(ioda::ObsSpace &, const eckit::Configuration &, + MWCLWCheck(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr >, std::shared_ptr >); ~MWCLWCheck(); @@ -49,7 +70,7 @@ class MWCLWCheck : public FilterBase, std::vector> &) const override; int qcFlag() const override {return QCflags::clw;} - oops::Variables invars_; + Parameters_ parameters_; }; } // namespace ufo diff --git a/src/ufo/filters/MetOfficeBuddyCheck.cc b/src/ufo/filters/MetOfficeBuddyCheck.cc index ed9b6d83b..86a7ed025 100644 --- a/src/ufo/filters/MetOfficeBuddyCheck.cc +++ b/src/ufo/filters/MetOfficeBuddyCheck.cc @@ -95,17 +95,15 @@ struct MetOfficeBuddyCheck::MetaData { std::vector stationIds; }; -MetOfficeBuddyCheck::MetOfficeBuddyCheck(ioda::ObsSpace& obsdb, const eckit::Configuration& config, +MetOfficeBuddyCheck::MetOfficeBuddyCheck(ioda::ObsSpace& obsdb, const Parameters_& parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, std::move(flags), std::move(obserr)) + : FilterBase(obsdb, parameters, std::move(flags), std::move(obserr)), options_(parameters) { - oops::Log::debug() << "MetOfficeBuddyCheck: config = " << config_ << std::endl; - - options_.reset(new MetOfficeBuddyCheckParameters()); - options_->deserialize(config); - + oops::Log::debug() << "MetOfficeBuddyCheck: config = " << options_ << std::endl; allvars_ += Variables(filtervars_, "HofX"); + for (size_t i = 0; i < filtervars_.size(); ++i) + allvars_ += backgroundErrorVariable(filtervars_[i]); } // Required for the correct destruction of options_. @@ -127,8 +125,6 @@ void MetOfficeBuddyCheck::applyFilter(const std::vector & apply, const oops::Variables &observedVars = obsdb_.obsvariables(); ioda::ObsDataVector obsValues(obsdb_, filtervars.toOopsVariables(), "ObsValue"); - ioda::ObsDataVector obsBiases(obsdb_, filtervars.toOopsVariables(), "ObsBias", - false /*fail if not found?*/); auto getFilterVariableName = [&] (size_t filterVarIndex) { return filtervars.variable(filterVarIndex).variable(); @@ -140,11 +136,9 @@ void MetOfficeBuddyCheck::applyFilter(const std::vector & apply, ScalarSingleLevelVariableData data; data.varFlags = &(*flags_)[observedVarIndex]; data.obsValues = &obsValues[filterVarIndex]; - data.obsBiases = &obsBiases[filterVarIndex]; data.obsErrors = &(*obserr_)[observedVarIndex]; data_.get(ufo::Variable(filtervars[filterVarIndex], "HofX"), data.bgValues); - // TODO(wsmigaj): It isn't clear where to get background error variances from. - data_.get(ufo::Variable(filtervars[filterVarIndex], "HofXError"), data.bgErrors); + data_.get(backgroundErrorVariable(filtervars[filterVarIndex]), data.bgErrors); // TODO(wsmigaj): How is this variable going to be initialized? data_.get(ufo::Variable(filtervars[filterVarIndex], "GrossErrorProbability"), data.grossErrorProbabilities); @@ -154,8 +148,8 @@ void MetOfficeBuddyCheck::applyFilter(const std::vector & apply, // Identify buddy pairs const std::vector *pressures = - options_->sortByPressure ? obsData.pressures.get_ptr() : nullptr; - MetOfficeBuddyPairFinder buddyPairFinder(*options_, obsData.latitudes, obsData.longitudes, + options_.sortByPressure ? obsData.pressures.get_ptr() : nullptr; + MetOfficeBuddyPairFinder buddyPairFinder(options_, obsData.latitudes, obsData.longitudes, obsData.datetimes, pressures, obsData.stationIds); const std::vector buddyPairs = buddyPairFinder.findBuddyPairs(validObsIds); @@ -184,8 +178,8 @@ void MetOfficeBuddyCheck::applyFilter(const std::vector & apply, *firstComponentData.varFlags, verbose, bgErrorHorizCorrScales, obsData.stationIds, obsData.datetimes, - *firstComponentData.obsValues, *firstComponentData.obsBiases, - *secondComponentData.obsValues, *secondComponentData.obsBiases, + *firstComponentData.obsValues, + *secondComponentData.obsValues, *firstComponentData.obsErrors, firstComponentData.bgValues, secondComponentData.bgValues, firstComponentData.bgErrors, @@ -211,7 +205,7 @@ void MetOfficeBuddyCheck::applyFilter(const std::vector & apply, checkScalarSurfaceData(buddyPairs, *data.varFlags, verbose, bgErrorHorizCorrScales, obsData.stationIds, obsData.datetimes, - *data.obsValues, *data.obsBiases, *data.obsErrors, + *data.obsValues, *data.obsErrors, data.bgValues, data.bgErrors, data.grossErrorProbabilities); calculatedGrossErrProbsByVarName[getFilterVariableName(filterVarIndex)] = @@ -234,6 +228,10 @@ void MetOfficeBuddyCheck::applyFilter(const std::vector & apply, } } +Variable MetOfficeBuddyCheck::backgroundErrorVariable(const Variable &filterVariable) const { + return Variable(filterVariable.variable() + "_background_error@ObsDiag"); +} + MetOfficeBuddyCheck::MetaData MetOfficeBuddyCheck::collectMetaData() const { MetaData obsData; @@ -257,9 +255,9 @@ MetOfficeBuddyCheck::MetaData MetOfficeBuddyCheck::collectMetaData() const { } std::vector MetOfficeBuddyCheck::getStationIds() const { - const boost::optional &stationIdVariable = options_->stationIdVariable.value(); + const boost::optional &stationIdVariable = options_.stationIdVariable.value(); if (stationIdVariable == boost::none) { - if (obsdb_.obs_group_var().empty()) { + if (obsdb_.obs_group_vars().empty()) { // Observations were not grouped into records. // Assume all observations were taken by the same station. return std::vector(obsdb_.nlocs(), 0); @@ -295,7 +293,7 @@ std::vector MetOfficeBuddyCheck::calcBackgroundErrorHorizontalCorrelation std::vector abscissas, ordinates; for (const std::pair &xy : - options_->horizontalCorrelationScaleInterpolationPoints.value()) { + options_.horizontalCorrelationScaleInterpolationPoints.value()) { abscissas.push_back(xy.first); ordinates.push_back(xy.second); } @@ -324,8 +322,8 @@ std::vector MetOfficeBuddyCheck::flagAndPrintVerboseObservations( for (size_t obsId : validObsIds) { verbose[obsId] = std::any_of( - options_->tracedBoxes.value().begin(), - options_->tracedBoxes.value().end(), + options_.tracedBoxes.value().begin(), + options_.tracedBoxes.value().end(), [&](const LatLonBoxParameters &box) { return box.contains(latitudes[obsId], longitudes[obsId]); }); @@ -355,7 +353,6 @@ void MetOfficeBuddyCheck::checkScalarSurfaceData(const std::vector &stationIds, const std::vector &datetimes, const std::vector &obsValues, - const std::vector &obsBiases, const std::vector &obsErrors, const std::vector &bgValues, const std::vector &bgErrors, @@ -365,13 +362,13 @@ void MetOfficeBuddyCheck::checkScalarSurfaceData(const std::vectortemporalCorrelationScale.value().toSeconds(); + const double invTemporalCorrScale = 1.0 / options_.temporalCorrelationScale.value().toSeconds(); for (const MetOfficeBuddyPair &pair : pairs) { const size_t jA = pair.obsIdA; @@ -397,8 +394,8 @@ void MetOfficeBuddyCheck::checkScalarSurfaceData(const std::vectordampingFactor1 * (-0.5 * std::log(1.0 - rho2) + expArg); // exponent of + expArg = options_.dampingFactor1 * (-0.5 * std::log(1.0 - rho2) + expArg); // exponent of expArg = std::min(expArgMax, std::max(-expArgMax, expArg)); // eqn 3.18 // Z = P(OA)*P(OB)/P(OA and OB) double z = 1.0 / (1.0 - (1.0 - pges[jA]) * (1.0 - pges[jB]) * (1.0 - std::exp(expArg))); if (z <= 0.0) z = 1.0; // rounding error control - z = std::pow(z, options_->dampingFactor2); // eqn 3.16 + z = std::pow(z, options_.dampingFactor2); // eqn 3.16 pges[jA] *= z; // eqn 3.17 pges[jB] *= z; // eqn 3.17 if (isMaster && (verbose[jA] || verbose[jB])) { @@ -436,9 +433,7 @@ void MetOfficeBuddyCheck::checkVectorSurfaceData(const std::vector &stationIds, const std::vector &datetimes, const std::vector &uObsValues, - const std::vector &uObsBiases, const std::vector &vObsValues, - const std::vector &vObsBiases, const std::vector &obsErrors, const std::vector &uBgValues, const std::vector &vBgValues, @@ -449,13 +444,13 @@ void MetOfficeBuddyCheck::checkVectorSurfaceData(const std::vectortemporalCorrelationScale.value().toSeconds(); + const double invTemporalCorrScale = 1.0 / options_.temporalCorrelationScale.value().toSeconds(); for (const MetOfficeBuddyPair &pair : pairs) { const size_t jA = pair.obsIdA; @@ -482,19 +477,19 @@ void MetOfficeBuddyCheck::checkVectorSurfaceData(const std::vectornonDivergenceConstraint * scaleDist) * lCovar; // eqn 3.12, 13 + double tCovar = (1.0 - options_.nonDivergenceConstraint * scaleDist) * lCovar; // eqn 3.12, 13 // rho2 = (total error correlation between ob positions)**2 double lRho2 = sqr(lCovar) / (errVarA * errVarB); // eqn 3.14 double tRho2 = sqr(tCovar) / (errVarA * errVarB); // eqn 3.14 @@ -515,13 +510,13 @@ void MetOfficeBuddyCheck::checkVectorSurfaceData(const std::vectordampingFactor1 * (-0.5 * std::log((1.0 - lRho2) * (1.0 - lRho2)) + expArg); + expArg = options_.dampingFactor1 * (-0.5 * std::log((1.0 - lRho2) * (1.0 - lRho2)) + expArg); expArg = std::min(expArgMax, std::max(-expArgMax, expArg)); // eqn 3.22 // Z = P(OA)*P(OB)/P(OA and OB) double z = 1.0 / (1.0 - (1.0 - pges[jA]) * (1.0 - pges[jB]) * (1.0 - std::exp(expArg))); if (z <= 0.0) z = 1.0; // rounding error control - z = std::pow(z, options_->dampingFactor2); // eqn 3.16 + z = std::pow(z, options_.dampingFactor2); // eqn 3.16 pges[jA] *= z; // eqn 3.17 pges[jB] *= z; // eqn 3.17 @@ -561,13 +556,13 @@ void MetOfficeBuddyCheck::flagRejectedObservations( ASSERT(grossErrProbs.size() == variableFlagged.size()); for (size_t obsId = 0; obsId < grossErrProbs.size(); ++obsId) - if (grossErrProbs[obsId] >= options_->rejectionThreshold) + if (grossErrProbs[obsId] >= options_.rejectionThreshold) variableFlagged[obsId] = true; } } void MetOfficeBuddyCheck::print(std::ostream & os) const { - os << "MetOfficeBuddyCheck: config = " << config_ << std::endl; + os << "MetOfficeBuddyCheck: config = " << options_ << std::endl; } } // namespace ufo diff --git a/src/ufo/filters/MetOfficeBuddyCheck.h b/src/ufo/filters/MetOfficeBuddyCheck.h index b07e9bf0d..2d079f405 100644 --- a/src/ufo/filters/MetOfficeBuddyCheck.h +++ b/src/ufo/filters/MetOfficeBuddyCheck.h @@ -19,6 +19,7 @@ #include "ioda/ObsDataVector.h" #include "oops/util/ObjectCounter.h" #include "ufo/filters/FilterBase.h" +#include "ufo/filters/MetOfficeBuddyCheckParameters.h" #include "ufo/filters/QCflags.h" namespace eckit { @@ -37,7 +38,6 @@ class DateTime; namespace ufo { class RecursiveSplitter; -class MetOfficeBuddyCheckParameters; class MetOfficeBuddyPair; /// \brief Met Office's implementation of the buddy check. @@ -63,12 +63,29 @@ class MetOfficeBuddyPair; /// \endcode /// /// See MetOfficeBuddyCheckParameters for the documentation of the other available parameters. +/// +/// This filter assumes background error estimates for each filter variable `var` can be retrieved +/// from the `var_background_error` ObsDiagnostic. These diagnostics are typically produced using +/// the BackgroundErrorVertInterp or BackgroundErrorIdentity operators. To make sure these operators +/// are applied in addition to the "main" operator calculating model equivalents of observations, +/// instruct the system to use a Composite operator with two or more components; for instance, +/// +/// \code{.yaml} +/// obs operator: +/// name: Composite +/// components: +/// # operator used to evaluate H(x) +/// - name: Identity +/// # operator used to evaluate background errors +/// - name: BackgroundErrorIdentity +/// \endcode class MetOfficeBuddyCheck : public FilterBase, private util::ObjectCounter { public: + typedef MetOfficeBuddyCheckParameters Parameters_; static const std::string classname() {return "ufo::MetOfficeBuddyCheck";} - MetOfficeBuddyCheck(ioda::ObsSpace &obsdb, const eckit::Configuration &config, + MetOfficeBuddyCheck(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, std::shared_ptr > flags, std::shared_ptr > obserr); @@ -82,6 +99,10 @@ class MetOfficeBuddyCheck : public FilterBase, std::vector> &) const override; int qcFlag() const override { return QCflags::buddy; } + /// \brief Return the name of the variable containing the background error estimate of the + /// specified filter variable. + Variable backgroundErrorVariable(const Variable &filterVariable) const; + /// \brief Returns a vector of IDs of all observations that should be buddy-checked. std::vector getValidObservationIds(const std::vector &apply) const; @@ -132,9 +153,7 @@ class MetOfficeBuddyCheck : public FilterBase, /// \param datetimes /// Observation times. /// \param obsValues - /// Observed values (excluding bias corrections). - /// \param obsBiases - /// Bias corrections. + /// Observed values. /// \param obsErrors /// Estimated errors of observed values. /// \param bgValues @@ -150,7 +169,6 @@ class MetOfficeBuddyCheck : public FilterBase, const std::vector &stationIds, const std::vector &datetimes, const std::vector &obsValues, - const std::vector &obsBiases, const std::vector &obsErrors, const std::vector &bgValues, const std::vector &bgErrors, @@ -173,13 +191,9 @@ class MetOfficeBuddyCheck : public FilterBase, /// \param datetimes /// Observation times. /// \param uObsValues - /// Observed values of the first component, u (excluding bias corrections). - /// \param uObsBiases - /// Bias corrections for u. + /// Observed values of the first component, u. /// \param vObsValues - /// Observed values of the second component, v (excluding bias corrections). - /// \param vObsBiases - /// Bias corrections for v. + /// Observed values of the second component, v. /// \param obsErrors /// Estimated errors of observed values (u or v). /// \param uBgValues @@ -197,9 +211,7 @@ class MetOfficeBuddyCheck : public FilterBase, const std::vector &stationIds, const std::vector &datetimes, const std::vector &uObsValues, - const std::vector &uObsBiases, const std::vector &vObsValues, - const std::vector &vObsBiases, const std::vector &obsErrors, const std::vector &uBgValues, const std::vector &vBgValues, @@ -214,7 +226,7 @@ class MetOfficeBuddyCheck : public FilterBase, std::vector> &flagged) const; private: - std::unique_ptr options_; + Parameters_ options_; }; } // namespace ufo diff --git a/src/ufo/filters/MetOfficeBuddyCheckParameters.h b/src/ufo/filters/MetOfficeBuddyCheckParameters.h index 17378e98b..e0dfcf0fb 100644 --- a/src/ufo/filters/MetOfficeBuddyCheckParameters.h +++ b/src/ufo/filters/MetOfficeBuddyCheckParameters.h @@ -16,6 +16,7 @@ #include "oops/util/parameters/OptionalParameter.h" #include "oops/util/parameters/Parameter.h" #include "oops/util/parameters/Parameters.h" +#include "ufo/filters/FilterParametersBase.h" #include "ufo/utils/Constants.h" #include "ufo/utils/parameters/ParameterTraitsVariable.h" @@ -38,8 +39,8 @@ class LatLonBoxParameters : public oops::Parameters { }; /// \brief Options controlling the operation of the MetOfficeBuddyCheck filter. -class MetOfficeBuddyCheckParameters : public oops::Parameters { - OOPS_CONCRETE_PARAMETERS(MetOfficeBuddyCheckParameters, Parameters) +class MetOfficeBuddyCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(MetOfficeBuddyCheckParameters, FilterParametersBase) public: /// \name Parameters controlling buddy pair identification @@ -48,7 +49,7 @@ class MetOfficeBuddyCheckParameters : public oops::Parameters { /// Maximum distance between two observations that may be classified as buddies, in km. oops::Parameter searchRadius{"search_radius", 100, this}; - /// Variable storing integer-valued station IDs. + /// Variable storing string-valued or integer-valued station IDs. /// /// If not set and observations were grouped into records when the observation space was /// constructed, each record is assumed to consist of observations taken by a separate @@ -56,7 +57,7 @@ class MetOfficeBuddyCheckParameters : public oops::Parameters { /// assumed to have been taken by a single station. /// /// Note: the variable used to group observations into records can be set with the - /// \c obs space.obsdatain.obsgrouping.group_variable YAML option. + /// \c obs space.obsdatain.obsgrouping.groupvariable YAML option. oops::OptionalParameter stationIdVariable{"station_id_variable", this}; /// Number of zonal bands to split the Earth's surface into when building a search data structure. diff --git a/src/ufo/filters/ModelObThreshold.cc b/src/ufo/filters/ModelObThreshold.cc new file mode 100644 index 000000000..c1c1ccd4f --- /dev/null +++ b/src/ufo/filters/ModelObThreshold.cc @@ -0,0 +1,182 @@ +/* ----------------------------------------------------------------------------- + * (C) British Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/ModelObThreshold.h" + +#include "eckit/config/Configuration.h" + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" + +#include "oops/base/ObsFilterBase.h" +#include "oops/interface/ObsFilter.h" +#include "oops/util/Logger.h" +#include "oops/util/PropertiesOfNVectors.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/utils/PiecewiseLinearInterpolation.h" + +namespace ufo { + +constexpr char ThresholdTypeParameterTraitsHelper::enumTypeName[]; +constexpr util::NamedEnumerator + ThresholdTypeParameterTraitsHelper::namedValues[]; + +// ----------------------------------------------------------------------------- + +ModelObThreshold::ModelObThreshold(ioda::ObsSpace & obsdb, const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) +{ + oops::Log::trace() << "ModelObThreshold contructor starting" << std::endl; + allvars_ += parameters_.model_profile; + allvars_ += parameters_.model_vcoord; + allvars_ += parameters_.obs_height; +} + +// ----------------------------------------------------------------------------- + +ModelObThreshold::~ModelObThreshold() { + oops::Log::trace() << "ModelObThreshold destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- +/*! \brief Filter to apply a threshold to a model profile interpolated to the + * observation height. + * + * \details The specified model profile variable is linearly (vertical) interpolated + * to the observation height using the specified model vertical coordinate variable. + * This is referred to as the "ModelOb". Note that the ModelOb is not necessarily + * one of the HofX variables. + * + * The observation height must be in the same coordinate system as that specified + * for the model vertical coordinate, e.g. both pressure. + * + * The thresholds to compare the ModelOb against is specified as height-dependent. + * We supply a vector of threshold values, and a vector of vertical coordinate + * values corresponding to those thresholds. The coordinate values must be in the same + * vertical coordinate as the observation, e.g. pressure. The threshold values are + * then linearly interpolated to the observation height. + * + * The observation is flagged for rejection if the ModelOb lies outside the threshold + * value according to threshold type - min or max. E.g. if the threshold type is min, + * then the observation is flagged if ModelOb is less than the interpolated threshold + * value. + * + * Example for relative humidity: + * \code{.unparsed} + * obs filters: + * - filter: ModelOb Threshold + * model profile: + * name: relative_humidity@GeoVaLs + * model vertical coordinate: + * name: air_pressure@GeoVaLs + * observation height: + * name: air_pressure_levels@MetaData + * thresholds: [50,50,40,30] + * coordinate values: [100000,80000,50000,20000] + * threshold type: min + * \endcode + * + * \author J.Cotton (Met Office) + * + * \date 12/03/2021: Created + */ + +void ModelObThreshold::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const { + oops::Log::trace() << "ModelObThreshold priorFilter" << std::endl; + print(oops::Log::trace()); + + const float missing = util::missingValue(missing); + const size_t nlocs = obsdb_.nlocs(); + +// Get piece-wise parameters from options. + const std::vector coord_vals = parameters_.coord_vals.value(); + const std::vector thresholds = parameters_.thresholds.value(); + oops::Log::debug() << "QC coord vals are " << coord_vals << std::endl; + oops::Log::debug() << "QC thresholds are " << thresholds << std::endl; + +// get names of GeoVal variables + const std::string model_profile_name = Variable(parameters_.model_profile).variable(); + const std::string model_vcoord_name = Variable(parameters_.model_vcoord).variable(); + + std::ostringstream errString; +// Ensure same size vectors (coord_vals and threshold); Also ensure more than one value in each. + if (coord_vals.size() <= 1 || coord_vals.size() != thresholds.size()) { + errString << "At least one of coord_vals, thresholds is wrong size - either unequal or < 2" + << oops::listOfVectorSizes(coord_vals, thresholds) << std::endl; + throw eckit::BadValue(errString.str()); + } + +// Get variables from ObsSpace +// Get obs_height, the observation height + std::vector obs_height; + data_.get(parameters_.obs_height, obs_height); + +// Get GeoVaLs + const ufo::GeoVaLs * gvals = data_.getGeoVaLs(); + +// Setup interpolation of height-dependent thresholds +// N.B. inputs to interp must be double precision + ufo::PiecewiseLinearInterpolation interp_thresholds(coord_vals, thresholds); + +// Loop through locations + for (size_t iloc=0; iloc < nlocs; ++iloc) { + // interpolate threshold values to observation height + float bg_threshold = interp_thresholds(obs_height[iloc]); + + // Vectors storing GeoVaL column for current location. + std::vector model_profile_column; + std::vector model_vcoord_column; + model_profile_column.assign(gvals->nlevs(model_profile_name), 0.0); + model_vcoord_column.assign(gvals->nlevs(model_vcoord_name), 0.0); + // Get GeoVaLs at the specified location. + gvals->getAtLocation(model_profile_column, model_profile_name, iloc); + gvals->getAtLocation(model_vcoord_column, model_vcoord_name, iloc); + + // interpolate model profile values to observation height + ufo::PiecewiseLinearInterpolation interp_model(model_vcoord_column, model_profile_column); + float bg_model = interp_model(obs_height[iloc]); + + // Apply threshold + if (apply[iloc]) { + // check to see if one of the compared values is missing + if (bg_model == missing || bg_threshold == missing) { + for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { + flagged[jv][iloc] = true; + } + } else { + // Check if model value is outside threshold and set flag + for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { + if (parameters_.threshold_type == ThresholdType::MIN) { + if (bg_model < (bg_threshold)) { + flagged[jv][iloc] = true; + } + } + if (parameters_.threshold_type == ThresholdType::MAX) { + if (bg_model > (bg_threshold)) { + flagged[jv][iloc] = true; + } + } + } + } + } + } +} + +// ----------------------------------------------------------------------------- + +void ModelObThreshold::print(std::ostream & os) const { + os << "ModelObThreshold: config = " << parameters_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/ModelObThreshold.h b/src/ufo/filters/ModelObThreshold.h new file mode 100644 index 000000000..0870993b6 --- /dev/null +++ b/src/ufo/filters/ModelObThreshold.h @@ -0,0 +1,113 @@ +/* ----------------------------------------------------------------------------- + * (C) British Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_MODELOBTHRESHOLD_H_ +#define UFO_FILTERS_MODELOBTHRESHOLD_H_ + +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/QCflags.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} + +namespace ufo { + +enum class ThresholdType { + MIN, MAX +}; + +struct ThresholdTypeParameterTraitsHelper { + typedef ThresholdType EnumType; + static constexpr char enumTypeName[] = "ThresholdType"; + static constexpr util::NamedEnumerator namedValues[] = { + { ThresholdType::MIN, "min" }, + { ThresholdType::MAX, "max" } + }; +}; + +} // namespace ufo + +namespace oops { + +template <> +struct ParameterTraits : + public EnumParameterTraits +{}; + +} // namespace oops + +namespace ufo { + +/// \brief Parameters controlling the operation of the ModelObThreshold filter. +class ModelObThresholdParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(ModelObThresholdParameters, FilterParametersBase) + + public: + /// Name of the model profile variable (GeoVaLs) + oops::RequiredParameter model_profile{"model profile", this}; + /// Name of the model vertical coordinate variable (GeoVal) + oops::RequiredParameter model_vcoord{"model vertical coordinate", this}; + /// Name of the observation height variable to interpolate to + oops::RequiredParameter obs_height{"observation height", this}; + /// Vector of threshold values + oops::RequiredParameter> thresholds{"thresholds", this}; + /// Vector of vertical coordinates corresponding to vector of thresholds + oops::RequiredParameter> coord_vals{"coordinate values", this}; + /// Threshold type: min or max + oops::RequiredParameter threshold_type{"threshold type", this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief A filter that interpolates a model profile (GeoVaL) and a height-dependent +/// threshold to the observation location and flags observations which are outside the +/// specified limit. +/// +/// See ModelObThresholdParameters for the documentation of the parameters controlling this filter. +class ModelObThreshold : public FilterBase, + private util::ObjectCounter { + public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef ModelObThresholdParameters Parameters_; + + static const std::string classname() {return "ufo::ModelObThreshold";} + + ModelObThreshold(ioda::ObsSpace &, const Parameters_ &, + std::shared_ptr >, + std::shared_ptr >); + ~ModelObThreshold(); + + private: + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override {return QCflags::modelobthresh;} + + Parameters_ parameters_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_MODELOBTHRESHOLD_H_ diff --git a/src/ufo/filters/ObsAccessor.cc b/src/ufo/filters/ObsAccessor.cc new file mode 100644 index 000000000..83dedcfb7 --- /dev/null +++ b/src/ufo/filters/ObsAccessor.cc @@ -0,0 +1,238 @@ +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/ObsAccessor.h" + +#include +#include +#include + +#include "ioda/distribution/InefficientDistribution.h" +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" +#include "ufo/filters/QCflags.h" +#include "ufo/utils/RecursiveSplitter.h" + +namespace ufo { + +namespace { + +template +std::vector getVariableFromObsSpaceImpl( + const std::string &group, const std::string &variable, + const ioda::ObsSpace &obsdb, const ioda::Distribution &obsDistribution) { + std::vector result(obsdb.nlocs()); + obsdb.get_db(group, variable, result); + obsDistribution.allGatherv(result); + return result; +} + +/// Return the vector of elements of \p categories with indices \p validObsIds. +template +std::vector getValidObservationCategories(const std::vector &categories, + const std::vector &validObsIds) { + std::vector validObsCategories(validObsIds.size()); + for (size_t validObsIndex = 0; validObsIndex < validObsIds.size(); ++validObsIndex) { + validObsCategories[validObsIndex] = categories[validObsIds[validObsIndex]]; + } + return validObsCategories; +} + +template +void groupObservationsByVariableImpl( + const Variable &variable, + const std::vector &validObsIds, + const ioda::ObsSpace &obsdb, + const ioda::Distribution &obsDistribution, + RecursiveSplitter &splitter) { + std::vector obsCategories(obsdb.nlocs()); + obsdb.get_db(variable.group(), variable.variable(), obsCategories); + obsDistribution.allGatherv(obsCategories); + + const std::vector validObsCategories = getValidObservationCategories( + obsCategories, validObsIds); + + splitter.groupBy(validObsCategories); +} + +} // namespace + +ObsAccessor::ObsAccessor(const ioda::ObsSpace &obsdb, + GroupBy groupBy, + boost::optional categoryVariable) + : obsdb_(&obsdb), groupBy_(groupBy), categoryVariable_(categoryVariable) +{ + if (groupBy_ == GroupBy::VARIABLE && wereRecordsGroupedByCategoryVariable()) + groupBy_ = GroupBy::RECORD_ID; + + if (groupBy_ == GroupBy::RECORD_ID) { + // Each record is held by a single process, so there's no need to exchange data between + // processes and we can use an InefficientDistribution rather than the distribution taken from + // obsdb_. Which in this case is *efficient*! + eckit::LocalConfiguration emptyConfig; + obsDistribution_ = std::make_shared(obsdb_->comm(), + emptyConfig); + oops::Log::trace() << "ObservationAccessor: no MPI communication necessary" << std::endl; + } else { + obsDistribution_ = obsdb.distribution(); + } +} + +ObsAccessor ObsAccessor::toAllObservations( + const ioda::ObsSpace &obsdb) { + return ObsAccessor(obsdb, GroupBy::NOTHING, boost::none); +} + +ObsAccessor ObsAccessor::toObservationsSplitIntoIndependentGroupsByRecordId( + const ioda::ObsSpace &obsdb) { + return ObsAccessor(obsdb, GroupBy::RECORD_ID, boost::none); +} + +ObsAccessor ObsAccessor::toObservationsSplitIntoIndependentGroupsByVariable( + const ioda::ObsSpace &obsdb, const Variable &variable) { + return ObsAccessor(obsdb, GroupBy::VARIABLE, variable); +} + +std::vector ObsAccessor::getValidObservationIds( + const std::vector &apply, const ioda::ObsDataVector &flags) const { + // TODO(wsmigaj): use std::vector to save space + std::vector globalApply(apply.size()); + for (size_t obsId = 0; obsId < apply.size(); ++obsId) + globalApply[obsId] = apply[obsId] && flags[0][obsId] == QCflags::pass; + obsDistribution_->allGatherv(globalApply); + + std::vector validObsIds; + for (size_t obsId = 0; obsId < globalApply.size(); ++obsId) + if (globalApply[obsId]) + validObsIds.push_back(obsId); + + return validObsIds; +} + +std::vector ObsAccessor::getValidObservationIds( + const std::vector &apply) const { + // TODO(wsmigaj): use std::vector to save space + std::vector globalApply(apply.begin(), apply.end()); + obsDistribution_->allGatherv(globalApply); + + std::vector validObsIds; + for (size_t obsId = 0; obsId < globalApply.size(); ++obsId) + if (globalApply[obsId]) + validObsIds.push_back(obsId); + + return validObsIds; +} + +std::vector ObsAccessor::getIntVariableFromObsSpace( + const std::string &group, const std::string &variable) const { + return getVariableFromObsSpaceImpl(group, variable, *obsdb_, *obsDistribution_); +} + +std::vector ObsAccessor::getFloatVariableFromObsSpace( + const std::string &group, const std::string &variable) const { + return getVariableFromObsSpaceImpl(group, variable, *obsdb_, *obsDistribution_); +} + +std::vector ObsAccessor::getDoubleVariableFromObsSpace( + const std::string &group, const std::string &variable) const { + return getVariableFromObsSpaceImpl(group, variable, *obsdb_, *obsDistribution_); +} + +std::vector ObsAccessor::getStringVariableFromObsSpace( + const std::string &group, const std::string &variable) const { + return getVariableFromObsSpaceImpl(group, variable, *obsdb_, *obsDistribution_); +} + +std::vector ObsAccessor::getDateTimeVariableFromObsSpace( + const std::string &group, const std::string &variable) const { + return getVariableFromObsSpaceImpl(group, variable, *obsdb_, *obsDistribution_); +} + +std::vector ObsAccessor::getRecordIds() const { + std::vector recordIds = obsdb_->recnum(); + obsDistribution_->allGatherv(recordIds); + return recordIds; +} + +size_t ObsAccessor::totalNumObservations() const { + return obsdb_->globalNumLocs(); +} + +RecursiveSplitter ObsAccessor::splitObservationsIntoIndependentGroups( + const std::vector &validObsIds) const { + RecursiveSplitter splitter(validObsIds.size()); + switch (groupBy_) { + case GroupBy::NOTHING: + // Nothing to do + break; + case GroupBy::RECORD_ID: + groupObservationsByRecordNumber(validObsIds, splitter); + break; + case GroupBy::VARIABLE: + groupObservationsByCategoryVariable(validObsIds, splitter); + break; + } + return splitter; +} + +void ObsAccessor::groupObservationsByRecordNumber(const std::vector &validObsIds, + RecursiveSplitter &splitter) const { + const std::vector &obsCategories = obsdb_->recnum(); + std::vector validObsCategories = getValidObservationCategories( + obsCategories, validObsIds); + splitter.groupBy(validObsCategories); +} + +void ObsAccessor::groupObservationsByCategoryVariable( + const std::vector &validObsIds, + RecursiveSplitter &splitter) const { + switch (obsdb_->dtype(categoryVariable_->group(), categoryVariable_->variable())) { + case ioda::ObsDtype::Integer: + groupObservationsByVariableImpl(*categoryVariable_, validObsIds, + *obsdb_, *obsDistribution_, splitter); + break; + + case ioda::ObsDtype::String: + groupObservationsByVariableImpl(*categoryVariable_, validObsIds, + *obsdb_, *obsDistribution_, splitter); + break; + + default: + throw eckit::UserError( + categoryVariable_->variable() + "@" + categoryVariable_->group() + + " is neither an integer nor a string variable", Here()); + } +} + +void ObsAccessor::flagRejectedObservations( + const std::vector &isRejected, std::vector > &flagged) const { + const size_t localNumObs = obsdb_->nlocs(); + for (const std::vector & variableFlagged : flagged) + ASSERT(variableFlagged.size() == localNumObs); + + for (size_t localObsId = 0; localObsId < localNumObs; ++localObsId) { + const size_t globalObsId = + obsDistribution_->globalUniqueConsecutiveLocationIndex(localObsId); + if (isRejected[globalObsId]) { + for (std::vector & variableFlagged : flagged) + variableFlagged[localObsId] = true; + } + } +} + +bool ObsAccessor::wereRecordsGroupedByCategoryVariable() const { + std::vector groupingVars = obsdb_->obs_group_vars(); + std::string groupingVar; + if (groupingVars.size() > 0) { + groupingVar = groupingVars[0]; + } + return categoryVariable_ != boost::none && + categoryVariable_->variable() == groupingVar && + categoryVariable_->group() == "MetaData"; +} + +} // namespace ufo diff --git a/src/ufo/filters/ObsAccessor.h b/src/ufo/filters/ObsAccessor.h new file mode 100644 index 000000000..f9e07b1d4 --- /dev/null +++ b/src/ufo/filters/ObsAccessor.h @@ -0,0 +1,202 @@ +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSACCESSOR_H_ +#define UFO_FILTERS_OBSACCESSOR_H_ + +#include +#include +#include + +#include + +#include "oops/util/DateTime.h" +#include "ufo/filters/Variable.h" + +namespace ioda { +class Distribution; +template class ObsDataVector; +class ObsSpace; +} + +namespace ufo { + +class RecursiveSplitter; + +/// \brief This class provides access to observations that may be held on multiple MPI ranks. +/// +/// It is used by filters that may be configured to +/// +/// * process observations held on all MPI ranks as a single group +/// * process observations from each record (by definition, held on a single MPI rank) +/// independently from all others +/// * process observations with each distinct value of a particular variable (held on a single MPI +/// rank if this variable was used to group observations into records or on multiple MPI ranks +/// if not) independently from all others. +/// +/// Depending on which of these cases applies, create an ObservationAccessor object by calling the +/// ObsAccessor::toAllObservations(), +/// ObsAccessor::toObservationsSplitIntoIndependentGroupsByRecordId() or the +/// ObsAccessor::toObservationsSplitIntoIndependentGroupsByVariable() static function. The +/// ObsAccessor will then determine whether each independent group consists of +/// observations held only on a single MPI rank. If so, methods such as getValidObservationIds() and +/// getIntVariableFromObsSpace() will return vectors constructed from data held only on the current +/// MPI rank (without any MPI communication); otherwise, these vectors will be constructed from +/// data obtained from all MPI ranks. +/// +/// Call splitObservationsIntoIndependentGroups() to construct a RecursiveSplitter object whose +/// groups() method will return groups of observations that can be processed independently from +/// each other (according to the criterion specified when the ObsAccessor was constructed). +class ObsAccessor { + public: + ~ObsAccessor() = default; + ObsAccessor(const ObsAccessor &) = delete; + ObsAccessor(ObsAccessor &&) = default; + ObsAccessor & operator=(const ObsAccessor &) = delete; + ObsAccessor & operator=(ObsAccessor &&) = default; + + /// \brief Create an accessor to observations from the observation space \p obsdb, assuming that + /// the whole set of observations held on all MPI ranks must be processed together as a single + /// group. + static ObsAccessor toAllObservations( + const ioda::ObsSpace &obsdb); + + /// \brief Create an accessor to the collection of observations held in \p obsdb, assuming that + /// each record can be processed independently. + static ObsAccessor toObservationsSplitIntoIndependentGroupsByRecordId( + const ioda::ObsSpace &obsdb); + + /// \brief Create an accessor to the collection of observations held in \p obsdb, assuming that + /// observations with different values of the variable \p variable can be processed independently. + static ObsAccessor toObservationsSplitIntoIndependentGroupsByVariable( + const ioda::ObsSpace &obsdb, const Variable &variable); + + /// \brief Return the IDs of observation locations that should be treated as valid by a filter. + /// + /// \param apply + /// Vector whose ith element is set to true if ith observation location held on the current + /// MPI rank was selected by the \c where clause in the filter's configuration. + /// + /// \param flags + /// An ObsDataVector holding the QC flags (set by any filters run previously) + /// of observations held on the current MPI rank. + /// + /// An observation location is treated as valid if (a) it has been selected by the \c where + /// clause and (b) its QC flag for the first filtered variable is set to \c pass (see note + /// below). + /// + /// If each independent group of observations is stored entirely on a single MPI rank, the + /// returned vector contains local IDs of valid observation locations held on the current rank + /// only. Otherwise the vector contains global IDs of valid locations held on all ranks, with IDs + /// from 0 to nlocs(0) - 1 corresponding to locations held on rank 0, IDs from nlocs(0) to + /// nlocs(0) + nlocs(1) - 1 corresponding to locations held on rank 1 and so on, where nlocs(i) + /// denotes the number of locations held on ith rank. + /// + /// \note It is an open question what should be done if there's more than one filtered variable + /// and their QC flags differ. Should only observation locations where none of the filtered + /// variables have so far been rejected be treated as candidates for retaining? Or all locations + /// where at least one of these variables has not yet been rejected? For the moment we duck the + /// question and check only the QC flags of the first filtered variable, implicitly assuming that + /// if multiple variables are thinned together, they all have the same QC flags. It might be best + /// to make this user-configurable. + std::vector getValidObservationIds(const std::vector &apply, + const ioda::ObsDataVector &flags) const; + + /// \brief Return the IDs of both flagged and unflagged observation locations selected by the + /// where clause. + /// + /// \param apply + /// Vector whose ith element is set to true if ith observation location held on the current + /// MPI rank was selected by the \c where clause in the filter's configuration. + /// + /// An observation location is treated as valid if it has been selected by the \c where + /// clause. + /// + /// If each independent group of observations is stored entirely on a single MPI rank, the + /// returned vector contains local IDs of valid observation locations held on the current rank + /// only. Otherwise the vector contains global IDs of valid locations held on all ranks, with IDs + /// from 0 to nlocs(0) - 1 corresponding to locations held on rank 0, IDs from nlocs(0) to + /// nlocs(0) + nlocs(1) - 1 corresponding to locations held on rank 1 and so on, where nlocs(i) + /// denotes the number of locations held on ith rank. + std::vector getValidObservationIds(const std::vector &apply) const; + + /// \brief Return the values of the specified variable at successive observation locations. + /// + /// If each independent group of observations is stored entirely on a single MPI rank, the + /// returned vector contains values observed at locations held on the current rank only. + /// Otherwise the vector is a concatenation of vectors obtained on all ranks. + std::vector getIntVariableFromObsSpace(const std::string &group, + const std::string &variable) const; + std::vector getFloatVariableFromObsSpace(const std::string &group, + const std::string &variable) const; + std::vector getDoubleVariableFromObsSpace(const std::string &group, + const std::string &variable) const; + std::vector getStringVariableFromObsSpace(const std::string &group, + const std::string &variable) const; + std::vector getDateTimeVariableFromObsSpace(const std::string &group, + const std::string &variable) const; + + /// \brief Return the vector of IDs of records successive observation locations belong to. + /// + /// If each independent group of observations is stored entirely on a single MPI rank, the + /// returned vector contains record IDs of observation locations held on the current rank + /// only. Otherwise the vector is a concatenation of vectors obtained on all ranks. + std::vector getRecordIds() const; + + /// If each independent group of observations is stored entirely on a single MPI rank, return the + /// number of observation locations held on the current rank. Otherwise return the total number + /// of observation locations held on all ranks. + size_t totalNumObservations() const; + + /// Construct a RecursiveSplitter object whose groups() method will return groups of observations + /// that can be processed independently from each other (according to the criterion specified when + /// the ObsAccessor was constructed). + RecursiveSplitter splitObservationsIntoIndependentGroups( + const std::vector &validObsIds) const; + + /// \brief Update flags of observations held on the current MPI rank. + /// + /// \param isRejected + /// A vector of length totalNumObservations() whose ith element indicates if ith observation + /// should be rejected. + /// + /// \param[inout] flagged + /// A vector of vectors, each with as many elements as there are observation locations on the + /// current MPI rank. On output, flagged[i][j] will be set to true for each i if the element of + /// isRejected corresponding to jth observation location on the current rank is true. + void flagRejectedObservations(const std::vector &isRejected, + std::vector > &flagged) const; + + private: + enum class GroupBy { NOTHING, RECORD_ID, VARIABLE }; + + /// Private constructor. Construct instances of this class by calling toAllObservations(), + /// toObservationsSplitIntoIndependentGroupsByRecordId() or + /// toObservationsSplitIntoIndependentGroupsByVariable() instead. + ObsAccessor(const ioda::ObsSpace &obsdb, + GroupBy groupBy, + boost::optional categoryVariable); + + bool wereRecordsGroupedByCategoryVariable() const; + + void groupObservationsByRecordNumber(const std::vector &validObsIds, + RecursiveSplitter &splitter) const; + + void groupObservationsByCategoryVariable(const std::vector &validObsIds, + RecursiveSplitter &splitter) const; + + private: + const ioda::ObsSpace *obsdb_; + std::shared_ptr obsDistribution_; + + GroupBy groupBy_; + boost::optional categoryVariable_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_OBSACCESSOR_H_ diff --git a/src/ufo/filters/ObsBoundsCheck.cc b/src/ufo/filters/ObsBoundsCheck.cc index 17b8fb8e3..c281cb610 100644 --- a/src/ufo/filters/ObsBoundsCheck.cc +++ b/src/ufo/filters/ObsBoundsCheck.cc @@ -21,23 +21,58 @@ #include "oops/util/missingValues.h" #include "ufo/filters/obsfunctions/ObsFunction.h" #include "ufo/filters/QCflags.h" +#include "ufo/utils/PrimitiveVariables.h" #include "ufo/utils/StringUtils.h" namespace ufo { +namespace { + // ----------------------------------------------------------------------------- -ObsBoundsCheck::ObsBoundsCheck(ioda::ObsSpace & obsdb, const eckit::Configuration & config, +/// Set each element of \p flagged to true if the corresponding element of \p apply is true +/// and the corresponding element of \p testValues is a non-missing value lying outside the +/// closed interval [\p minValue, \p maxValue]. +void flagWhereOutOfBounds(const std::vector & apply, + const std::vector & testValues, + float minValue, + float maxValue, + bool treatMissingAsOutOfBounds, + std::vector &flagged) { + const size_t nlocs = testValues.size(); + ASSERT(apply.size() == nlocs); + ASSERT(flagged.size() == nlocs); + + const float missing = util::missingValue(missing); + for (size_t i = 0; i < nlocs; ++i) { + if (apply[i]) { + if (testValues[i] != missing) { + if (minValue != missing && testValues[i] < minValue) + flagged[i] = true; + if (maxValue != missing && testValues[i] > maxValue) + flagged[i] = true; + } else { + if (treatMissingAsOutOfBounds) + flagged[i] = true; + } + } + } +} + +} // namespace + +// ----------------------------------------------------------------------------- + +ObsBoundsCheck::ObsBoundsCheck(ioda::ObsSpace & obsdb, const Parameters_ & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) { - if (config_.has("test variables")) { - std::vector testvarconf; - config_.get("test variables", testvarconf); - allvars_ += ufo::Variables(testvarconf); + if (parameters_.testVariables.value() != boost::none) { + for (const Variable & var : *parameters_.testVariables.value()) + allvars_ += var; } - oops::Log::debug() << "ObsBoundsCheck: config (constructor) = " << config_ << std::endl; + oops::Log::debug() << "ObsBoundsCheck: config (constructor) = " << parameters_ << std::endl; } // ----------------------------------------------------------------------------- @@ -49,104 +84,59 @@ ObsBoundsCheck::~ObsBoundsCheck() {} void ObsBoundsCheck::applyFilter(const std::vector & apply, const Variables & filtervars, std::vector> & flagged) const { - const float missing = util::missingValue(missing); - const oops::Variables observed = obsdb_.obsvariables(); - -// Find which variables are tested and the conditions + // Find the variables that should be tested. Use the variables specified in the 'test variables' + // option if present, otherwise the filter variables. ufo::Variables testvars; -// Use variables specified in test variables/functions for testing, otherwise filter variables - if (config_.has("test variables")) { - std::vector varconfs; - config_.get("test variables", varconfs); - testvars += ufo::Variables(varconfs); + if (parameters_.testVariables.value() != boost::none) { + for (const Variable & var : *parameters_.testVariables.value()) + testvars += var; } else { testvars += ufo::Variables(filtervars, "ObsValue"); } - const float vmin = config_.getFloat("minvalue", missing); - const float vmax = config_.getFloat("maxvalue", missing); - -// Sanity checks - if (filtervars.nvars() == 0) { - oops::Log::error() << "No variables will be filtered out in filter " - << config_ << std::endl; - ABORT("No variables specified to be filtered out in filter"); - } + if (!testvars) + throw eckit::UserError("ObsBoundsCheck: The list of test variables is empty", Here()); oops::Log::debug() << "ObsBoundsCheck: filtering " << filtervars << " with " << testvars << std::endl; - oops::Log::debug() << " sizes of each: " << filtervars.size() << " and " - << testvars.size() << std::endl; - -// Initialize map from filtervars to observed variables - std::vector filt2obs; - for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { - filt2obs.push_back(observed.find(filtervars.variable(jv).variable())); - } - - if (filtervars.size() == testvars.size()) { - // Loop over all test variables to get data - for (size_t iv = 0; iv < testvars.size(); ++iv) { - const std::string grp = testvars[iv].group(); - ioda::ObsDataVector testdata(obsdb_, testvars[iv].toOopsVariables()); - if (grp == "ObsFunction") { - data_.get(testvars[iv], testdata); - } else { - for (size_t ii = 0; ii < testvars[iv].size(); ++ii) { - size_t kv = ii + iv * testvars[iv].size(); - data_.get(testvars.variable(kv), testdata[ii]); - } - } - - std::vector test_jv(filtervars[iv].size(), 0); - if (testvars[iv].size() == filtervars[iv].size()) { - std::iota(test_jv.begin(), test_jv.end(), 0); - } - - // Loop over all variables to filter - for (size_t jv = 0; jv < filtervars[iv].size(); ++jv) { - for (size_t jobs = 0; jobs < obsdb_.nlocs(); ++jobs) { - if (apply[jobs] && (*flags_)[filt2obs[jv]][jobs] == QCflags::pass) { - ASSERT(testdata[test_jv[jv]][jobs] != missing); - size_t kv = jv + filtervars[iv].size() * iv; - if (vmin != missing && testdata[test_jv[jv]][jobs] < vmin) flagged[kv][jobs] = true; - if (vmax != missing && testdata[test_jv[jv]][jobs] > vmax) flagged[kv][jobs] = true; - } - } - } + // Retrieve the bounds. + const float missing = util::missingValue(missing); + const float vmin = parameters_.minvalue.value().value_or(missing); + const float vmax = parameters_.maxvalue.value().value_or(missing); + + // Determine the mode of operation. + const bool flagAllFilterVarsIfAnyTestVarOutOfBounds = + parameters_.testVariables.value() != boost::none && + (parameters_.flagAllFilterVarsIfAnyTestVarOutOfBounds.value() || + testvars.nvars() == 1); + const bool treatMissingAsOutOfBounds = parameters_.treatMissingAsOutOfBounds; + + // Do the actual work. + if (flagAllFilterVarsIfAnyTestVarOutOfBounds) { + std::vector anyTestVarOutOfBounds(obsdb_.nlocs(), false); + // Loop over all channels of all test variables and record all locations where any of these + // channels is out of bounds. + for (PrimitiveVariable singleChannelTestVar : PrimitiveVariables(testvars, data_)) { + const std::vector & testValues = singleChannelTestVar.values(); + flagWhereOutOfBounds(apply, testValues, vmin, vmax, treatMissingAsOutOfBounds, + anyTestVarOutOfBounds); } + // Copy these flags to the flags of all filtered variables. + for (std::vector &f : flagged) + f = anyTestVarOutOfBounds; } else { - int iv = 0; - if (testvars.size() != 1) { - oops::Log::error() << "When number filtervars not equal number of test vars, " - << "the latter can only be one." << config_ << std::endl; - ABORT("ONLY one testvar when filtervars>1 because its usage is ambiguous otherwise"); - } - - ioda::ObsDataVector testdata(obsdb_, testvars[iv].toOopsVariables()); - - const std::string grp = testvars[iv].group(); - - if (grp == "ObsFunction") { - data_.get(testvars[iv], testdata); - } else { - data_.get(testvars.variable(iv), testdata); - } - - for (size_t jv = 0; jv < filtervars.size(); ++jv) { - oops::Log::debug() << "ObsBoundsCheck: testing filter var with index " << jv << std::endl; - if (testvars[iv].size() != filtervars[jv].size()) { - oops::Log::error() << "Dimension of filtervar, " << filtervars[jv].size() - << " does not equal testvar dimension, " << testvars[iv].size() << std::endl; - ABORT("Aborting, sizes must be equivalent."); - } - for (size_t jobs = 0; jobs < obsdb_.nlocs(); ++jobs) { - if (apply[jobs] && (*flags_)[filt2obs[jv]][jobs] == QCflags::pass) { - ASSERT(testdata[iv][jobs] != missing); - if (vmin != missing && testdata[iv][jobs] < vmin) flagged[jv][jobs] = true; - if (vmax != missing && testdata[iv][jobs] > vmax) flagged[jv][jobs] = true; - } - } + if (filtervars.nvars() != testvars.nvars()) + throw eckit::UserError("The number of 'primitive' (single-channel) test variables must match " + "that of 'primitive' filter variables unless the 'flag all filter " + "variables if any test variable is out of bounds' option is set"); + // Loop over all channels of all test variables and for each locations where that channel is out + // of bounds, flag the corresponding filter variable channel. + ASSERT(filtervars.nvars() == flagged.size()); + size_t ifiltervar = 0; + for (PrimitiveVariable singleChannelTestVar : PrimitiveVariables(testvars, data_)) { + const std::vector & testValues = singleChannelTestVar.values(); + flagWhereOutOfBounds(apply, testValues, vmin, vmax, treatMissingAsOutOfBounds, + flagged[ifiltervar++]); } } } @@ -154,7 +144,7 @@ void ObsBoundsCheck::applyFilter(const std::vector & apply, // ----------------------------------------------------------------------------- void ObsBoundsCheck::print(std::ostream & os) const { - os << "ObsBoundsCheck: config = " << config_ << std::endl; + os << "ObsBoundsCheck: config = " << parameters_ << std::endl; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/ObsBoundsCheck.h b/src/ufo/filters/ObsBoundsCheck.h index 32dfde417..0583102d5 100644 --- a/src/ufo/filters/ObsBoundsCheck.h +++ b/src/ufo/filters/ObsBoundsCheck.h @@ -14,8 +14,12 @@ #include #include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/RequiredParameter.h" #include "ufo/filters/FilterBase.h" #include "ufo/filters/QCflags.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" namespace eckit { class Configuration; @@ -28,16 +32,65 @@ namespace ioda { namespace ufo { -/// ObsBoundsCheck: generic quality control based on observation data only +/// Parameters controlling the operation of the ObsBoundsCheck filter. +class ObsBoundsCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(ObsBoundsCheckParameters, FilterParametersBase) -// Check that observations are within some bounds over some domain + public: + /// Minimum allowed value of the tested variables. + oops::OptionalParameter minvalue{"minvalue", this}; + + /// Maximum allowed value of the tested variables. + oops::OptionalParameter maxvalue{"maxvalue", this}; + + /// Variables to be compared against the bounds specified in the `minvalue` and `maxvalue` + /// options. + /// + /// There are three valid possibilities: + /// + /// * If this option is not set, the filter will flag each filter variable at each location + /// where the measured value of that variable lies outside the specified bounds. + /// + /// * If this option is set to a single-element list containing only one single-channel variable + /// or the `flag all filter variables if any test variable is out of bounds` option is set to + /// `true`, the filter will flag each filter variable at each location where any test variable + /// lies outside the specified bounds. + /// + /// * If this option is set to a list with as many elements as there are filter variables and + /// the `flag all filter variables if any test variable is out of bounds` option is set to + /// `false`, the filter will flag each filter variable at each location + /// where the corresponding test variable lies outside the specified bounds. + oops::OptionalParameter> testVariables{"test variables", this}; + + /// Set this option to `true` to flag all filter variables at each location where any test + /// variable lies outside the specified bounds. + /// + /// This option is ignored if the `test variables` option is not set. + oops::Parameter flagAllFilterVarsIfAnyTestVarOutOfBounds{ + "flag all filter variables if any test variable is out of bounds", false, this}; + + /// By default, the filter flags filter variables at locations where the corresponding test + /// variable is set to the missing value indicator. Set this option to `false` to stop it from + /// doing so (hence assuming "optimistically" that the test variable was in fact in bounds). + oops::Parameter treatMissingAsOutOfBounds{"treat missing as out of bounds", true, this}; +}; + +/// \brief Flag observations that lie outside specified bounds. +/// +/// This is a generic quality control filter based on observation data only. +/// +/// See ObsBoundsCheckParameters for the documentation of the parameters controlling this filter. class ObsBoundsCheck : public FilterBase, private util::ObjectCounter { public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef ObsBoundsCheckParameters Parameters_; + static const std::string classname() {return "ufo::ObsBoundsCheck";} - ObsBoundsCheck(ioda::ObsSpace &, const eckit::Configuration &, + ObsBoundsCheck(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr >, std::shared_ptr >); ~ObsBoundsCheck(); @@ -47,6 +100,7 @@ class ObsBoundsCheck : public FilterBase, void applyFilter(const std::vector &, const Variables &, std::vector> &) const override; int qcFlag() const override {return QCflags::bounds;} + Parameters_ parameters_; }; } // namespace ufo diff --git a/src/ufo/filters/ObsDomainCheck.cc b/src/ufo/filters/ObsDomainCheck.cc index 6ddbdbcb4..5a19a0bb4 100644 --- a/src/ufo/filters/ObsDomainCheck.cc +++ b/src/ufo/filters/ObsDomainCheck.cc @@ -20,12 +20,12 @@ namespace ufo { // ----------------------------------------------------------------------------- -ObsDomainCheck::ObsDomainCheck(ioda::ObsSpace & obsdb, const eckit::Configuration & config, +ObsDomainCheck::ObsDomainCheck(ioda::ObsSpace & obsdb, const Parameters_ & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) { - oops::Log::debug() << "ObsDomainCheck: config = " << config_ << std::endl; + oops::Log::debug() << "ObsDomainCheck: config = " << parameters_ << std::endl; } // ----------------------------------------------------------------------------- @@ -47,7 +47,7 @@ void ObsDomainCheck::applyFilter(const std::vector & inside, // ----------------------------------------------------------------------------- void ObsDomainCheck::print(std::ostream & os) const { - os << "ObsDomainCheck: config = " << config_ << std::endl; + os << "ObsDomainCheck: config = " << parameters_ << std::endl; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/ObsDomainCheck.h b/src/ufo/filters/ObsDomainCheck.h index 6e5921038..ec0122dc8 100644 --- a/src/ufo/filters/ObsDomainCheck.h +++ b/src/ufo/filters/ObsDomainCheck.h @@ -28,6 +28,12 @@ namespace ioda { namespace ufo { +class ObsDomainCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(ObsDomainCheckParameters, FilterParametersBase) + + // This filter doesn't take any extra parameters. +}; + /// Domain check: generic check that obs are within domain // Domain is defined by metadata criteria regardless of obs value. @@ -39,9 +45,13 @@ namespace ufo { class ObsDomainCheck : public FilterBase, private util::ObjectCounter { public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef ObsDomainCheckParameters Parameters_; + static const std::string classname() {return "ufo::ObsDomainCheck";} - ObsDomainCheck(ioda::ObsSpace &, const eckit::Configuration &, + ObsDomainCheck(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr >, std::shared_ptr >); ~ObsDomainCheck(); @@ -51,6 +61,8 @@ class ObsDomainCheck : public FilterBase, void applyFilter(const std::vector &, const Variables &, std::vector> &) const override; int qcFlag() const override {return QCflags::domain;} + + Parameters_ parameters_; }; } // namespace ufo diff --git a/src/ufo/filters/ObsDomainErrCheck.cc b/src/ufo/filters/ObsDomainErrCheck.cc index 2ee83302b..1c90c7ca2 100644 --- a/src/ufo/filters/ObsDomainErrCheck.cc +++ b/src/ufo/filters/ObsDomainErrCheck.cc @@ -25,18 +25,14 @@ namespace ufo { // ----------------------------------------------------------------------------- -ObsDomainErrCheck::ObsDomainErrCheck(ioda::ObsSpace & obsdb, const eckit::Configuration & config, +ObsDomainErrCheck::ObsDomainErrCheck(ioda::ObsSpace & obsdb, const Parameters_ & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr), - parameter_(0.0) + : FilterBase(obsdb, parameters, flags, obserr), + parameters_(parameters) { - oops::Log::debug() << "ObsDomainErrCheck: config = " << config_ << std::endl; + oops::Log::debug() << "ObsDomainErrCheck: config = " << parameters_ << std::endl; ASSERT(obserr); - - const float missing = util::missingValue(missing); - float parameter_ = config.getFloat("infltparameter", missing); - ASSERT(parameter_ != missing); } // ----------------------------------------------------------------------------- @@ -63,6 +59,7 @@ void ObsDomainErrCheck::applyFilter(const std::vector & inside, } size_t count = 0; + const float parameter = parameters_.infltparameter; for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { size_t iv = observed.find(filtervars.variable(jv).variable()); for (size_t jobs = 0; jobs < obsdb_.nlocs(); ++jobs) { @@ -72,7 +69,7 @@ void ObsDomainErrCheck::applyFilter(const std::vector & inside, ASSERT((*obserr_)[iv][jobs] != util::missingValue((*obserr_)[iv][jobs])); ASSERT(obs[jv][jobs] != util::missingValue(obs[jv][jobs])); float bound = 2.5 * (*obserr_)[iv][jobs]; - float obserrinc = parameter_ * std::max((values[jobs]-9.0), 0.0) * (*obserr_)[iv][jobs]; + float obserrinc = parameter * std::max((values[jobs]-9.0), 0.0) * (*obserr_)[iv][jobs]; obserrinc = std::max((*obserr_)[iv][jobs], bound); (*obserr_)[iv][jobs] = sqrt(pow((*obserr_)[iv][jobs], 2) + pow(obserrinc, 2)); ++count; @@ -85,7 +82,7 @@ void ObsDomainErrCheck::applyFilter(const std::vector & inside, // ----------------------------------------------------------------------------- void ObsDomainErrCheck::print(std::ostream & os) const { - os << "ObsDomainErrCheck: config = " << config_ << std::endl; + os << "ObsDomainErrCheck: config = " << parameters_ << std::endl; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/ObsDomainErrCheck.h b/src/ufo/filters/ObsDomainErrCheck.h index 8f872f0bf..2df4c32fa 100644 --- a/src/ufo/filters/ObsDomainErrCheck.h +++ b/src/ufo/filters/ObsDomainErrCheck.h @@ -14,6 +14,7 @@ #include #include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/RequiredParameter.h" #include "ufo/filters/FilterBase.h" #include "ufo/filters/QCflags.h" @@ -28,21 +29,26 @@ namespace ioda { namespace ufo { -/// Domain check: AMSU-A scattering check and obserr inflation -// that obs are within domain +/// Parameters controlling the operation of the ObsDomainErrCheck filter. +class ObsDomainErrCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(ObsDomainErrCheckParameters, FilterParametersBase) -// Domain is defined by metadata criteria regardless of obs value. -// If obs value is required, use ObsBoundsCheck. + public: + oops::RequiredParameter infltparameter{"infltparameter", this}; +}; -// The same effect can be achieved with opposite criteria through BlackList, -// the choice is a matter of convenience or which seems more natural. +/// AMSU-A scattering check and obserr inflation class ObsDomainErrCheck : public FilterBase, private util::ObjectCounter { public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef ObsDomainErrCheckParameters Parameters_; + static const std::string classname() {return "ufo::ObsDomainErrCheck";} - ObsDomainErrCheck(ioda::ObsSpace &, const eckit::Configuration &, + ObsDomainErrCheck(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr >, std::shared_ptr >); ~ObsDomainErrCheck(); @@ -53,7 +59,7 @@ class ObsDomainErrCheck : public FilterBase, std::vector> &) const override; int qcFlag() const override {return QCflags::domain;} - float parameter_; + Parameters_ parameters_; }; } // namespace ufo diff --git a/src/ufo/filters/ObsFilterData.h b/src/ufo/filters/ObsFilterData.h index f9c1495b6..08e618960 100644 --- a/src/ufo/filters/ObsFilterData.h +++ b/src/ufo/filters/ObsFilterData.h @@ -82,6 +82,10 @@ class ObsFilterData : public util::Printable, size_t nlevs(const Variable &) const; //! Returns reference to ObsSpace associated with ObsFilterData ioda::ObsSpace & obsspace() const {return obsdb_;} + //! Returns reference to GeoVaLs required by 1DVar + const GeoVaLs * getGeoVaLs() const {return gvals_;} + //! Returns reference to ObsDiagnostics + const ObsDiagnostics * getObsDiags() const {return diags_;} private: void print(std::ostream &) const; bool hasVector(const std::string &, const std::string &) const; diff --git a/src/ufo/filters/ObsProcessorBase.cc b/src/ufo/filters/ObsProcessorBase.cc new file mode 100644 index 000000000..cb5f93f03 --- /dev/null +++ b/src/ufo/filters/ObsProcessorBase.cc @@ -0,0 +1,93 @@ +/* + * (C) Copyright 2017-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/ObsProcessorBase.h" + +#include +#include + +#include "eckit/config/Configuration.h" + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/interface/ObsFilter.h" +#include "oops/util/Logger.h" + +#include "ufo/filters/actions/FilterAction.h" +#include "ufo/filters/GenericFilterParameters.h" +#include "ufo/GeoVaLs.h" +#include "ufo/ObsDiagnostics.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- + +ObsProcessorBase::ObsProcessorBase(ioda::ObsSpace & os, bool deferToPost, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : obsdb_(os), + flags_(flags), obserr_(obserr), + data_(obsdb_), prior_(false), post_(false), + deferToPost_(deferToPost) +{ + oops::Log::trace() << "ObsProcessorBase constructor" << std::endl; + ASSERT(flags); + ASSERT(obserr); + data_.associate(*flags_, "QCflagsData"); + data_.associate(*obserr_, "ObsErrorData"); +} + +// ----------------------------------------------------------------------------- + +ObsProcessorBase::~ObsProcessorBase() { + oops::Log::trace() << "ObsProcessorBase destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsProcessorBase::preProcess() { + oops::Log::trace() << "ObsProcessorBase preProcess begin" << std::endl; +// Cannot determine earlier when to apply filter because subclass +// constructors add to allvars + if (allvars_.hasGroup("HofX") || allvars_.hasGroup("ObsDiag") || deferToPost_) { + post_ = true; + } else { + if (allvars_.hasGroup("GeoVaLs")) { + prior_ = true; + } else { + this->doFilter(); + } + } + oops::Log::trace() << "ObsProcessorBase preProcess end" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsProcessorBase::priorFilter(const GeoVaLs & gv) { + oops::Log::trace() << "ObsProcessorBase priorFilter begin" << std::endl; + if (prior_ || post_) data_.associate(gv); + if (prior_) this->doFilter(); + oops::Log::trace() << "ObsProcessorBase priorFilter end" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsProcessorBase::postFilter(const ioda::ObsVector & hofx, const ObsDiagnostics & diags) { + oops::Log::trace() << "ObsProcessorBase postFilter begin" << std::endl; + if (post_) { + data_.associate(hofx, "HofX"); + data_.associate(diags); + this->doFilter(); + } + oops::Log::trace() << "ObsProcessorBase postFilter end" << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/ObsProcessorBase.h b/src/ufo/filters/ObsProcessorBase.h new file mode 100644 index 000000000..954f467ff --- /dev/null +++ b/src/ufo/filters/ObsProcessorBase.h @@ -0,0 +1,74 @@ +/* + * (C) Copyright 2017-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSPROCESSORBASE_H_ +#define UFO_FILTERS_OBSPROCESSORBASE_H_ + +#include + +#include "ioda/ObsDataVector.h" +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "oops/util/Printable.h" +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/Variables.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + template class ObsDataVector; + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsDiagnostics; + +/// \brief Base class for UFO observation processors (including QC filters). +/// +/// Observation processors only need to implement the constructor and the doFilter method; +/// the base class takes care of applying the processor at the pre, prior or post stage. + +class ObsProcessorBase : public util::Printable { + public: + ObsProcessorBase(ioda::ObsSpace &, bool deferToPost, + std::shared_ptr >, + std::shared_ptr >); + ~ObsProcessorBase(); + + void preProcess(); + void priorFilter(const GeoVaLs &); + void postFilter(const ioda::ObsVector &, const ObsDiagnostics &); + + oops::Variables requiredVars() const { + return allvars_.allFromGroup("GeoVaLs").toOopsVariables();} + oops::Variables requiredHdiagnostics() const { + return allvars_.allFromGroup("ObsDiag").toOopsVariables();} + + protected: + ioda::ObsSpace & obsdb_; + std::shared_ptr> flags_; + std::shared_ptr> obserr_; + ufo::Variables allvars_; + ObsFilterData data_; + + private: + virtual void doFilter() const = 0; + + bool prior_; + bool post_; + + // Variables extracted from the filter parameters. + bool deferToPost_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_OBSPROCESSORBASE_H_ diff --git a/src/ufo/filters/PerformAction.cc b/src/ufo/filters/PerformAction.cc new file mode 100644 index 000000000..0d6ae3834 --- /dev/null +++ b/src/ufo/filters/PerformAction.cc @@ -0,0 +1,53 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/PerformAction.h" + +#include +#include + +#include "eckit/config/Configuration.h" + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- + +PerformAction::PerformAction(ioda::ObsSpace & obsdb, const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) +{ + oops::Log::debug() << "PerformAction: config = " << parameters_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +void PerformAction::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const { + for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { + for (size_t jobs = 0; jobs < obsdb_.nlocs(); ++jobs) { + flagged[jv][jobs] = apply[jobs]; + } + } +} + +// ----------------------------------------------------------------------------- + +void PerformAction::print(std::ostream & os) const { + os << "PerformAction: config = " << parameters_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/PerformAction.h b/src/ufo/filters/PerformAction.h new file mode 100644 index 000000000..085e5cecb --- /dev/null +++ b/src/ufo/filters/PerformAction.h @@ -0,0 +1,93 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_PERFORMACTION_H_ +#define UFO_FILTERS_PERFORMACTION_H_ + +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "oops/util/parameters/RequiredPolymorphicParameter.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/QCflags.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} + +namespace ufo { + +/// \brief Parameters controlling the action performed on observations flagged by a filter. +/// +/// The action must be specified explicitly (there is no default action). +class RequiredFilterActionParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(RequiredFilterActionParameters, Parameters) + + public: + /// After deserialization, holds an instance of a subclass of FilterActionParametersBase + /// controlling the behavior of a filter action. The type of the subclass is determined + /// by the value of the `name` key in the Configuration object from which this object + /// is deserialized. + oops::RequiredPolymorphicParameter + actionParameters{"name", this}; +}; + +/// \brief Parameters controlling the Perform Action filter. +class PerformActionParameters : public FilterParametersBaseWithAbstractAction { + OOPS_CONCRETE_PARAMETERS(PerformActionParameters, FilterParametersBaseWithAbstractAction) + + public: + /// Parameters controlling the action performed on observations flagged by the filter. + const FilterActionParametersBase &action() const override { + return action_.value().actionParameters.value(); + } + + private: + /// Parameters controlling the action performed on observations flagged by the filter. + oops::RequiredParameter action_{"action", this}; +}; + +/// \brief Perform the action specified in the `action` YAML section on each observation selected by +/// the `where` statement. +/// +/// In contrast to other filters, this filter requires the action to be specified explicitly in the +/// YAML file; there is no default action. +class PerformAction : public FilterBase, + private util::ObjectCounter { + public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef PerformActionParameters Parameters_; + + static const std::string classname() {return "ufo::PerformAction";} + + PerformAction(ioda::ObsSpace &, const Parameters_ &, + std::shared_ptr >, + std::shared_ptr >); + + private: + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override {return QCflags::black;} + + Parameters_ parameters_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_PERFORMACTION_H_ diff --git a/src/ufo/filters/PoissonDiskThinning.cc b/src/ufo/filters/PoissonDiskThinning.cc index 7e7590e9e..04783ee1f 100644 --- a/src/ufo/filters/PoissonDiskThinning.cc +++ b/src/ufo/filters/PoissonDiskThinning.cc @@ -24,6 +24,7 @@ #include "oops/util/IsAnyPointInVolumeInterior.h" #include "oops/util/Logger.h" #include "ufo/filters/getScalarOrFilterData.h" +#include "ufo/filters/ObsAccessor.h" #include "ufo/filters/PoissonDiskThinningParameters.h" #include "ufo/utils/Constants.h" #include "ufo/utils/RecursiveSplitter.h" @@ -137,18 +138,18 @@ struct PoissonDiskThinning::ObsData boost::optional> times; boost::optional> priorities; + + // Total number of observations held by all MPI tasks + size_t totalNumObs = 0; }; PoissonDiskThinning::PoissonDiskThinning(ioda::ObsSpace & obsdb, - const eckit::Configuration & config, + const Parameters_ ¶meters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr) + : FilterBase(obsdb, parameters, flags, obserr), options_(parameters) { - oops::Log::debug() << "PoissonDiskThinning: config = " << config_ << std::endl; - - options_.reset(new PoissonDiskThinningParameters()); - options_->deserialize(config); + oops::Log::debug() << "PoissonDiskThinning: config = " << options_ << std::endl; } // Required for the correct destruction of options_. @@ -158,20 +159,36 @@ PoissonDiskThinning::~PoissonDiskThinning() void PoissonDiskThinning::applyFilter(const std::vector & apply, const Variables & filtervars, std::vector> & flagged) const { - const std::vector validObsIds = getValidObservationIds(apply); + ObsAccessor obsAccessor = createObsAccessor(); - if (validObsIds.empty()) { - return; - } + const std::vector validObsIds = getValidObservationIds(apply, obsAccessor); int numSpatialDims, numNonspatialDims; - ObsData obsData = getObsData(numSpatialDims, numNonspatialDims); - - std::vector isThinned(apply.size(), false); + ObsData obsData = getObsData(obsAccessor, numSpatialDims, numNonspatialDims); + + std::vector isThinned(obsData.totalNumObs, false); + + if (options_.shuffle) { + // If the same observations will be processed on multiple ranks, they must use random number + // generators seeded with the same value because they need to reject the same observations. If + // all ranks process disjoint sets of observations, synchronisation of random number generators + // isn't necessary (and in fact somewhat undesirable). The sets of observations processed by + // different ranks will be disjoint if both of the following conditions are met: + // * the category_variable option is set to the variable used previously to split the + // observation space into records + // * each record is held on a single rank only (i.e. the observation space isn't using the + // InefficientDistribution). + // Unfortunately the second condition can't be checked without breaking the encapsulation + // of ioda::Distribution (and reintroducing the isDistributed() method). + // + // So to stay on the safe side we synchronise the random number generators whenever the shuffle + // option is selected. + synchroniseRandomNumberGenerators(obsdb_.comm()); + } // Thin points from each category separately. - RecursiveSplitter categorySplitter(validObsIds.size()); - groupObservationsByCategory(validObsIds, categorySplitter); + RecursiveSplitter categorySplitter = + obsAccessor.splitObservationsIntoIndependentGroups(validObsIds); for (auto categoryGroup : categorySplitter.multiElementGroups()) { std::vector obsIdsInCategory; for (size_t validObsIndex : categoryGroup) { @@ -181,75 +198,69 @@ void PoissonDiskThinning::applyFilter(const std::vector & apply, // Within each category, sort points by descending priority and then (if requested) // randomly shuffle points of equal priority. RecursiveSplitter prioritySplitter(obsIdsInCategory.size()); - groupObservationsByPriority(obsIdsInCategory, prioritySplitter); - if (options_->shuffle) { - if (options_->randomSeed.value() != boost::none) - prioritySplitter.shuffleGroups(*options_->randomSeed.value()); - else - prioritySplitter.shuffleGroups(); - } + groupObservationsByPriority(obsIdsInCategory, obsAccessor, prioritySplitter); + if (options_.shuffle) + prioritySplitter.shuffleGroups(); // Select points to retain within the category. thinCategory(obsData, obsIdsInCategory, prioritySplitter, numSpatialDims, numNonspatialDims, isThinned); } - flagThinnedObservations(isThinned, flagged); + obsAccessor.flagRejectedObservations(isThinned, flagged); if (filtervars.size() != 0) { oops::Log::trace() << "PoissonDiskThinning: flagged? = " << flagged[0] << std::endl; } } -PoissonDiskThinning::ObsData PoissonDiskThinning::getObsData(int &numSpatialDims, - int &numNonspatialDims) const +ObsAccessor PoissonDiskThinning::createObsAccessor() const { + if (options_.categoryVariable.value() != boost::none) { + return ObsAccessor::toObservationsSplitIntoIndependentGroupsByVariable( + obsdb_, *options_.categoryVariable.value()); + } else { + return ObsAccessor::toAllObservations(obsdb_); + } +} + +PoissonDiskThinning::ObsData PoissonDiskThinning::getObsData( + const ObsAccessor &obsAccessor, int &numSpatialDims, int &numNonspatialDims) const { ObsData obsData; numSpatialDims = 0; numNonspatialDims = 0; - { - obsData.minHorizontalSpacings = options_->minHorizontalSpacing.value(); - if (obsData.minHorizontalSpacings != boost::none) { - validateSpacings(*obsData.minHorizontalSpacings, "min_horizontal_spacing"); - obsData.latitudes.emplace(obsdb_.nlocs()); - obsData.longitudes.emplace(obsdb_.nlocs()); - obsdb_.get_db("MetaData", "latitude", *obsData.latitudes); - obsdb_.get_db("MetaData", "longitude", *obsData.longitudes); - numSpatialDims = 3; - } + obsData.minHorizontalSpacings = options_.minHorizontalSpacing.value(); + if (obsData.minHorizontalSpacings != boost::none) { + validateSpacings(*obsData.minHorizontalSpacings, "min_horizontal_spacing"); + obsData.latitudes = obsAccessor.getFloatVariableFromObsSpace("MetaData", "latitude"); + obsData.longitudes = obsAccessor.getFloatVariableFromObsSpace("MetaData", "longitude"); + numSpatialDims = 3; } - { - obsData.minVerticalSpacings = options_->minVerticalSpacing.value(); - if (obsData.minVerticalSpacings != boost::none) { - validateSpacings(*obsData.minVerticalSpacings, "min_vertical_spacing"); - obsData.pressures.emplace(obsdb_.nlocs()); - obsdb_.get_db("MetaData", "air_pressure", *obsData.pressures); - ++numNonspatialDims; - } + obsData.minVerticalSpacings = options_.minVerticalSpacing.value(); + if (obsData.minVerticalSpacings != boost::none) { + validateSpacings(*obsData.minVerticalSpacings, "min_vertical_spacing"); + obsData.pressures = obsAccessor.getFloatVariableFromObsSpace("MetaData", "air_pressure"); + ++numNonspatialDims; } - { - obsData.minTimeSpacings = options_->minTimeSpacing.value(); - if (obsData.minTimeSpacings != boost::none) { - validateSpacings(*obsData.minTimeSpacings, "min_time_spacing"); - obsData.times.emplace(obsdb_.nlocs()); - obsdb_.get_db("MetaData", "datetime", *obsData.times); - ++numNonspatialDims; - } + obsData.minTimeSpacings = options_.minTimeSpacing.value(); + if (obsData.minTimeSpacings != boost::none) { + validateSpacings(*obsData.minTimeSpacings, "min_time_spacing"); + obsData.times = obsAccessor.getDateTimeVariableFromObsSpace("MetaData", "datetime"); + ++numNonspatialDims; } - { - const boost::optional priorityVariable = options_->priorityVariable; - if (priorityVariable != boost::none) { - obsData.priorities.emplace(obsdb_.nlocs()); - obsdb_.get_db(priorityVariable.get().group(), priorityVariable.get().variable(), - *obsData.priorities); - } + const boost::optional priorityVariable = options_.priorityVariable; + if (priorityVariable != boost::none) { + obsData.priorities = obsAccessor.getIntVariableFromObsSpace( + priorityVariable.get().group(), priorityVariable.get().variable()); } + obsData.totalNumObs = obsAccessor.totalNumObservations(); + return obsData; } @@ -276,41 +287,52 @@ void PoissonDiskThinning::validateSpacings( } std::vector PoissonDiskThinning::getValidObservationIds( - const std::vector & apply) const { - std::vector validObsIds; - for (size_t obsId = 0; obsId < apply.size(); ++obsId) - if (apply[obsId] && (*flags_)[0][obsId] == QCflags::pass) - validObsIds.push_back(obsId); + const std::vector & apply, + const ObsAccessor &obsAccessor) const { + std::vector validObsIds = obsAccessor.getValidObservationIds(apply, *flags_); + + if (!options_.shuffle) { + // The user wants to process observations in fixed (non-random) order. Ensure the filter + // produces the same results regardless of the number of MPI ranks by ordering the observations + // to be processed as if we were running in serial: by record ID. + const std::vector recordIds = obsAccessor.getRecordIds(); + std::stable_sort(validObsIds.begin(), validObsIds.end(), + [&recordIds](size_t obsIdA, size_t obsIdB) + { return recordIds[obsIdA] < recordIds[obsIdB]; }); + } + return validObsIds; } -void PoissonDiskThinning::groupObservationsByCategory( - const std::vector &validObsIds, - RecursiveSplitter &splitter) const { - boost::optional categoryVariable = options_->categoryVariable; - if (categoryVariable == boost::none) - return; - - ioda::ObsDataVector obsDataVector(obsdb_, categoryVariable.get().variable(), - categoryVariable.get().group()); - const ioda::ObsDataRow &category = obsDataVector[0]; +void PoissonDiskThinning::synchroniseRandomNumberGenerators(const eckit::mpi::Comm &comm) const +{ + const size_t rootRank = 0; + + size_t seed; + if (options_.randomSeed.value() != boost::none) { + seed = *options_.randomSeed.value(); + } else { + if (comm.rank() == rootRank) + // Perhaps oops could provide a function returning this default seed. + seed = static_cast(std::time(nullptr)); + comm.broadcast(seed, rootRank); + } - std::vector validObsCategories(validObsIds.size()); - for (size_t validObsIndex = 0; validObsIndex < validObsIds.size(); ++validObsIndex) - validObsCategories[validObsIndex] = category[validObsIds[validObsIndex]]; - splitter.groupBy(validObsCategories); + RecursiveSplitter splitter(1); + splitter.setSeed(seed, true /* force? */); } void PoissonDiskThinning::groupObservationsByPriority( const std::vector &validObsIds, + const ObsAccessor &obsAccessor, RecursiveSplitter &splitter) const { - boost::optional priorityVariable = options_->priorityVariable; + boost::optional priorityVariable = options_.priorityVariable; if (priorityVariable == boost::none) return; - ioda::ObsDataVector obsDataVector(obsdb_, priorityVariable.get().variable(), - priorityVariable.get().group()); - const ioda::ObsDataRow &priority = obsDataVector[0]; + // TODO(wsmigaj): reuse the priority vector from obsData. + std::vector priority = obsAccessor.getIntVariableFromObsSpace( + priorityVariable.get().group(), priorityVariable.get().variable()); auto reverse = [](int i) { return -i - std::numeric_limits::lowest() + std::numeric_limits::max(); @@ -360,9 +382,9 @@ void PoissonDiskThinning::thinCategory(const ObsData &obsData, const size_t obsId = obsIdsInCategory[obsIndex]; std::array point = getObservationPosition(obsId, obsData); std::array semiAxes = getExclusionVolumeSemiAxes(obsId, obsData); - if ((options_->exclusionVolumeShape == ExclusionVolumeShape::CYLINDER && + if ((options_.exclusionVolumeShape == ExclusionVolumeShape::CYLINDER && pointIndex.isAnyPointInCylinderInterior(point, semiAxes, numSpatialDims)) || - (options_->exclusionVolumeShape == ExclusionVolumeShape::ELLIPSOID && + (options_.exclusionVolumeShape == ExclusionVolumeShape::ELLIPSOID && pointIndex.isAnyPointInEllipsoidInterior(point, semiAxes))) { isThinned[obsId] = true; } else { @@ -442,17 +464,8 @@ std::array PoissonDiskThinning::getExclusionVolumeSemiAxes( return semiAxes; } -void PoissonDiskThinning::flagThinnedObservations( - const std::vector & isThinned, - std::vector> & flagged) const { - for (std::vector & variableFlagged : flagged) - for (size_t obsId = 0; obsId < isThinned.size(); ++obsId) - if (isThinned[obsId]) - variableFlagged[obsId] = true; -} - void PoissonDiskThinning::print(std::ostream & os) const { - os << "PoissonDiskThinning: config = " << config_ << std::endl; + os << "PoissonDiskThinning: config = " << options_ << std::endl; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/PoissonDiskThinning.h b/src/ufo/filters/PoissonDiskThinning.h index 956514e6c..140a02a8f 100644 --- a/src/ufo/filters/PoissonDiskThinning.h +++ b/src/ufo/filters/PoissonDiskThinning.h @@ -19,6 +19,7 @@ #include "ioda/ObsDataVector.h" #include "oops/util/ObjectCounter.h" #include "ufo/filters/FilterBase.h" +#include "ufo/filters/PoissonDiskThinningParameters.h" #include "ufo/filters/QCflags.h" namespace eckit { @@ -39,7 +40,7 @@ namespace ufo { class DistanceCalculator; class EquispacedBinSelector; -class PoissonDiskThinningParameters; +class ObsAccessor; class RecursiveSplitter; class SpatialBinSelector; @@ -51,9 +52,11 @@ class SpatialBinSelector; class PoissonDiskThinning : public FilterBase, private util::ObjectCounter { public: + typedef PoissonDiskThinningParameters Parameters_; + static const std::string classname() {return "ufo::PoissonDiskThinning";} - PoissonDiskThinning(ioda::ObsSpace &obsdb, const eckit::Configuration &config, + PoissonDiskThinning(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, std::shared_ptr > flags, std::shared_ptr > obserr); @@ -67,6 +70,8 @@ class PoissonDiskThinning : public FilterBase, std::vector> &) const override; int qcFlag() const override {return QCflags::thinned;} + ObsAccessor createObsAccessor() const; + /// \brief Collect all observation data components used for thinning. /// /// \param[out] numSpatialDims @@ -74,23 +79,24 @@ class PoissonDiskThinning : public FilterBase, /// 0 otherwise). /// \param[out] numNonspatialDims /// Number of non-spatial dimensions used for thinning. - ObsData getObsData(int &numSpatialDims, int &numNonspatialDims) const; + ObsData getObsData(const ObsAccessor &obsAccessor, + int &numSpatialDims, int &numNonspatialDims) const; template void validateSpacings(const util::ScalarOrMap &spacingsByPriority, const std::string ¶meterName) const; - std::vector getValidObservationIds(const std::vector &apply) const; + std::vector getValidObservationIds(const std::vector &apply, + const ObsAccessor &obsAccessor) const; - void groupObservationsByCategory(const std::vector &validObsIds, - RecursiveSplitter &splitter) const; + /// Initialise random number generators used for shuffling with the same seed on + /// all processes belonging to the given communicator. + void synchroniseRandomNumberGenerators(const eckit::mpi::Comm &comm) const; void groupObservationsByPriority(const std::vector &validObsIds, + const ObsAccessor &obsAccessor, RecursiveSplitter &splitter) const; - void flagThinnedObservations(const std::vector &isThinned, - std::vector > &flagged) const; - void thinCategory(const ObsData &obsData, const std::vector &obsIdsInCategory, const RecursiveSplitter &prioritySplitter, @@ -115,7 +121,7 @@ class PoissonDiskThinning : public FilterBase, size_t obsId, const ObsData &obsData) const; private: - std::unique_ptr options_; + Parameters_ options_; }; } // namespace ufo diff --git a/src/ufo/filters/PoissonDiskThinningParameters.h b/src/ufo/filters/PoissonDiskThinningParameters.h index cfd58d2b0..3c10d0c89 100644 --- a/src/ufo/filters/PoissonDiskThinningParameters.h +++ b/src/ufo/filters/PoissonDiskThinningParameters.h @@ -17,6 +17,7 @@ #include "oops/util/parameters/Parameter.h" #include "oops/util/parameters/Parameters.h" #include "oops/util/parameters/ParameterTraitsScalarOrMap.h" +#include "ufo/filters/FilterParametersBase.h" #include "ufo/utils/parameters/ParameterTraitsVariable.h" namespace eckit { @@ -56,8 +57,8 @@ namespace ufo { /// \note The descriptions of several options refer to the _exclusion volume_, which is a domain /// surrounding the location of each observation. If an observation is retained, then no other /// observations lying in the interior of its exclusion volume may be retained at the same time. -class PoissonDiskThinningParameters : public oops::Parameters { - OOPS_CONCRETE_PARAMETERS(PoissonDiskThinningParameters, Parameters) +class PoissonDiskThinningParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(PoissonDiskThinningParameters, FilterParametersBase) public: typedef int Priority; @@ -127,8 +128,15 @@ class PoissonDiskThinningParameters : public oops::Parameters { // Observation categories - /// Variable storing integer-valued IDs associated with observations. Observations belonging - /// to different categories are thinned separately. + /// A string-valued or integer-valued variable. Observations with different values of that + /// variable will be thinned separately. + /// + /// If not set and observations were grouped into records when the observation space was + /// constructed, observations from each record will be thinned separately. If not set and + /// observations were not grouped into records, all observations will be thinned together. + /// + /// Note: the variable used to group observations into records can be set with the + /// \c obs space.obsdatain.obsgrouping.group variable YAML option. oops::OptionalParameter categoryVariable{"category_variable", this}; // Selection of observations to retain diff --git a/src/ufo/filters/ProfileBackgroundCheck.cc b/src/ufo/filters/ProfileBackgroundCheck.cc new file mode 100644 index 000000000..2ba0859e5 --- /dev/null +++ b/src/ufo/filters/ProfileBackgroundCheck.cc @@ -0,0 +1,178 @@ +/* * (C) Copyright 2017-2018 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/ProfileBackgroundCheck.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" + +#include "oops/interface/ObsFilter.h" +#include "oops/util/Logger.h" + +#include "ufo/filters/getScalarOrFilterData.h" +#include "ufo/filters/QCflags.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +/// ProfileBackgroundCheck: check observation closeness to background over a profile +/// +/// This will use the record number in obsdb_ to identify which observations belong to a +/// given profile (all members of a profile must share the same record number). For +/// each profile the RMS of observed-HofX is calculated, and this is compared against +/// the given threshold. If "relative threshold" is used (rather than "absolute threshold") +/// then the RMS is normalised by observation error. +/// +/// The threshold can be a floating-point constant or the name of a variable indicating +/// the threshold to use at each location. In this case, the value of the threshold is +/// taken from the threshold given for the first observation in the profile. +/// +/// This is related to BackgroundCheck, which checks each observation against a threshold. +/// There is also a group of other profile checks (ProfileConsistencyChecks) which are +/// mostly aimed to processing radiosondes. + +ProfileBackgroundCheck::ProfileBackgroundCheck( + ioda::ObsSpace & obsdb, + const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) +{ + oops::Log::trace() << "ProfileBackgroundCheck constructor" << std::endl; + + ASSERT(parameters_.relativeThreshold.value() || + parameters_.absoluteThreshold.value()); +} + +// ----------------------------------------------------------------------------- + +ProfileBackgroundCheck::~ProfileBackgroundCheck() { + oops::Log::trace() << "ProfileBackgroundCheck destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- +/// Apply the profile background check filter. + +void ProfileBackgroundCheck::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const { + oops::Log::trace() << "ProfileBackgroundCheck postFilter" << std::endl; + const oops::Variables observed = obsdb_.obsvariables(); + + oops::Log::debug() << "ProfileBackgroundCheck obserr: " << *obserr_; + + ioda::ObsDataVector obs(obsdb_, filtervars.toOopsVariables(), "ObsValue"); + ioda::ObsDataVector bias(obsdb_, filtervars.toOopsVariables(), "ObsBias", false); + + // Get the record numbers from the observation data. These will be used to identify + // which observations belong to which profile. + const std::vector &unique = obsdb_.recidx_all_recnums(); + oops::Log::debug() << "Unique record numbers" << std::endl; + for (size_t iUnique : unique) + oops::Log::debug() << iUnique << ' '; + oops::Log::debug() << std::endl; + + // Threshold for all variables + std::vector abs_thr(obsdb_.nlocs(), std::numeric_limits::max()); + std::vector rel_thr(obsdb_.nlocs(), std::numeric_limits::max()); + if (parameters_.absoluteThreshold.value()) + abs_thr = getScalarOrFilterData(*parameters_.absoluteThreshold.value(), data_); + if (parameters_.relativeThreshold.value()) + rel_thr = getScalarOrFilterData(*parameters_.relativeThreshold.value(), data_); + + Variables varhofx(filtervars_, "HofX"); + for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { + size_t iv = observed.find(filtervars.variable(jv).variable()); + // H(x) + std::vector hofx; + data_.get(varhofx.variable(jv), hofx); + + // Loop over the unique profiles + for (const auto& iprofile : unique) { + std::vector obs_numbers = obsdb_.recidx_vector(iprofile); + // Initialise the accumulators for this profile + double total_diff = 0; + int total_nobs = 0; + bool using_rel_thresh = false; + bool first_ob = true; + float profile_threshold = 0; + + // First run through all the observations to calculate the O-B statistics + // (normalised by the observation error). + // Determine whether this profile uses threshold or abs_threshold based + // on the first value in the profile. + for (size_t jobs : obs_numbers) { + if (apply[jobs] && (*flags_)[iv][jobs] == QCflags::pass) { + ASSERT((*obserr_)[iv][jobs] != util::missingValue((*obserr_)[iv][jobs])); + ASSERT(obs[jv][jobs] != util::missingValue(obs[jv][jobs])); + ASSERT(hofx[jobs] != util::missingValue(hofx[jobs])); + + if (first_ob) { + if (rel_thr[jobs] == std::numeric_limits::max()) { + using_rel_thresh = false; + profile_threshold = abs_thr[jobs]; + } else { + using_rel_thresh = true; + profile_threshold = rel_thr[jobs]; + } + first_ob = false; + ASSERT(profile_threshold < std::numeric_limits::max() && + profile_threshold > 0.0f); + } + + // Apply bias correction + const float yy = obs[jv][jobs] - bias[jv][jobs]; + + // Accumulate distance from background + if ((*obserr_)[iv][jobs] > 0) { + total_nobs++; + if (using_rel_thresh) { + // Normalise by observation error + total_diff += std::pow((hofx[jobs] - yy) / (*obserr_)[iv][jobs], 2); + } else { + // Use the absolute differences + total_diff += std::pow(hofx[jobs] - yy, 2); + } + } + } + } + + // Calculate average difference over profile + if (total_nobs > 0) { + total_diff = std::pow(total_diff / total_nobs, 0.5); + } + oops::Log::debug() << "ProfileBackgroundCheck: For profile " << iprofile << + " rms diff is " << total_diff << std::endl; + + // Second run through the observations to apply the threshold to reject profiles + for (size_t jobs : obs_numbers) { + if (apply[jobs] && (*flags_)[iv][jobs] == QCflags::pass) { + // If rejecting this profile, then flag all elements + if (total_diff > static_cast(profile_threshold)) flagged[jv][jobs] = true; + } + } + } + } +} + +// ----------------------------------------------------------------------------- + +void ProfileBackgroundCheck::print(std::ostream & os) const { + os << "ProfileBackgroundCheck: config = " << parameters_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/ProfileBackgroundCheck.h b/src/ufo/filters/ProfileBackgroundCheck.h new file mode 100644 index 000000000..5bb6d59ca --- /dev/null +++ b/src/ufo/filters/ProfileBackgroundCheck.h @@ -0,0 +1,81 @@ +/* + * (C) Copyright 2017-2018 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_PROFILEBACKGROUNDCHECK_H_ +#define UFO_FILTERS_PROFILEBACKGROUNDCHECK_H_ + +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/Printable.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/QCflags.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} + +namespace ufo { + +/// Parameters controlling the operation of the ProfileBackgroundCheck filter. +class ProfileBackgroundCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(ProfileBackgroundCheckParameters, FilterParametersBase) + + public: + /// The filter will flag observations whose bias-corrected value differs from its model equivalent + /// by more than `relative threshold` times the current estimate of the observation error. + /// + /// `relative threshold` can be a real number or the name of a variable. + oops::OptionalParameter relativeThreshold{"relative threshold", this}; + + /// The filter will flag observations whose bias-corrected value differs from its model equivalent + /// by more than `absolute threshold` + /// + /// `absolute threshold` can be a real number or the name of a variable. + oops::OptionalParameter absoluteThreshold{"absolute threshold", this}; +}; + +/// ProfileBackgroundCheck: check observation closeness to background over a profile +/// +/// See the cc file for more details. + +class ProfileBackgroundCheck : public FilterBase, + private util::ObjectCounter { + public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef ProfileBackgroundCheckParameters Parameters_; + + static const std::string classname() {return "ufo::ProfileBackgroundCheck";} + + ProfileBackgroundCheck(ioda::ObsSpace &, const Parameters_ &, + std::shared_ptr >, + std::shared_ptr >); + ~ProfileBackgroundCheck(); + + private: + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override {return QCflags::fguess;} + Parameters_ parameters_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_PROFILEBACKGROUNDCHECK_H_ diff --git a/src/ufo/filters/ProfileConsistencyCheckParameters.h b/src/ufo/filters/ProfileConsistencyCheckParameters.h index 2e09ed89f..4e06c7ba2 100644 --- a/src/ufo/filters/ProfileConsistencyCheckParameters.h +++ b/src/ufo/filters/ProfileConsistencyCheckParameters.h @@ -8,6 +8,7 @@ #ifndef UFO_FILTERS_PROFILECONSISTENCYCHECKPARAMETERS_H_ #define UFO_FILTERS_PROFILECONSISTENCYCHECKPARAMETERS_H_ +#include #include #include @@ -17,6 +18,8 @@ #include "oops/util/parameters/Parameter.h" #include "oops/util/parameters/Parameters.h" +#include "ufo/filters/FilterParametersBase.h" + #include "ufo/profile/DataHandlerParameters.h" #include "ufo/utils/Constants.h" @@ -30,23 +33,16 @@ namespace eckit { namespace ufo { /// \brief Options controlling the operation of the ProfileConsistencyChecks filter. - class ProfileConsistencyCheckParameters : public DataHandlerParameters { - OOPS_CONCRETE_PARAMETERS(ProfileConsistencyCheckParameters, DataHandlerParameters) + class ProfileConsistencyCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(ProfileConsistencyCheckParameters, FilterParametersBase) public: // variables /// @name Generic parameters /// @{ - /// Maximum number of profile levels to be processed (a legacy of the OPS code). - /// No maximum is assigned if this parameter is not specified. - oops::OptionalParameter maxlev {"maxlev", this}; - /// List of checks to perform oops::Parameter> Checks {"Checks", {}, this}; - /// If not sorting observations, ensure number of profiles is consistent - oops::Parameter ValidateTotalNumProf {"ValidateTotalNumProf", true, this}; - /// Print station ID oops::Parameter PrintStationID {"PrintStationID", false, this}; @@ -314,6 +310,82 @@ namespace ufo { /// @} + /// @name Profile averaging parameters + /// @{ + + /// Factor used to determine big gaps for sondes + /// (dimensionless; multiplied by log(10)). + oops::Parameter AvgP_SondeGapFactor {"AvgP_SondeGapFactor", 1.0, this}; + + /// Factor used to determine big gaps for wind profilers + /// (dimensionless; multiplied by log(10)). + oops::Parameter AvgP_WinProGapFactor {"AvgP_WinProGapFactor", 1.0, this}; + + /// Minimum value of denominator used when computing big gaps + /// (dimensionless; equal to log (pressure threshold / hPa)). + oops::Parameter AvgP_GapLogPDiffMin {"AvgP_GapLogPDiffMin", std::log(5.0), this}; + + /// Minimum fraction of a model layer that must have been covered (in the vertical coordinate) + /// by observed values in order for temperature to be averaged onto that layer. + oops::Parameter AvgT_SondeDZFraction {"AvgT_SondeDZFraction", 0.5, this}; + + /// Probability of gross error threshold above which rejection flags are set + /// in the temperature averaging routine. + oops::Parameter AvgT_PGEskip {"AvgT_PGEskip", 0.9, this}; + + /// Minimum fraction of a model layer that must have been covered (in the vertical coordinate) + /// by observed values in order for wind speed to be averaged onto that layer. + oops::Parameter AvgU_SondeDZFraction {"AvgU_SondeDZFraction", 0.5, this}; + + /// Probability of gross error threshold above which rejection flags are set + /// in the wind speed averaging routine. + oops::Parameter AvgU_PGEskip {"AvgU_PGEskip", 0.9, this}; + + /// Probability of gross error threshold above which rejection flags are set + /// in the relative humidity averaging routine. + oops::Parameter AvgRH_PGEskip {"AvgRH_PGEskip", 0.9, this}; + + /// Minimum fraction of a model layer that must have been covered (in the vertical coordinate) + /// by observed values in order for relative humidity to be averaged onto that layer. + oops::Parameter AvgRH_SondeDZFraction {"AvgRH_SondeDZFraction", 0.5, this}; + + /// Perform interpolation or averaging of relative humidity observations? + oops::Parameter AvgRH_Interp {"AvgRH_Interp", true, this}; + + /// Default average temperature threshold below which average relative humidity + /// observations are rejected (degrees C). + oops::Parameter AvgRH_AvgTThreshold {"AvgRH_AvgTThreshold", -40.0, this}; + + /// Custom average temperature thresholds below which average relative humidity + /// observations are rejected (degrees C). + /// These thresholds are stored in a map with keys equal to the WMO codes for + /// radiosonde instrument types and values equal to the custom thresholds. + /// + /// The full list of codes can be found in "WMO Manual on Codes - + /// International Codes, Volume I.2, Annex II to the WMO Technical Regulations: + /// Part C - Common Features to Binary and Alphanumeric Codes" + /// (available at https://library.wmo.int/?lvl=notice_display&id=10684). + /// + /// The default list of custom thresholds applies to the following codes: + /// - Types 37, 52, 60-63, 66-67 = Vaisala RS80, + /// - Types 71-74, 78 = Vaisala RS90, + /// - Types 79-81 = Vaisala RS92. + /// + /// To customise this list in the yaml file, please note the following information from + /// oops/util/parameter/ParameterTraits.h: + /// Owing to a bug in the eckit YAML parser, maps need to be written in the JSON style, + /// with keys quoted. Example: + /// my_int_to_float_map: {"1": 123, "2": 321} + oops::Parameter> AvgRH_InstrTThresholds + {"AvgRH_InstrTThresholds", + {{37, -60.0}, {52, -60.0}, {60, -60.0}, {61, -60.0}, + {62, -60.0}, {63, -60.0}, {66, -60.0}, {67, -60.0}, + {71, -80.0}, {72, -80.0}, {73, -80.0}, {74, -80.0}, + {78, -80.0}, {79, -80.0}, {80, -80.0}, {81, -80.0} + }, this}; + + /// @} + /// @name OPS comparison parameters /// @{ @@ -325,8 +397,16 @@ namespace ufo { /// @} + /// @name Parameters classes + /// @{ + + /// Parameters related to profile data handler + DataHandlerParameters DHParameters{this}; + /// Parameters related to PGE calculations ProbabilityOfGrossErrorParameters PGEParameters{this}; + + /// @} }; } // namespace ufo diff --git a/src/ufo/filters/ProfileConsistencyChecks.cc b/src/ufo/filters/ProfileConsistencyChecks.cc index 6757e4f90..6dcb6a0b7 100644 --- a/src/ufo/filters/ProfileConsistencyChecks.cc +++ b/src/ufo/filters/ProfileConsistencyChecks.cc @@ -21,16 +21,10 @@ #include "oops/util/abor1_cpp.h" #include "oops/util/Logger.h" -#include "ufo/filters/getScalarOrFilterData.h" #include "ufo/filters/ProfileConsistencyCheckParameters.h" #include "ufo/filters/ProfileConsistencyChecks.h" -#include "ufo/profile/EntireSampleDataHandler.h" -#include "ufo/profile/ProfileChecker.h" -#include "ufo/profile/ProfileCheckValidator.h" -#include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" -#include "ufo/profile/VariableNames.h" +#include "ufo/GeoVaLs.h" namespace ufo { @@ -38,21 +32,47 @@ namespace ufo { ProfileConsistencyChecks::ProfileConsistencyChecks (ioda::ObsSpace & obsdb, - const eckit::Configuration & config, + const Parameters_ & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr) + : FilterBase(obsdb, parameters, flags, obserr), options_(parameters) { - options_.reset(new ProfileConsistencyCheckParameters()); - options_->deserialize(config); - allvars_ += Variables(filtervars_, "HofX"); - // Throw exception if expected configuration option is missing. + // Add the names of any required GeoVaLs to \c allvars_. + // This is performed here because \c allvars_ must be filled prior to the filter being run. + // The names of the required GeoVaLs are listed in the \c getGeoVaLNames() function + // of each check (which returns an empty set by default). + // The \c profileChecker class must be instantiated in order to do this. + const ProfileChecker profileChecker(options_); + const auto &requiredGeoVaLNames = profileChecker.getGeoVaLNames(); + if (requiredGeoVaLNames.size() > 0) { + ufo::Variables reqGeoVaLs(requiredGeoVaLNames); + allvars_ += Variables(reqGeoVaLs, "GeoVaLs"); + } + + // Add the names of any required obs diagnostics to \c allvars_. + // The names of the required obs diagnostics are listed in the \c getObsDiagNames() function + // of each check (which returns an empty set by default). + const auto &requiredObsDiagNames = profileChecker.getObsDiagNames(); + if (requiredObsDiagNames.size() > 0) { + ufo::Variables reqObsDiags(requiredObsDiagNames); + allvars_ += Variables(reqObsDiags, "ObsDiag"); + } + + if (options_.compareWithOPS.value()) { + const auto &validationGeoVaLNames = profileChecker.getValidationGeoVaLNames(); + if (validationGeoVaLNames.size() > 0) { + ufo::Variables valGeoVaLs(validationGeoVaLNames); + allvars_ += Variables(valGeoVaLs, "GeoVaLs"); + } + } + // It is essential for observations to be grouped according to (e.g.) station ID - // (unless there is only one profile in the sample, which would be very unusual) - if (obsdb.obs_group_var().empty()) - throw eckit::BadParameter("group variable is empty.", Here()); + // (unless there is only one profile in the sample, which would be very unusual). + // Throw an exception if the "group variable" configuration option is missing. + if (obsdb.obs_group_vars().empty()) + throw eckit::BadParameter("group variables configuration is empty.", Here()); } // ----------------------------------------------------------------------------- @@ -61,39 +81,16 @@ namespace ufo { // ----------------------------------------------------------------------------- - void ProfileConsistencyChecks::applyFilter(const std::vector & apply, - const Variables & filtervars, - std::vector> & flagged) const + void ProfileConsistencyChecks::individualProfileChecks + (ProfileDataHandler &profileDataHandler, + ProfileCheckValidator &profileCheckValidator, + ProfileChecker &profileChecker, + const CheckSubgroup &subGroupChecks) const { - print(oops::Log::trace()); - - const int nlocs = static_cast (obsdb_.nlocs()); const int nprofs = static_cast (obsdb_.nrecs()); - // Handles data in entire sample - EntireSampleDataHandler entireSampleDataHandler(obsdb_, - *options_); - - // Determines indices of profile's observations in entire sample - ProfileIndices profileIndices(obsdb_, - *options_, - apply); - - // Handles individual profile data - ProfileDataHandler profileDataHandler(obsdb_, - *options_, - entireSampleDataHandler, - profileIndices); - - // (Optionally) validates check results against OPS values - ProfileCheckValidator profileCheckValidator(*options_, - profileDataHandler); - - // Applies checks to each profile - ProfileChecker profileChecker(*options_, - profileIndices, - profileDataHandler, - profileCheckValidator); + // Reset profile indices prior to looping through entire sample. + profileDataHandler.resetProfileIndices(); // Loop over profiles oops::Log::debug() << "Starting loop over profiles..." << std::endl; @@ -101,14 +98,11 @@ namespace ufo { for (int jprof = 0; jprof < nprofs; ++jprof) { oops::Log::debug() << "Profile " << (jprof + 1) << " / " << nprofs << std::endl; - // Determine indices in entire sample that correspond to this profile - profileIndices.determineProfileIndices(); - - // Reset contents of profile data handler - profileDataHandler.reset(); + // Initialise the next profile prior to applying checks. + profileDataHandler.initialiseNextProfile(); // Print station ID if requested - if (options_->PrintStationID.value()) { + if (options_.PrintStationID.value()) { const std::vector &station_ID = profileDataHandler.get(ufo::VariableNames::station_ID); if (!station_ID.empty()) @@ -116,36 +110,95 @@ namespace ufo { } // Run checks - profileChecker.runChecks(); + profileChecker.runChecks(profileDataHandler, + subGroupChecks); - // After all checks have run, set final report flags in this profile - profileDataHandler.setFinalReportFlags(); + // Update information, including the 'flagged' vector, for this profile. + profileDataHandler.updateProfileInformation(); + } - // Modify 'flagged' vector for each filter variable based on check results - profileDataHandler.setFlagged(filtervars.nvars(), flagged); + // Write various quantities to the obsdb. + profileDataHandler.writeQuantitiesToObsdb(); - // If any variables in the current profile were modified by the checks, - // the equivalent variables in the entire sample are set to the modified values. - profileDataHandler.updateEntireSampleData(); + oops::Log::debug() << "... Finished loop over profiles" << std::endl; + oops::Log::debug() << std::endl; + } - // Optionally compare check results with OPS values - if (options_->compareWithOPS.value() && profileChecker.getBasicCheckResult()) { - profileCheckValidator.validate(); - nMismatches_.emplace_back(profileCheckValidator.getMismatches()); - } - } + // ----------------------------------------------------------------------------- - // Write out any quantities that may have changed to obsdb - entireSampleDataHandler.writeQuantitiesToObsdb(); + void ProfileConsistencyChecks::entireSampleChecks + (ProfileDataHandler &profileDataHandler, + ProfileCheckValidator &profileCheckValidator, + ProfileChecker &profileChecker, + const CheckSubgroup &subGroupChecks) const + { + oops::Log::debug() << "Running checks on entire profile sample..." << std::endl; - oops::Log::debug() << "... Finished loop over profiles" << std::endl; + // Run checks + profileChecker.runChecks(profileDataHandler, + subGroupChecks); + + // Write various quantities to the obsdb. + profileDataHandler.writeQuantitiesToObsdb(); + + oops::Log::debug() << "... Finished running checks" << std::endl; oops::Log::debug() << std::endl; } // ----------------------------------------------------------------------------- + void ProfileConsistencyChecks::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const + { + print(oops::Log::trace()); + + // Handles individual profile data + ProfileDataHandler profileDataHandler(data_, + options_.DHParameters, + apply, + filtervars, + flagged); + + // (Optionally) validates check results against OPS values + ProfileCheckValidator profileCheckValidator(options_); + + // Applies checks to each profile + ProfileChecker profileChecker(options_); + + // Loop over each check subgroup in turn. + const auto checkSubgroups = profileChecker.getCheckSubgroups(); + for (const auto& checkSubgroup : checkSubgroups) { + if (checkSubgroup.runOnEntireSample) { + // Run checks that use all of the profiles at once. + entireSampleChecks(profileDataHandler, + profileCheckValidator, + profileChecker, + checkSubgroup); + } else { + // Run checks on individual profiles sequentially. + individualProfileChecks(profileDataHandler, + profileCheckValidator, + profileChecker, + checkSubgroup); + } + } + + // Optionally compare check results with OPS values + if (options_.compareWithOPS.value()) { + profileDataHandler.resetProfileIndices(); + for (int jprof = 0; jprof < obsdb_.nrecs(); ++jprof) { + profileDataHandler.initialiseNextProfile(); + profileCheckValidator.validate(profileDataHandler, obsdb_.comm().size()); + nMismatches_.emplace_back(profileCheckValidator.getMismatches()); + } + } + } + + // ----------------------------------------------------------------------------- + void ProfileConsistencyChecks::print(std::ostream & os) const { - os << "ProfileConsistencyChecks: config = " << config_ << std::endl; + os << "ProfileConsistencyChecks: config = " << options_ << std::endl; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/ProfileConsistencyChecks.h b/src/ufo/filters/ProfileConsistencyChecks.h index 488940983..84d6837d0 100644 --- a/src/ufo/filters/ProfileConsistencyChecks.h +++ b/src/ufo/filters/ProfileConsistencyChecks.h @@ -17,8 +17,15 @@ #include "oops/util/Printable.h" #include "ufo/filters/FilterBase.h" +#include "ufo/filters/ProfileConsistencyCheckParameters.h" #include "ufo/filters/QCflags.h" +#include "ufo/profile/EntireSampleDataHandler.h" +#include "ufo/profile/ProfileChecker.h" +#include "ufo/profile/ProfileCheckValidator.h" +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/VariableNames.h" + #include "ufo/utils/Constants.h" namespace eckit { @@ -30,10 +37,6 @@ namespace ioda { class ObsSpace; } -namespace ufo { - class ProfileConsistencyCheckParameters; -} - namespace ufo { /// \brief Profile QC checks. @@ -80,9 +83,11 @@ namespace ufo { class ProfileConsistencyChecks : public FilterBase, private util::ObjectCounter { public: + typedef ProfileConsistencyCheckParameters Parameters_; + static const std::string classname() {return "ufo::ProfileConsistencyChecks";} - ProfileConsistencyChecks(ioda::ObsSpace &, const eckit::Configuration &, + ProfileConsistencyChecks(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr >, std::shared_ptr >); ~ProfileConsistencyChecks(); @@ -98,10 +103,23 @@ namespace ufo { void print(std::ostream &) const override; void applyFilter(const std::vector &, const Variables &, std::vector> &) const override; + + /// Run checks on individual profiles sequentially. + void individualProfileChecks(ProfileDataHandler &profileDataHandler, + ProfileCheckValidator &profileCheckValidator, + ProfileChecker &profileChecker, + const CheckSubgroup &subGroupChecks) const; + + /// Run checks that use all of the profiles at once. + void entireSampleChecks(ProfileDataHandler &profileDataHandler, + ProfileCheckValidator &profileCheckValidator, + ProfileChecker &profileChecker, + const CheckSubgroup &subGroupChecks) const; + int qcFlag() const override {return QCflags::profile;} /// Configurable options - std::unique_ptr options_; + ProfileConsistencyCheckParameters options_; /// Number of mismatches between values produced in this code /// and their OPS equivalents (used for validation) diff --git a/src/ufo/filters/ProfileFewObsCheck.cc b/src/ufo/filters/ProfileFewObsCheck.cc new file mode 100644 index 000000000..5301dd831 --- /dev/null +++ b/src/ufo/filters/ProfileFewObsCheck.cc @@ -0,0 +1,94 @@ +/* + * (C) British Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/ProfileFewObsCheck.h" + +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" + +#include "oops/util/Logger.h" +#include "ufo/filters/QCflags.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +/// ProfileFewObsCheck: Check the number of observations in a profile +/// +/// This will use the record number in obsdb_ to identify which observations belong to a +/// given profile (all members of a profile must share the same record number). +/// For each profile the number of valid observations is found, and the profile +/// is rejected if this is below the given threshold. + +ProfileFewObsCheck::ProfileFewObsCheck( + ioda::ObsSpace & obsdb, + const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) +{ + oops::Log::trace() << "ProfileFewObsCheck constructor" << std::endl; +} + +// ----------------------------------------------------------------------------- + +ProfileFewObsCheck::~ProfileFewObsCheck() { + oops::Log::trace() << "ProfileFewObsCheck destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- +/// Apply the profile check for the number of observations. + +void ProfileFewObsCheck::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const { + oops::Log::trace() << "ProfileFewObsCheck preProcess filter" << std::endl; + const oops::Variables observed = obsdb_.obsvariables(); + + // Get the record numbers from the observation data. These will be used to identify + // which observations belong to which profile. + const std::vector & record_numbers = obsdb_.recidx_all_recnums(); + oops::Log::debug() << "Unique record numbers" << std::endl; + for (size_t iProfile : record_numbers) + oops::Log::debug() << iProfile << ' '; + oops::Log::debug() << std::endl; + + // For each variable, check the number of observations in the profile + for (size_t iFilterVar = 0; iFilterVar < filtervars.nvars(); ++iFilterVar) { + const size_t iVar = observed.find(filtervars.variable(iFilterVar).variable()); + + // Loop over the unique profiles + for (size_t iProfile : record_numbers) { + const std::vector & obs_numbers = obsdb_.recidx_vector(iProfile); + + // Count the number of valid observations in this profile + int numValid = 0; + for (size_t jobs : obs_numbers) + if (apply[jobs] && (*flags_)[iVar][jobs] == QCflags::pass) + numValid++; + + // Reject profiles which don't contain sufficient observations + if (numValid < parameters_.threshold.value()) + for (size_t jobs : obs_numbers) + if (apply[jobs] && (*flags_)[iVar][jobs] == QCflags::pass) + flagged[iFilterVar][jobs] = true; + } + } +} + +// ----------------------------------------------------------------------------- + +void ProfileFewObsCheck::print(std::ostream & os) const { + os << "ProfileFewObsCheck: config = " << parameters_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/ProfileFewObsCheck.h b/src/ufo/filters/ProfileFewObsCheck.h new file mode 100644 index 000000000..8fc45d9c4 --- /dev/null +++ b/src/ufo/filters/ProfileFewObsCheck.h @@ -0,0 +1,68 @@ +/* + * (C) British Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_PROFILEFEWOBSCHECK_H_ +#define UFO_FILTERS_PROFILEFEWOBSCHECK_H_ + +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/NumericConstraints.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "oops/util/Printable.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/QCflags.h" + +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} + +namespace ufo { + +/// Parameters controlling the operation of the ProfileFewObsCheck filter. +class ProfileFewObsCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(ProfileFewObsCheckParameters, FilterParametersBase) + + public: + /// The filter will flag profiles which contain fewer than `threshold` number + /// of observations + oops::RequiredParameter threshold{"threshold", this, {oops::minConstraint(0)}}; +}; + +/// ProfileFewObsCheck: Check the number of observations in a profile +/// +/// See the cc file for more details. + +class ProfileFewObsCheck : public FilterBase, + private util::ObjectCounter { + public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef ProfileFewObsCheckParameters Parameters_; + + static const std::string classname() {return "ufo::ProfileFewObsCheck";} + + ProfileFewObsCheck(ioda::ObsSpace &, const Parameters_ &, + std::shared_ptr >, + std::shared_ptr >); + ~ProfileFewObsCheck(); + + private: + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override {return QCflags::profile;} + Parameters_ parameters_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_PROFILEFEWOBSCHECK_H_ diff --git a/src/ufo/filters/QCflags.h b/src/ufo/filters/QCflags.h index 86d6156a6..1065a1940 100644 --- a/src/ufo/filters/QCflags.h +++ b/src/ufo/filters/QCflags.h @@ -11,22 +11,31 @@ namespace ufo { namespace QCflags { - constexpr int pass = 0; // we like that one! - constexpr int missing = 1; // missing values prevent use of observation - constexpr int preQC = 2; // observation rejected by pre-processing - constexpr int bounds = 3; // observation value out of bounds - constexpr int domain = 4; // observation not within domain of use - constexpr int black = 5; // observation black listed - constexpr int Hfailed = 6; // H(x) computation failed - constexpr int thinned = 7; // observation removed due to thinning - constexpr int diffref = 8; // metadata too far from reference - constexpr int clw = 9; // observation removed due to cloud field - constexpr int fguess = 10; // observation too far from guess - constexpr int seaice = 11; // observation based sea ice detection, also flags land points - constexpr int track = 12; // observation removed as inconsistent with the rest of track - constexpr int buddy = 13; // observation rejected by the buddy check - constexpr int derivative = 14; // observation removed due to metadata derivative value - constexpr int profile = 15; // observation rejected by at least one profile QC check + constexpr int pass = 0; // we like that one! + constexpr int passive = 1; // H(x) is computed (for monitoring, BC...) but obs not assimilated +// Single digit values reserved for DA use. +// For now only 0, 1 and >1 are used but keeping space for other potential use cases. + +// Actual rejection flags + constexpr int missing = 10; // missing values prevent use of observation + constexpr int preQC = 11; // observation rejected by pre-processing + constexpr int bounds = 12; // observation value out of bounds + constexpr int domain = 13; // observation not within domain of use + constexpr int black = 14; // observation black listed + constexpr int Hfailed = 15; // H(x) computation failed + constexpr int thinned = 16; // observation removed due to thinning + constexpr int diffref = 17; // metadata too far from reference + constexpr int clw = 18; // observation removed due to cloud field + constexpr int fguess = 19; // observation too far from guess + constexpr int seaice = 20; // observation based sea ice detection, also flags land points + constexpr int track = 21; // observation removed as inconsistent with the rest of track + constexpr int buddy = 22; // observation rejected by the buddy check + constexpr int derivative = 23; // observation removed due to metadata derivative value + constexpr int profile = 24; // observation rejected by at least one profile QC check + constexpr int onedvar = 25; // observation failed to converge in 1dvar check + constexpr int bayesianQC = 26; // observation failed due to Bayesian background check + constexpr int modelobthresh = 27; // observation failed modelob threshold check + constexpr int history = 28; // observation failed when compared with historical data }; // namespace QCflags } // namespace ufo diff --git a/src/ufo/filters/QCmanager.cc b/src/ufo/filters/QCmanager.cc index 62f017512..0e72412c8 100644 --- a/src/ufo/filters/QCmanager.cc +++ b/src/ufo/filters/QCmanager.cc @@ -7,11 +7,14 @@ #include "ufo/filters/QCmanager.h" +#include #include +#include #include #include "eckit/config/Configuration.h" +#include "ioda/distribution/Accumulator.h" #include "ioda/ObsDataVector.h" #include "ioda/ObsSpace.h" #include "ioda/ObsVector.h" @@ -90,91 +93,69 @@ QCmanager::~QCmanager() { // ----------------------------------------------------------------------------- void QCmanager::print(std::ostream & os) const { - for (size_t jj = 0; jj < observed_.size(); ++jj) { - size_t iobs = obsdb_.nlocs(); - size_t ipass = 0; - size_t imiss = 0; - size_t ipreq = 0; - size_t ibnds = 0; - size_t iwhit = 0; - size_t iblck = 0; - size_t iherr = 0; - size_t ifgss = 0; - size_t ignss = 0; - size_t ithin = 0; - size_t idydx = 0; - size_t iclw = 0; - size_t iprof = 0; - size_t idiffref = 0; - size_t iseaice = 0; - size_t itrack = 0; - size_t ibuddy = 0; - - for (size_t jobs = 0; jobs < iobs; ++jobs) { - if ((*flags_)[jj][jobs] == QCflags::pass) ++ipass; - if ((*flags_)[jj][jobs] == QCflags::missing) ++imiss; - if ((*flags_)[jj][jobs] == QCflags::preQC) ++ipreq; - if ((*flags_)[jj][jobs] == QCflags::bounds) ++ibnds; - if ((*flags_)[jj][jobs] == QCflags::domain) ++iwhit; - if ((*flags_)[jj][jobs] == QCflags::black) ++iblck; - if ((*flags_)[jj][jobs] == QCflags::Hfailed) ++iherr; - if ((*flags_)[jj][jobs] == QCflags::fguess) ++ifgss; - if ((*flags_)[jj][jobs] == QCflags::thinned) ++ithin; - if ((*flags_)[jj][jobs] == QCflags::clw) ++iclw; - if ((*flags_)[jj][jobs] == QCflags::profile) ++iprof; - if ((*flags_)[jj][jobs] == QCflags::diffref) ++idiffref; - if ((*flags_)[jj][jobs] == QCflags::seaice) ++iseaice; - if ((*flags_)[jj][jobs] == 76 || (*flags_)[jj][jobs] == 77) ++ignss; - if ((*flags_)[jj][jobs] == QCflags::track) ++itrack; - if ((*flags_)[jj][jobs] == QCflags::buddy) ++ibuddy; - if ((*flags_)[jj][jobs] == QCflags::derivative) ++idydx; - } - - if (obsdb_.isDistributed()) { - obsdb_.comm().allReduceInPlace(iobs, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(ipass, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(imiss, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(ipreq, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(ibnds, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(iwhit, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(iblck, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(iherr, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(ifgss, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(iclw, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(iprof, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(ignss, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(ithin, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(idiffref, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(iseaice, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(itrack, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(ibuddy, eckit::mpi::sum()); - obsdb_.comm().allReduceInPlace(idydx, eckit::mpi::sum()); + const std::vector> cases{ + // Special cases reported using dedicated code + {QCflags::pass, nullptr}, + {76, nullptr}, // } The numbers of observations with these two flags + {77, nullptr}, // } will be added up and reported together + + // "Normal" cases reported in a uniform way + {QCflags::missing, "missing values"}, + {QCflags::preQC, "rejected by pre QC"}, + {QCflags::bounds, "out of bounds"}, + {QCflags::domain, "out of domain of use"}, + {QCflags::black, "black-listed"}, + {QCflags::Hfailed, "H(x) failed"}, + {QCflags::thinned, "removed by thinning"}, + {QCflags::derivative, "dy/dx out of valid range"}, + {QCflags::clw, "removed by cloud liquid water check"}, + {QCflags::profile, "removed by profile consistency check"}, + {QCflags::fguess, "rejected by first-guess check"}, + {QCflags::diffref, "rejected by difference check"}, + {QCflags::seaice, "removed by sea ice check"}, + {QCflags::track, "removed by track check"}, + {QCflags::buddy, "removed by buddy check"}, + {QCflags::onedvar, "removed by 1D Var check"}, + {QCflags::bayesianQC, "removed by Bayesian background check"}, + {QCflags::modelobthresh, "removed by ModelOb threshold"} + }; + const size_t numSpecialCases = 3; + + const size_t nlocs = obsdb_.nlocs(); + const size_t gnlocs = obsdb_.globalNumLocs(); + + for (size_t jvar = 0; jvar < observed_.size(); ++jvar) { + std::unique_ptr>> accumulator = + obsdb_.distribution()->createAccumulator(cases.size()); + + for (size_t jobs = 0; jobs < nlocs; ++jobs) { + const int actualFlag = (*flags_)[jvar][jobs]; + for (size_t jcase = 0; jcase < cases.size(); ++jcase) + if (actualFlag == cases[jcase].first) + accumulator->addTerm(jobs, jcase, 1); } + const std::vector counts = accumulator->computeResult(); if (obsdb_.comm().rank() == 0) { - const std::string info = "QC " + flags_->obstype() + " " + observed_[jj] + ": "; - if (imiss > 0) os << info << imiss << " missing values." << std::endl; - if (ipreq > 0) os << info << ipreq << " rejected by pre QC." << std::endl; - if (ibnds > 0) os << info << ibnds << " out of bounds." << std::endl; - if (iwhit > 0) os << info << iwhit << " out of domain of use." << std::endl; - if (iblck > 0) os << info << iblck << " black-listed." << std::endl; - if (iherr > 0) os << info << iherr << " H(x) failed." << std::endl; - if (ithin > 0) os << info << ithin << " removed by thinning." << std::endl; - if (idydx > 0) os << info << idydx << " dy/dx out of valid range." << std::endl; - if (iclw > 0) os << info << iclw << " removed by cloud liquid water check." << std::endl; - if (iprof > 0) os << info << iprof << " removed by profile consistency check." << std::endl; - if (ifgss > 0) os << info << ifgss << " rejected by first-guess check." << std::endl; - if (ignss > 0) os << info << ignss << " rejected by GNSSRO reality check." << std::endl; - if (idiffref > 0) os << info << idiffref << " rejected by difference check." << std::endl; - if (iseaice > 0) os << info << iseaice << " removed by sea ice check." << std::endl; - if (itrack > 0) os << info << itrack << " removed by track check." << std::endl; - if (ibuddy > 0) os << info << ibuddy << " removed by buddy check." << std::endl; - - os << info << ipass << " passed out of " << iobs << " observations." << std::endl; + const std::string info = "QC " + flags_->obstype() + " " + observed_[jvar] + ": "; + + // Normal cases + for (size_t i = numSpecialCases; i < counts.size(); ++i) + if (counts[i] > 0) + os << info << counts[i] << " " << cases[i].second << "." << std::endl; + + // Special cases: the GNSSRO check... + const size_t nGNSSRO = counts[1] + counts[2]; + if (nGNSSRO > 0) + os << info << nGNSSRO << " rejected by GNSSRO reality check." << std::endl; + + // ... the number of passed observations and the total number of observations. + const size_t npass = counts[0]; + os << info << npass << " passed out of " << gnlocs << " observations." << std::endl; } - ASSERT(ipass + imiss + ipreq + ibnds + iwhit + iblck + iherr + ithin + iclw + iprof + ifgss + \ - ignss + idiffref + iseaice + itrack + ibuddy + idydx == iobs); + const size_t numRecognizedFlags = std::accumulate(counts.begin(), counts.end(), 0); + ASSERT(numRecognizedFlags == gnlocs); } } diff --git a/src/ufo/filters/SatName.cc b/src/ufo/filters/SatName.cc new file mode 100644 index 000000000..d106849c0 --- /dev/null +++ b/src/ufo/filters/SatName.cc @@ -0,0 +1,127 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ +//------------------------------------------------------------------------------- +// +// This filter is the first step in the AMV processing. +// It creates a string for classifying Atmospheric Motion Vectors (AMV)'s into groups. +// This is then used for later AMV processing modules +// +// Inputs: +// data from an AMV WMO bufr file in netcdf4 format +// +// Required : +// +// "MetaData", "sensor_central_frequency", cfreq +// "MetaData", "originating_subcentre", orsub +// "MetaData", "satellite_identifier", satid +// "MetaData", "wind_computation_method", compm +// +// Outputs: +// "MetaData", "satwind_id", wind_id +//------------------------------------------------------------------------------- +// +// Method: +// +// There are a number of satellite series (both geostationary and polar) that currently provide AMVs +// Each satellite or satellite pairs, provides AMVs using difference channels in the visible +// infrared and water vapour bands. +// +// +// satellite frequency and computational method are used to generate the AMV character string, +// an example is: +// "NOAA16" and "ir108" are combined "NOAA16ir108" and added to the output NETCDF4 file +// as a variable satwind_id +// +// +// satids channel values +// +// hrvis vis06 vis08 +// ir16 ir38 ir87 ir97 ir108 ir120 +// wv62 wv67 wv73 +// cswv62 cswv67 cswv73 +// mixwv62 mixwv67 mixwv73 +// misstyp unknown satellite identified +// +// +#include "ufo/filters/SatName.h" +#include +#include +#include +#include +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/QCflags.h" +#include "ufo/utils/StringUtils.h" +namespace ufo { +std::string Sat_Characteristics(int SatID, float centralFrequency, int satobchannel, + const std::vector &SatIDRanges) { + std::string Missing_Sat_windChan = "misstyp"; + for (const SatIDRangeParameters &SatIDRange : SatIDRanges) + if (SatIDRange.minSatID <= SatID && SatID <= SatIDRange.maxSatID) + for (const FrequencyBandParameters &frequencyBand : SatIDRange.Satellite_comp.value()) + if (frequencyBand.minFrequency <= centralFrequency && + centralFrequency <= frequencyBand.maxFrequency && + (frequencyBand.satobchannel.value() == boost::none || + satobchannel == *frequencyBand.satobchannel.value())) { + return frequencyBand.windChannel; + } + return Missing_Sat_windChan; +} +std::string sat_id(int SatID, + const std::vector &SatIDRanges) { + std::string satmn = "misatid"; + for (const SatIDRangeParameters &SatIDRange : SatIDRanges) + for (const SatnameParameters &SatNames : SatIDRange.Satellite_id.value()) + if (SatID == SatNames.Satnumber.value()) {return SatNames.Satname;} + return satmn; +} +// ----------------------------------------------------------------------------- +SatName::SatName(ioda::ObsSpace & obsdb, const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) +{ + oops::Log::debug() << "SatName: config (constructor) = " << parameters_ << std::endl; +} +// ----------------------------------------------------------------------------- +SatName::~SatName() {} +// ----------------------------------------------------------------------------- +void SatName::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const { + std::vector cfreq(obsdb_.nlocs()); + std::vector orsub(obsdb_.nlocs()); + std::vector orgin(obsdb_.nlocs()); + std::vector satid(obsdb_.nlocs()); + std::vector compm(obsdb_.nlocs()); + std::vector wind_id(obsdb_.nlocs()); + obsdb_.get_db("MetaData", "sensor_central_frequency", cfreq); + obsdb_.get_db("MetaData", "originating_subcentre", orsub); + obsdb_.get_db("MetaData", "originating_centre", orgin); + obsdb_.get_db("MetaData", "satellite_identifier", satid); + obsdb_.get_db("MetaData", "wind_computation_method", compm); +// + for (size_t jobs = 0; jobs < obsdb_.nlocs(); ++jobs) { + wind_id[jobs] = sat_id(satid[jobs], parameters_.SatNameAssignments.value()) + + Sat_Characteristics(satid[jobs], cfreq[jobs], compm[jobs], + parameters_.SatNameAssignments.value()); + } + obsdb_.put_db("MetaData", "satwind_id", wind_id); +// only print the first 10 observations while testing + for (size_t ii = 0; ii < 10 ; ++ii) { + oops::Log::trace() << " freq " << cfreq[ii] << "orsub " << orsub[ii] << "orgin " + << orgin[ii] << " satid " << satid[ii] << " compm " << compm[ii] + << " wind id "<< wind_id[ii] << std::endl;} +} +// ----------------------------------------------------------------------------- +void SatName::print(std::ostream & os) const { + os << "SatName: config = " << parameters_ << std::endl; +} +// ----------------------------------------------------------------------------- +} // namespace ufo diff --git a/src/ufo/filters/SatName.h b/src/ufo/filters/SatName.h new file mode 100644 index 000000000..af9fb2652 --- /dev/null +++ b/src/ufo/filters/SatName.h @@ -0,0 +1,90 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ +#ifndef UFO_FILTERS_SATNAME_H_ +#define UFO_FILTERS_SATNAME_H_ + +#include +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/QCflags.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" +namespace eckit { + class Configuration; +} +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} +namespace ufo { +// +// table in yaml file to relate satellite name and wmo number label in yaml "Satellite_id" +// +class SatnameParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(SatnameParameters, Parameters) + public: + oops::RequiredParameter Satname{"Sat name", this}; + oops::RequiredParameter Satnumber{"Sat ID", this}; +}; +// +// various satillite instrument characteristics label in yaml "Satellite_comp" +// +class FrequencyBandParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(FrequencyBandParameters, Parameters) + public: + oops::RequiredParameter minFrequency{"min frequency", this}; + oops::RequiredParameter maxFrequency{"max frequency", this}; + oops::OptionalParameter satobchannel{"satobchannel", this}; + oops::RequiredParameter windChannel{"wind channel", this}; +}; +// +// range of satellite wmo numbers "min obs type" and "max obs type" +// +class SatIDRangeParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(SatIDRangeParameters, Parameters) + public: + oops::RequiredParameter minSatID{"min WMO Satellite id", this}; + oops::RequiredParameter maxSatID{"max WMO Satellite id", this}; + oops::Parameter> Satellite_comp{ + "Satellite_comp", {}, this}; + oops::Parameter> Satellite_id{ + "Satellite_id", {}, this}; +}; +// Parameters controlling the operation of the SatName filter. +class SatNameParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(SatNameParameters, FilterParametersBase) + + public: + oops::Parameter> + SatNameAssignments{ "SatName assignments", {}, this};}; +class SatName : public FilterBase, + private util::ObjectCounter { + public: +// The type of parameters accepted by the constructor of this filter. +// This typedef is used by the FilterFactory. + typedef SatNameParameters Parameters_; + static const std::string classname() {return "ufo::SatName";} + SatName(ioda::ObsSpace &, const Parameters_ &, + std::shared_ptr >, + std::shared_ptr >); + ~SatName(); + private: + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override {return QCflags::pass;} + Parameters_ parameters_; +}; +} // namespace ufo +#endif // UFO_FILTERS_SATNAME_H_ diff --git a/src/ufo/filters/StuckCheck.cc b/src/ufo/filters/StuckCheck.cc new file mode 100644 index 000000000..b00c52d1c --- /dev/null +++ b/src/ufo/filters/StuckCheck.cc @@ -0,0 +1,186 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/StuckCheck.h" + +#include +#include +#include +#include +#include + +#include +#include "eckit/config/Configuration.h" +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" +#include "oops/base/Variables.h" +#include "oops/util/DateTime.h" +#include "oops/util/Duration.h" +#include "oops/util/Logger.h" +#include "ufo/filters/ObsAccessor.h" +#include "ufo/filters/StuckCheckParameters.h" +#include "ufo/filters/TrackCheckUtils.h" +#include "ufo/utils/RecursiveSplitter.h" + +namespace ufo { + +StuckCheck::StuckCheck(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), options_(parameters) +{ + obsGroupDateTimes_.reset(new std::vector); + oops::Log::debug() << "StuckCheck: config = " << options_ << '\n'; +} + +// Required for the correct destruction of ObsGroupDateTimes_. +StuckCheck::~StuckCheck() +{} + +/// The filter removes observations if they are part of a 'streak'. A streak is where the number +/// of identical observation values in sequence (for a given variable) is greater than a user +/// defined count. +/// To be a streak, it must also continue for longer than a user-defined duration or every +/// observation in the station's group must have an identical value. +void StuckCheck::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const { + ObsAccessor obsAccessor = TrackCheckUtils::createObsAccessor(options_.stationIdVariable, obsdb_); + const std::vector validObsIds = obsAccessor.getValidObservationIds(apply); + *obsGroupDateTimes_ = obsAccessor.getDateTimeVariableFromObsSpace( + "MetaData", "datetime"); + // Create groups based on record number (assumed station ID) or category variable + // (stationIdVariable) or otherwise assume observations all taken by the same station (1 group) + RecursiveSplitter splitter = obsAccessor.splitObservationsIntoIndependentGroups(validObsIds); + TrackCheckUtils::sortTracksChronologically(validObsIds, obsAccessor, splitter); + std::vector isRejected(validObsIds.size(), false); + std::vector filterVariables = filtervars.toOopsVariables().variables(); + // Iterates through observations to see how long each variable is stuck on one observation + for (std::string const& variable : filterVariables) { + size_t stationNumber = 0; + if (!obsdb_.has("ObsValue", variable)) { + std::string errorMessage = + "StuckCheck Error: ObsValue vector for " + variable + " not found.\n"; + throw std::invalid_argument(errorMessage); + } + const std::vector variableValues = obsAccessor.getFloatVariableFromObsSpace( + "ObsValue", variable); + for (auto station : splitter.multiElementGroups()) { + std::string stationId = std::to_string(stationNumber); + std::vector variableDataStation = collectStationVariableData( + station.begin(), station.end(), validObsIds, variableValues); + // the working variable's value associated with the prior observation + float previousObservationValue; + float currentObservationValue; + size_t firstSameValueIndex = 0; // the first observation in the current streak + for (size_t observationIndex = 0; observationIndex < variableDataStation.size(); + observationIndex++) { + currentObservationValue = variableDataStation.at(observationIndex); + if (observationIndex == 0) { + previousObservationValue = currentObservationValue; + } else { + if (currentObservationValue == previousObservationValue) { + // If the last observation of the track is part of a streak, the full streak will need + // to be checked at this point. + if (observationIndex == variableDataStation.size() - 1) { + StuckCheck::potentiallyRejectStreak(station.begin(), + station.end(), + validObsIds, + firstSameValueIndex, + observationIndex, + isRejected, + stationId); + } + } else { // streak ended in the previous observation + StuckCheck::potentiallyRejectStreak(station.begin(), + station.end(), + validObsIds, + firstSameValueIndex, + observationIndex - 1, + isRejected, + stationId); + // start the streak with the current observation and reset the count to 1 + firstSameValueIndex = observationIndex; + previousObservationValue = currentObservationValue; + } + } + } + stationNumber++; + } + } + obsAccessor.flagRejectedObservations(isRejected, flagged); +} + +void StuckCheck::print(std::ostream & os) const { + os << "StuckCheck: config = " << config_ << '\n'; +} + +/// \returns a vector containing all of the necessary data to run this filter for each observation, +/// stored by observation. +std::vector StuckCheck::collectStationVariableData( + std::vector::const_iterator stationObsIndicesBegin, + std::vector::const_iterator stationObsIndicesEnd, + const std::vector &validObsIds, + const std::vector &globalData) const { + std::vector stationData; + stationData.reserve(stationObsIndicesEnd - stationObsIndicesBegin); + size_t observationNumber = 0; + for (std::vector::const_iterator it = stationObsIndicesBegin; + it != stationObsIndicesEnd; ++it) { + const size_t obsId = validObsIds.at(*it); + stationData.push_back(globalData[obsId]); + observationNumber++; + } + return stationData; +} + +void StuckCheck::potentiallyRejectStreak( + std::vector::const_iterator stationIndicesBegin, + std::vector::const_iterator stationIndicesEnd, + const std::vector &validObsIds, + size_t startOfStreakIndex, + size_t endOfStreakIndex, + std::vector &isRejected, + std::string stationId = "") const { + + auto getObservationTime = [this, &stationIndicesBegin, &validObsIds] ( + size_t offsetFromBeginning)->util::DateTime{ + const size_t obsIndex = validObsIds.at(*(stationIndicesBegin + offsetFromBeginning)); + return obsGroupDateTimes_->at(obsIndex); + }; + + auto rejectObservation = [&validObsIds, &isRejected, &stationIndicesBegin, &stationId]( + size_t observationIndex) { + const size_t obsIndex = validObsIds.at(*(stationIndicesBegin + observationIndex)); + isRejected[obsIndex] = true; + oops::Log::trace() << "StuckCheck: Observation " << observationIndex << + " rejected from station " << stationId << std::endl; + }; + + size_t streakLength = endOfStreakIndex - startOfStreakIndex + 1; + if (streakLength <= options_.core.numberStuckTolerance) { + return; + } + + size_t stationLength = stationIndicesEnd - stationIndicesBegin; + + if (streakLength < stationLength) { + util::DateTime firstStreakObservationTime = getObservationTime(startOfStreakIndex); + util::DateTime lastStreakObservationTime = getObservationTime(endOfStreakIndex); + util::Duration streakDuration = lastStreakObservationTime - firstStreakObservationTime; + if (streakDuration <= options_.core.timeStuckTolerance) { + return; + } + } + // reject all observations within streak + for (size_t indexToReject = startOfStreakIndex; + indexToReject <= endOfStreakIndex; + indexToReject++) + rejectObservation(indexToReject); +} + +} // namespace ufo diff --git a/src/ufo/filters/StuckCheck.h b/src/ufo/filters/StuckCheck.h new file mode 100644 index 000000000..ab195cce5 --- /dev/null +++ b/src/ufo/filters/StuckCheck.h @@ -0,0 +1,85 @@ + +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_STUCKCHECK_H_ +#define UFO_FILTERS_STUCKCHECK_H_ + +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" +#include "oops/util/DateTime.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/ObsAccessor.h" +#include "ufo/filters/QCflags.h" +#include "ufo/filters/StuckCheckParameters.h" +#include "ufo/filters/TrackCheckUtils.h" + +namespace eckit { +class Configuration; +} + +namespace ioda { +template class ObsDataVector; +class ObsSpace; +} + +namespace ufo { + +/// Flags sequential observations whose filter variables have streaks of unchanging +/// measurements over a time span such that the following two conditions are satisfied: +/// 1. The streak exceeds a user-specified number of observations (\p numberStuckTolerance) +/// 2. The streak continues for longer than a user-specified duration (\p timeStuckTolerance) +/// and/or the streak continues throughout every one of the observations within the observation +/// grouping. +/// +/// Types of observations that this check might apply to include the following: +/// LNDSYN, LNDSYB, SHPSYN, SHPSYB, BUOY, MOBSYN, and OPENROAD +/// +class StuckCheck: public FilterBase, + private util::ObjectCounter { + public: + typedef StuckCheckParameters Parameters_; + + static const std::string classname() {return "ufo::StuckCheck";} + + StuckCheck(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, + std::shared_ptr > flags, + std::shared_ptr > obserr); + + ~StuckCheck() override; + + private: + Parameters_ options_; + // Instantiate object for accessing observations that may be held on multiple MPI ranks. + std::unique_ptr> obsGroupDateTimes_; + + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override {return QCflags::track;} + std::vector collectStationVariableData( + std::vector::const_iterator stationObsIndicesBegin, + std::vector::const_iterator stationObsIndicesEnd, + const std::vector &validObsIds, + const std::vector &globalData) const; + void potentiallyRejectStreak(std::vector::const_iterator stationIndicesBegin, + std::vector::const_iterator stationIndicesEnd, + const std::vector &validObsIds, + size_t startOfStreakIndex, + size_t endOfStreakIndex, + std::vector &isRejected, + std::string stationId) const; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_STUCKCHECK_H_ diff --git a/src/ufo/filters/StuckCheckParameters.h b/src/ufo/filters/StuckCheckParameters.h new file mode 100644 index 000000000..3a5e1edd6 --- /dev/null +++ b/src/ufo/filters/StuckCheckParameters.h @@ -0,0 +1,45 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_STUCKCHECKPARAMETERS_H_ +#define UFO_FILTERS_STUCKCHECKPARAMETERS_H_ + +#include "oops/util/Duration.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "ufo/filters/TrackCheckUtilsParameters.h" + +namespace ufo { +class StuckCheckCoreParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(StuckCheckCoreParameters, Parameters) + + public: + /// The maximum number of observations in a row that can have the same observed value + /// before the observations may be flagged by this filter. + oops::RequiredParameter numberStuckTolerance { + "number stuck tolerance", this + }; + + /// The maximum duration in which an observation value is "stuck" before + /// the observations may be flagged (unless all of the observations have that one value, + /// in which case the observations could be flagged anyway) + oops::RequiredParameter timeStuckTolerance { + "time stuck tolerance", this + }; +}; + +/// \brief Options controlling the operation of stuck check filter. +class StuckCheckParameters : public TrackCheckUtilsParameters { + OOPS_CONCRETE_PARAMETERS(StuckCheckParameters, TrackCheckUtilsParameters) + public: + StuckCheckCoreParameters core{this}; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_STUCKCHECKPARAMETERS_H_ diff --git a/src/ufo/filters/TemporalThinning.cc b/src/ufo/filters/TemporalThinning.cc index d5f2e0e22..84c0dcd11 100644 --- a/src/ufo/filters/TemporalThinning.cc +++ b/src/ufo/filters/TemporalThinning.cc @@ -21,6 +21,7 @@ #include "oops/util/DateTime.h" #include "oops/util/Duration.h" #include "oops/util/Logger.h" +#include "ufo/filters/ObsAccessor.h" #include "ufo/filters/TemporalThinningParameters.h" #include "ufo/utils/RecursiveSplitter.h" @@ -302,15 +303,12 @@ typename TemporalThinner::ForwardValidObsIndexIterator TemporalThinner::findNear } // namespace -TemporalThinning::TemporalThinning(ioda::ObsSpace & obsdb, const eckit::Configuration & config, +TemporalThinning::TemporalThinning(ioda::ObsSpace & obsdb, const Parameters_ & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr) + : FilterBase(obsdb, parameters, flags, obserr), options_(parameters) { - oops::Log::debug() << "TemporalThinning: config = " << config_ << std::endl; - - options_.reset(new TemporalThinningParameters()); - options_->deserialize(config); + oops::Log::debug() << "TemporalThinning: config = " << options_ << std::endl; } // Required for the correct destruction of options_. @@ -320,81 +318,61 @@ TemporalThinning::~TemporalThinning() void TemporalThinning::applyFilter(const std::vector & apply, const Variables & filtervars, std::vector> & flagged) const { - const std::vector isThinned = identifyThinnedObservations(apply); + ObsAccessor obsAccessor = createObsAccessor(); + + const std::vector isThinned = identifyThinnedObservations(apply, obsAccessor); - flagThinnedObservations(isThinned, flagged); + obsAccessor.flagRejectedObservations(isThinned, flagged); if (filtervars.size() != 0) { oops::Log::trace() << "TemporalThinning: flagged? = " << flagged[0] << std::endl; } } +ObsAccessor TemporalThinning::createObsAccessor() const { + if (options_.categoryVariable.value() != boost::none) { + return ObsAccessor::toObservationsSplitIntoIndependentGroupsByVariable( + obsdb_, *options_.categoryVariable.value() ); + } else if (!obsdb_.obs_group_vars().empty()) { + // Records exist. Thin each record separately. + return ObsAccessor::toObservationsSplitIntoIndependentGroupsByRecordId(obsdb_); + } else { + // Records don't exist. Thin all observations together. + return ObsAccessor::toAllObservations(obsdb_); + } +} + std::vector TemporalThinning::identifyThinnedObservations( - const std::vector & apply) const { - std::vector validObsIds = getValidObservationIds(apply); + const std::vector & apply, + const ObsAccessor &obsAccessor) const { + const std::vector validObsIds = obsAccessor.getValidObservationIds(apply, *flags_); - RecursiveSplitter splitter(validObsIds.size()); - groupObservationsByCategory(validObsIds, splitter); + RecursiveSplitter splitter = obsAccessor.splitObservationsIntoIndependentGroups(validObsIds); - std::vector times(obsdb_.nlocs()); - obsdb_.get_db("MetaData", "datetime", times); + std::vector times = obsAccessor.getDateTimeVariableFromObsSpace( + "MetaData", "datetime"); splitter.sortGroupsBy([×, &validObsIds](size_t obsIndexA, size_t obsIndexB) { return times[validObsIds[obsIndexA]] < times[validObsIds[obsIndexB]]; }); - std::unique_ptr> prioritiesDataVector = getObservationPriorities(); - const ioda::ObsDataRow *priorities = - prioritiesDataVector ? &(*prioritiesDataVector)[0] : nullptr; - - TemporalThinner thinner(validObsIds, times, priorities, splitter, *options_); - return thinner.identifyThinnedObservations(apply.size()); -} + boost::optional> priorities = getObservationPriorities(obsAccessor); -std::vector TemporalThinning::getValidObservationIds( - const std::vector & apply) const { - std::vector validObsIds; - for (size_t obsId = 0; obsId < apply.size(); ++obsId) - if (apply[obsId] && (*flags_)[0][obsId] == QCflags::pass) - validObsIds.push_back(obsId); - return validObsIds; + TemporalThinner thinner(validObsIds, times, priorities.get_ptr(), splitter, options_); + return thinner.identifyThinnedObservations(times.size()); } -void TemporalThinning::groupObservationsByCategory(const std::vector &validObsIds, - RecursiveSplitter &splitter) const { - boost::optional categoryVariable = options_->categoryVariable; - if (categoryVariable == boost::none) - return; - - ioda::ObsDataVector obsDataVector(obsdb_, categoryVariable.get().variable(), - categoryVariable.get().group()); - ioda::ObsDataRow &category = obsDataVector[0]; - - std::vector validObsCategories(validObsIds.size()); - for (size_t validObsIndex = 0; validObsIndex < validObsIds.size(); ++validObsIndex) - validObsCategories[validObsIndex] = category[validObsIds[validObsIndex]]; - splitter.groupBy(validObsCategories); -} - -std::unique_ptr> TemporalThinning::getObservationPriorities() const { - std::unique_ptr> priorities; - if (options_->priorityVariable.value() != boost::none) { - const ufo::Variable priorityVariable = options_->priorityVariable.value().get(); - priorities.reset(new ioda::ObsDataVector( - obsdb_, priorityVariable.variable(), priorityVariable.group())); +boost::optional> TemporalThinning::getObservationPriorities( + const ObsAccessor &obsAccessor) const { + boost::optional> priorities; + if (options_.priorityVariable.value() != boost::none) { + const ufo::Variable priorityVariable = options_.priorityVariable.value().get(); + priorities = obsAccessor.getIntVariableFromObsSpace(priorityVariable.group(), + priorityVariable.variable()); } return priorities; } -void TemporalThinning::flagThinnedObservations( - const std::vector & isThinned, - std::vector> & flagged) const { - for (std::vector & variableFlagged : flagged) - for (size_t obsId = 0; obsId < isThinned.size(); ++obsId) - if (isThinned[obsId]) - variableFlagged[obsId] = true; -} - void TemporalThinning::print(std::ostream & os) const { - os << "TemporalThinning: config = " << config_ << std::endl; + os << "TemporalThinning: config = " << options_ << std::endl; } } // namespace ufo diff --git a/src/ufo/filters/TemporalThinning.h b/src/ufo/filters/TemporalThinning.h index ffac73ad7..4d8560758 100644 --- a/src/ufo/filters/TemporalThinning.h +++ b/src/ufo/filters/TemporalThinning.h @@ -19,6 +19,7 @@ #include "oops/util/ObjectCounter.h" #include "ufo/filters/FilterBase.h" #include "ufo/filters/QCflags.h" +#include "ufo/filters/TemporalThinningParameters.h" namespace eckit { class Configuration; @@ -35,8 +36,8 @@ namespace util { namespace ufo { +class ObsAccessor; class RecursiveSplitter; -class TemporalThinningParameters; /// \brief Thin observations so that the retained ones are sufficiently separated in time. /// @@ -44,9 +45,11 @@ class TemporalThinningParameters; class TemporalThinning : public FilterBase, private util::ObjectCounter { public: + typedef TemporalThinningParameters Parameters_; + static const std::string classname() {return "ufo::TemporalThinning";} - TemporalThinning(ioda::ObsSpace &obsdb, const eckit::Configuration &config, + TemporalThinning(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, std::shared_ptr > flags, std::shared_ptr > obserr); @@ -58,20 +61,16 @@ class TemporalThinning : public FilterBase, std::vector> &) const override; int qcFlag() const override { return QCflags::thinned; } - std::vector identifyThinnedObservations(const std::vector &apply) const; - - std::vector getValidObservationIds(const std::vector &apply) const; - - std::unique_ptr> getObservationPriorities() const; + ObsAccessor createObsAccessor() const; - void groupObservationsByCategory(const std::vector &validObsIds, - RecursiveSplitter &splitter) const; + std::vector identifyThinnedObservations(const std::vector &apply, + const ObsAccessor &obsAccessor) const; - void flagThinnedObservations(const std::vector &isThinned, - std::vector> &flagged) const; + boost::optional> getObservationPriorities( + const ObsAccessor &obsAccessor) const; private: - std::unique_ptr options_; + Parameters_ options_; }; } // namespace ufo diff --git a/src/ufo/filters/TemporalThinningParameters.h b/src/ufo/filters/TemporalThinningParameters.h index 2c675d20c..b9466665e 100644 --- a/src/ufo/filters/TemporalThinningParameters.h +++ b/src/ufo/filters/TemporalThinningParameters.h @@ -14,6 +14,7 @@ #include "oops/util/parameters/OptionalParameter.h" #include "oops/util/parameters/Parameter.h" #include "oops/util/parameters/Parameters.h" +#include "ufo/filters/FilterParametersBase.h" #include "ufo/utils/Constants.h" #include "ufo/utils/parameters/ParameterTraitsVariable.h" @@ -24,8 +25,8 @@ namespace eckit { namespace ufo { /// \brief Options controlling the operation of the TemporalThinning filter. -class TemporalThinningParameters : public oops::Parameters { - OOPS_CONCRETE_PARAMETERS(TemporalThinningParameters, Parameters) +class TemporalThinningParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(TemporalThinningParameters, FilterParametersBase) public: /// Minimum spacing between two successive retained observations. @@ -46,8 +47,15 @@ class TemporalThinningParameters : public oops::Parameters { /// observations in reverse chronological order. oops::OptionalParameter seedTime{"seed_time", this}; - /// Variable storing integer-valued IDs associated with observations. Observations belonging - /// to different categories are thinned separately. + /// A string- or integer-valued variable. Observations with different values of that variable will + /// be thinned separately. + /// + /// If not set and observations were grouped into records when the observation space was + /// constructed, observations from each record will be thinned separately. If not set and + /// observations were not grouped into records, all observations will be thinned together. + /// + /// Note: the variable used to group observations into records can be set with the + /// \c obs space.obsdatain.obsgrouping.group variable YAML option. oops::OptionalParameter categoryVariable{"category_variable", this}; /// Variable storing observation priorities. Used together with \c tolerance; see the diff --git a/src/ufo/filters/Thinning.cc b/src/ufo/filters/Thinning.cc index 5593e8ea4..992e2811b 100644 --- a/src/ufo/filters/Thinning.cc +++ b/src/ufo/filters/Thinning.cc @@ -22,12 +22,12 @@ namespace ufo { // ----------------------------------------------------------------------------- -Thinning::Thinning(ioda::ObsSpace & obsdb, const eckit::Configuration & config, +Thinning::Thinning(ioda::ObsSpace & obsdb, const Parameters_ & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr) + : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters) { - oops::Log::debug() << "Thinning: config = " << config_ << std::endl; + oops::Log::debug() << "Thinning: config = " << parameters_ << std::endl; } // ----------------------------------------------------------------------------- @@ -41,23 +41,22 @@ void Thinning::applyFilter(const std::vector & apply, std::vector> & flagged) const { // get local and global number of locations const size_t nlocs = obsdb_.nlocs(); - const size_t gnlocs = obsdb_.gnlocs(); + const size_t gnlocs = obsdb_.globalNumLocs(); // get global indices of the local locations const std::vector & gindex = obsdb_.index(); - const float thinning = config_.getFloat("amount"); + const float amount = parameters_.amount; // create random numbers for each observation based on some seed - unsigned int random_seed = config_.getInt("random seed", std::time(0)); - int mymember = config_.getInt("member", 0); - random_seed += mymember; + unsigned int random_seed = parameters_.randomSeed.value().value_or(std::time(0)); + random_seed += parameters_.member; util::UniformDistribution rand(gnlocs, 0.0, 1.0, random_seed); for (size_t jv = 0; jv < filtervars.nvars(); ++jv) { for (size_t jobs = 0; jobs < nlocs; ++jobs) { - if ( apply[jobs] && rand[gindex[jobs]] < thinning ) flagged[jv][jobs] = true; + if ( apply[jobs] && rand[gindex[jobs]] < amount ) flagged[jv][jobs] = true; } } } @@ -65,7 +64,7 @@ void Thinning::applyFilter(const std::vector & apply, // ----------------------------------------------------------------------------- void Thinning::print(std::ostream & os) const { - os << "Thinning: config = " << config_ << std::endl; + os << "Thinning: config = " << parameters_ << std::endl; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/Thinning.h b/src/ufo/filters/Thinning.h index 0796acd4d..9805ea1af 100644 --- a/src/ufo/filters/Thinning.h +++ b/src/ufo/filters/Thinning.h @@ -15,6 +15,9 @@ #include "ioda/ObsDataVector.h" #include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/RequiredParameter.h" #include "ufo/filters/FilterBase.h" #include "ufo/filters/QCflags.h" @@ -29,14 +32,36 @@ namespace ioda { namespace ufo { -/// Thinning: randomly thin a given percentage of observations +/// Parameters controlling the operation of the Thinning filter. +class ThinningParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(ThinningParameters, FilterParametersBase) + + public: + /// (Approximate) fraction of observations to be thinned. + oops::RequiredParameter amount{"amount", this}; + + /// Seed used to initialize the random number generator (if it has not been initialized + /// before). If not set, the seed is derived from the calendar time. + oops::OptionalParameter randomSeed{"random seed", this}; + + /// Index of the ensemble member. + oops::Parameter member{"member", 0, this}; +}; + +/// \brief Randomly thin a given percentage of observations. +/// +/// See ThinningParameters for the documentation of the parameters controlling this filter. class Thinning : public FilterBase, private util::ObjectCounter { public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef ThinningParameters Parameters_; + static const std::string classname() {return "ufo::Thinning";} - Thinning(ioda::ObsSpace &, const eckit::Configuration &, + Thinning(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr >, std::shared_ptr >); ~Thinning(); @@ -46,6 +71,8 @@ class Thinning : public FilterBase, void applyFilter(const std::vector &, const Variables &, std::vector> &) const override; int qcFlag() const override {return QCflags::thinned;} + + Parameters_ parameters_; }; } // namespace ufo diff --git a/src/ufo/filters/TrackCheck.cc b/src/ufo/filters/TrackCheck.cc index 1c54407f7..4611d3533 100644 --- a/src/ufo/filters/TrackCheck.cc +++ b/src/ufo/filters/TrackCheck.cc @@ -22,6 +22,7 @@ #include "oops/util/Duration.h" #include "oops/util/Logger.h" #include "oops/util/sqr.h" +#include "ufo/filters/ObsAccessor.h" #include "ufo/filters/TrackCheckParameters.h" #include "ufo/filters/TrackCheckUtils.h" #include "ufo/utils/Constants.h" @@ -93,15 +94,12 @@ float TrackCheck::TrackObservation::getFailedChecksFraction() { } -TrackCheck::TrackCheck(ioda::ObsSpace & obsdb, const eckit::Configuration & config, +TrackCheck::TrackCheck(ioda::ObsSpace & obsdb, const Parameters_ & parameters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr) + : FilterBase(obsdb, parameters, flags, obserr), options_(parameters) { - oops::Log::debug() << "TrackCheck: config = " << config_ << std::endl; - - options_.reset(new TrackCheckParameters()); - options_->deserialize(config); + oops::Log::debug() << "TrackCheck: config = " << options_ << std::endl; } // Required for the correct destruction of options_. @@ -111,38 +109,39 @@ TrackCheck::~TrackCheck() void TrackCheck::applyFilter(const std::vector & apply, const Variables & filtervars, std::vector> & flagged) const { - const std::vector validObsIds = TrackCheckUtils::getValidObservationIds(apply, flags_); + ObsAccessor obsAccessor = TrackCheckUtils::createObsAccessor(options_.stationIdVariable, obsdb_); + + const std::vector validObsIds = obsAccessor.getValidObservationIds(apply, *flags_); - RecursiveSplitter splitter(validObsIds.size()); - TrackCheckUtils::groupObservationsByStation(validObsIds, splitter, config_, obsdb_); - TrackCheckUtils::sortTracksChronologically(validObsIds, splitter, obsdb_); + RecursiveSplitter splitter = obsAccessor.splitObservationsIntoIndependentGroups(validObsIds); + TrackCheckUtils::sortTracksChronologically(validObsIds, obsAccessor, splitter); - ObsGroupPressureLocationTime obsPressureLoc = collectObsPressuresLocationsTimes(); + ObsGroupPressureLocationTime obsPressureLoc = collectObsPressuresLocationsTimes(obsAccessor); PiecewiseLinearInterpolation maxSpeedByPressure = makeMaxSpeedByPressureInterpolation(); - std::vector isRejected(apply.size(), false); + std::vector isRejected(obsPressureLoc.pressures.size(), false); for (auto track : splitter.multiElementGroups()) { identifyRejectedObservationsInTrack(track.begin(), track.end(), validObsIds, obsPressureLoc, maxSpeedByPressure, isRejected); } - TrackCheckUtils::flagRejectedObservations(isRejected, flagged); + obsAccessor.flagRejectedObservations(isRejected, flagged); if (filtervars.size() != 0) { oops::Log::trace() << "TrackCheck: flagged? = " << flagged[0] << std::endl; } } -TrackCheck::ObsGroupPressureLocationTime TrackCheck::collectObsPressuresLocationsTimes() const { +TrackCheck::ObsGroupPressureLocationTime TrackCheck::collectObsPressuresLocationsTimes( + const ObsAccessor &obsAccessor) const { ObsGroupPressureLocationTime obsPressureLoc; - obsPressureLoc.locationTimes = TrackCheckUtils::collectObservationsLocations(obsdb_); - obsPressureLoc.pressures.resize(obsdb_.nlocs()); - obsdb_.get_db("MetaData", "air_pressure", obsPressureLoc.pressures); + obsPressureLoc.locationTimes = TrackCheckUtils::collectObservationsLocations(obsAccessor); + obsPressureLoc.pressures = obsAccessor.getFloatVariableFromObsSpace("MetaData", "air_pressure"); return obsPressureLoc; } PiecewiseLinearInterpolation TrackCheck::makeMaxSpeedByPressureInterpolation() const { const std::map &maxSpeedInterpolationPoints = - options_->maxSpeedInterpolationPoints.value(); + options_.maxSpeedInterpolationPoints.value(); std::vector pressures, maxSpeeds; pressures.reserve(maxSpeedInterpolationPoints.size()); @@ -215,7 +214,7 @@ TrackCheckUtils::SweepResult TrackCheck::sweepOverObservations( const bool firstSweep = obs.numNeighborsVisitedInPreviousSweep(dir) == NO_PREVIOUS_SWEEP; const int numNeighborsVisitedInPreviousSweep = firstSweep ? 0 : obs.numNeighborsVisitedInPreviousSweep(dir); - int numNewDistinctBuddiesToVisit = firstSweep ? options_->numDistinctBuddiesPerDirection : 0; + int numNewDistinctBuddiesToVisit = firstSweep ? options_.numDistinctBuddiesPerDirection : 0; auto getNthNeighbor = [&trackObservations, obsIdx, dir](int n) -> const TrackObservation* { const int neighborObsIdx = obsIdx + (dir == FORWARD ? n : -n); @@ -236,7 +235,7 @@ TrackCheckUtils::SweepResult TrackCheck::sweepOverObservations( // need to "undo" checks against rejected observations. minPressureBetween = std::min(minPressureBetween, neighborObs->pressure()); if (neighborObs->rejectedInPreviousSweep()) { - CheckResults results = obs.checkAgainstBuddy(*neighborObs, *options_, + CheckResults results = obs.checkAgainstBuddy(*neighborObs, options_, maxValidSpeedAtPressure, minPressureBetween); obs.unregisterCheckResults(results); if (results.isBuddyDistinct) { @@ -250,7 +249,7 @@ TrackCheckUtils::SweepResult TrackCheck::sweepOverObservations( neighborObs = getNthNeighbor(++neighborIdx)) { minPressureBetween = std::min(minPressureBetween, neighborObs->pressure()); if (!neighborObs->rejected()) { - CheckResults results = obs.checkAgainstBuddy(*neighborObs, *options_, + CheckResults results = obs.checkAgainstBuddy(*neighborObs, options_, maxValidSpeedAtPressure, minPressureBetween); obs.registerCheckResults(results); if (results.isBuddyDistinct) @@ -268,7 +267,7 @@ TrackCheckUtils::SweepResult TrackCheck::sweepOverObservations( const float maxFailedChecksFraction = *std::max_element(failedChecksFraction.begin(), failedChecksFraction.end()); - const float failedChecksThreshold = options_->rejectionThreshold * maxFailedChecksFraction; + const float failedChecksThreshold = options_.rejectionThreshold * maxFailedChecksFraction; if (failedChecksThreshold <= 0) return TrackCheckUtils::SweepResult::NO_MORE_SWEEPS_REQUIRED; @@ -294,7 +293,7 @@ void TrackCheck::flagRejectedTrackObservations( } void TrackCheck::print(std::ostream & os) const { - os << "TrackCheck: config = " << config_ << std::endl; + os << "TrackCheck: config = " << options_ << std::endl; } } // namespace ufo diff --git a/src/ufo/filters/TrackCheck.h b/src/ufo/filters/TrackCheck.h index d08b3760f..e33f56b01 100644 --- a/src/ufo/filters/TrackCheck.h +++ b/src/ufo/filters/TrackCheck.h @@ -17,6 +17,7 @@ #include "oops/util/ObjectCounter.h" #include "ufo/filters/FilterBase.h" #include "ufo/filters/QCflags.h" +#include "ufo/filters/TrackCheckParameters.h" #include "ufo/filters/TrackCheckUtils.h" namespace eckit { @@ -30,7 +31,7 @@ class ObsSpace; namespace ufo { -class TrackCheckParameters; +class ObsAccessor; class PiecewiseLinearInterpolation; class RecursiveSplitter; @@ -73,15 +74,20 @@ class TrackCheck : public FilterBase, std::vector pressures; }; - ObsGroupPressureLocationTime collectObsPressuresLocationsTimes() const; + ObsGroupPressureLocationTime collectObsPressuresLocationsTimes( + const ObsAccessor &obsAccessor) const; + public: + typedef TrackCheckParameters Parameters_; + static const std::string classname() { return "ufo::TrackCheck"; } - TrackCheck(ioda::ObsSpace &obsdb, const eckit::Configuration &config, + TrackCheck(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, std::shared_ptr > flags, std::shared_ptr > obserr); ~TrackCheck() override; + private: /// \brief Attributes of an observation belonging to a track. class TrackObservation { @@ -143,6 +149,7 @@ class TrackCheck : public FilterBase, std::vector> &) const override; int qcFlag() const override {return QCflags::track;} + ObsAccessor createObsAccessor() const; /// Returns an interpolator mapping pressures (in Pa) to maximum accepted speeds (in km/s). PiecewiseLinearInterpolation makeMaxSpeedByPressureInterpolation() const; @@ -177,7 +184,7 @@ class TrackCheck : public FilterBase, std::vector &workspace) const; private: - std::unique_ptr options_; + Parameters_ options_; }; } // namespace ufo diff --git a/src/ufo/filters/TrackCheckShip.cc b/src/ufo/filters/TrackCheckShip.cc index e3bba889c..7eaf76b23 100644 --- a/src/ufo/filters/TrackCheckShip.cc +++ b/src/ufo/filters/TrackCheckShip.cc @@ -1,5 +1,5 @@ /* - * (C) Copyright 2020 Met Office UK + * (C) Copyright 2021 Met Office UK * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -26,6 +26,7 @@ #include "oops/util/DateTime.h" #include "oops/util/Duration.h" #include "oops/util/Logger.h" +#include "ufo/filters/ObsAccessor.h" #include "ufo/filters/TrackCheckShipDiagnostics.h" #include "ufo/filters/TrackCheckShipParameters.h" #include "ufo/utils/Constants.h" @@ -33,16 +34,14 @@ namespace ufo { -TrackCheckShip::TrackCheckShip(ioda::ObsSpace &obsdb, const eckit::Configuration &config, +TrackCheckShip::TrackCheckShip(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, std::shared_ptr > flags, std::shared_ptr > obserr) - : FilterBase(obsdb, config, flags, obserr) + : FilterBase(obsdb, parameters, flags, obserr), options_(parameters) { - oops::Log::debug() << "TrackCheckShip: config = " << config << std::endl; - options_.reset(new TrackCheckShipParameters()); - options_->deserialize(config); - assert(options_->maxSpeed.value() > 0); - if (options_->testingMode) + oops::Log::debug() << "TrackCheckShip: config = " << options_ << std::endl; + assert(options_.core.maxSpeed.value() > 0); + if (options_.testingMode) diagnostics_.reset(new TrackCheckShipDiagnostics()); } @@ -56,20 +55,22 @@ double TrackCheckShip::speedEstimate( const TrackCheckShipParameters& options) { util::Duration temporalDistance = abs(obs1.getTime() - obs2.getTime()); - util::Duration tempRes = options.temporalResolution; + util::Duration tempRes = options.core.temporalResolution; auto dist = distance(obs1, obs2); - auto spatialRes = options.spatialResolution; + auto spatialRes = options.core.spatialResolution; double speedEst = 0.0; if (dist > spatialRes) { speedEst = (dist - 0.5 * spatialRes) / std::max(temporalDistance.toSeconds(), (tempRes.toSeconds())); + speedEst *= 1000.0; // convert units from km/s to m/s } return speedEst; } /// \brief Returns the angle in degrees (rounded to the nearest .1 degree) -/// formed by the track segment going from observation \p a to \p b to \p c. +/// between displacement vectors going from observations \p a to \p b +/// and \p b to \p c. float TrackCheckShip::angle(const TrackCheckShip::TrackObservation &a, const TrackCheckShip::TrackObservation &b, const TrackCheckShip::TrackObservation &c) { @@ -95,12 +96,7 @@ TrackCheckShip::TrackObservation::TrackObservation( std::shared_ptr const &checkCounter, size_t observationNumber) : obsLocationTime_(latitude, longitude, time), fullTrackStatistics_(ts), - checkCounter_(checkCounter), observationNumber_(observationNumber) {} - -void TrackCheckShip::TrackObservation::registerCheckResult( - const TrackCheckUtils::CheckResult &result) { - checkCounter_->registerCheckResult(result); -} + observationNumber_(observationNumber), rejected_(false) {} size_t TrackCheckShip::TrackObservation::getObservationNumber() const { @@ -130,38 +126,50 @@ void TrackCheckShip::flagRejectedTrackObservations( auto trackObsIndexIt = trackObsIndicesBegin; auto trackObsIt = trackObservations.begin(); for (; trackObsIndexIt != trackObsIndicesEnd; ++trackObsIndexIt, ++trackObsIt) - if (trackObsIt->rejected()) - isRejected[validObsIds[*trackObsIndexIt]] = true; + isRejected[validObsIds[*trackObsIndexIt]] = trackObsIt->rejected(); } void TrackCheckShip::print(std::ostream & os) const { - os << "TrackCheckShip: config = " << config_ << std::endl; + os << "TrackCheckShip: config = " << options_ << std::endl; } -/// \todo This section is not yet fully implemented. Current implementation includes separating -/// observations into tracks based on \p Station_Id, calculating distances, speeds, and -/// angles between observations, and incrementing track-specific counters should the -/// calculations produce unexpected values. Based on the counter values, the filter -/// may be stopped early. Simultaneous observations are then optionally ignored for later -/// re-inclusion or rejection. Using the above calculations and additional ones if necessary, -/// the observation directly before and/or after the fastest segment is chosen for rejection. -/// Re-checking of simultaneous observations is not yet implemented. +/// The filter begins with separating observations into tracks based on \p Station_Id. Various +/// calculations are then performed between consecutive observations: distances/speeds between +/// observation pairs, and angles between the displacement vectors formed by triplets of +/// consecutive observations. Track-specific counters are incremented for various metrics that +/// indicate an unexpected track path. +/// +/// Based on these counter values, the filter may ignore the current track and move on to the next +/// one. Using the described calculations and additional ones if necessary, the observation +/// directly before and/or after the fastest segment is chosen for rejection. This will continue +/// until all remaining segments that link accepted observations are either: +/// 1. slower than a user-defined "max speed (m/s)" where the angles between the fastest accepted +/// segment's displacement vector and its two neighbouring segments' displacement vectors are +/// both less than 90 degrees. +/// 2. slower than 80 percent of this user-defined speed. +/// +/// The full track will be rejected if the number of observations removed is more than the +/// user-defined "rejection threshold". void TrackCheckShip::applyFilter(const std::vector & apply, const Variables & filtervars, std::vector> & flagged) const { - const std::vector validObsIds = TrackCheckUtils::getValidObservationIds(apply, flags_); + ObsAccessor obsAccessor = TrackCheckUtils::createObsAccessor(options_.stationIdVariable, obsdb_); - RecursiveSplitter splitter(validObsIds.size()); - TrackCheckUtils::groupObservationsByStation(validObsIds, splitter, config_, obsdb_); - TrackCheckUtils::sortTracksChronologically(validObsIds, splitter, obsdb_); + const std::vector validObsIds = obsAccessor.getValidObservationIds(apply, *flags_); + + RecursiveSplitter splitter = obsAccessor.splitObservationsIntoIndependentGroups(validObsIds); + TrackCheckUtils::sortTracksChronologically(validObsIds, obsAccessor, splitter); TrackCheckUtils::ObsGroupLocationTimes obsLocTime = - TrackCheckUtils::collectObservationsLocations(obsdb_); + TrackCheckUtils::collectObservationsLocations(obsAccessor); - std::vector isRejected(apply.size(), false); + std::vector isRejected(obsLocTime.latitudes.size(), false); + size_t trackNumber = 0; for (auto track : splitter.multiElementGroups()) { + trackNumber++; + std::string stationId = std::to_string(trackNumber); std::vector trackObservations = collectTrackObservations( track.begin(), track.end(), validObsIds, obsLocTime); std::vector> trackObservationsReferences; @@ -173,13 +181,10 @@ void TrackCheckShip::applyFilter(const std::vector & apply, calculateTrackSegmentProperties(trackObservationsReferences, CalculationMethod::FIRSTITERATION); if (!trackObservationsReferences.empty() && - this->options_->earlyBreakCheck && - TrackCheckShip::earlyBreak(trackObservationsReferences)) { + this->options_.core.earlyBreakCheck && + TrackCheckShip::earlyBreak(trackObservationsReferences, stationId)) { continue; } - if (options_->deferredCheckSimultaneous.value()) { - trackObservationsReferences = removeSimultaneousObservations(trackObservationsReferences); - } else { // reinstating deferred observation is not yet implemented bool firstIterativeRemoval = true; while (trackObservationsReferences.size() >= 3) { // Initial loop: fastest (as determined by set of comparisons) observation removed @@ -190,9 +195,9 @@ void TrackCheckShip::applyFilter(const std::vector & apply, return a.getObservationStatistics().speed < b.getObservationStatistics().speed;}); auto maxSpeedValue = maxSpeedReferenceIterator->get().getObservationStatistics().speed; - if (maxSpeedValue <= (0.8 * options_->maxSpeed.value())) { + if (maxSpeedValue <= (0.8 * options_.core.maxSpeed.value())) { break; - } else if (maxSpeedValue < options_->maxSpeed.value()) { + } else if (maxSpeedValue < options_.core.maxSpeed.value()) { auto maxSpeedAngle = std::max( (maxSpeedReferenceIterator - 1)->get().getObservationStatistics().angle, maxSpeedReferenceIterator->get().getObservationStatistics().angle); @@ -201,15 +206,24 @@ void TrackCheckShip::applyFilter(const std::vector & apply, } } removeFaultyObservation( - trackObservationsReferences, maxSpeedReferenceIterator, firstIterativeRemoval); + trackObservationsReferences, maxSpeedReferenceIterator, firstIterativeRemoval, + stationId); firstIterativeRemoval = false; calculateTrackSegmentProperties(trackObservationsReferences, CalculationMethod::MAINLOOP); } + auto rejectedCount = std::count_if(trackObservations.begin(), trackObservations.end(), + [](const TrackObservation& a) {return a.rejected();}); + if (rejectedCount >= options_.core.rejectionThreshold.value() * trackObservations.size()) { + oops::Log::trace() << "CheckShipTrack: track " << stationId << " NumRej " << + rejectedCount << " out of " << trackObservations.size() << + " reports rejected. *** Reject whole track ***\n"; + for (TrackObservation &obs : trackObservations) + obs.setRejected(true); + } flagRejectedTrackObservations(track.begin(), track.end(), validObsIds, trackObservations, isRejected); - } } - TrackCheckUtils::flagRejectedObservations(isRejected, flagged); + obsAccessor.flagRejectedObservations(isRejected, flagged); } /// \returns a \p vector of \p TrackObservations that all hold a \p shared_ptr to an instance @@ -243,61 +257,49 @@ std::vector TrackCheckShip::collectTrackObserv /// This filter is best for mostly-accurate observation tracks. If the track has /// many "errors" (as indicated by the counters that are incremented before /// any observations are removed), it will stop early by default. +/// +/// Sometimes caused by two ships with same callsign or various reports with wrong time, +/// the check gives up. This is particularly a problem with WOD01 data - case studies +/// suggest that most suspect data is reasonable. bool TrackCheckShip::earlyBreak(const std::vector> - &trackObs) const { + &trackObs, const std::string trackId) const { bool breakResult = false; const auto& trackStats = *(trackObs[0].get().getFullTrackStatistics()); // if at least half of the track segments have a time difference of less than an hour // (if non-buoy), are faster than a configured maximum speed, or exhibit at least a 90 // degree bend - if ((2 * ((options_->inputCategory.value() != 1) // 1 is input category of buoy + if ((2 * ((options_.inputCategory.value() != SurfaceObservationSubtype::BUOY && + options_.inputCategory.value() != SurfaceObservationSubtype::BUOYPROF) * trackStats.numShort_ + trackStats.numFast_) + trackStats.numBends_) >= (trackObs.size() - 1)) { - std::string stationId = "no station id provided"; - if (options_->stationIdVariable.value() != boost::none) { - stationId = (options_->stationIdVariable.value().get()).variable(); - } - oops::Log::warning() << "ShipTrackCheck: " << stationId << "\n" << - "Time difference < 1 hour: " << trackStats.numShort_ << "\n" << - "Fast: " << trackStats.numFast_ << "\n" << - "Bends: " << trackStats.numBends_ << "\n" << - "Track was not checked." << std::endl; + oops::Log::trace() << "ShipTrackCheck: " << trackId << "\n" << + "Time difference < 1 hour: " << trackStats.numShort_ << "\n" << + "Fast: " << trackStats.numFast_ << "\n" << + "Bends: " << trackStats.numBends_ << "\n" << + "Total observations: " << trackObs.size() << "\n" << + "Track was not checked." << std::endl; breakResult = true; } - if (options_->testingMode) + if (options_.testingMode) diagnostics_->storeEarlyBreakResult(breakResult); return breakResult; } -/// \brief Filters out all observations that have been marked simultaneous for -/// rechecking at the end. -std::vector> -TrackCheckShip::removeSimultaneousObservations( - const std::vector> &trackObs) const { - std::vector> reducedTrackObs; - std::copy_if(trackObs.begin(), trackObs.end(), std::back_inserter(reducedTrackObs), - [](const TrackObservation &obs){ - return !(obs.getObservationStatistics().simultaneous);}); - calculateTrackSegmentProperties(reducedTrackObs, CalculationMethod::SIMULTANEOUSDEFERRAL); - return reducedTrackObs; -} - /// \brief Chooses which of the observations surrounding the speediest segment to remove, /// flagging it accordingly. void TrackCheckShip::removeFaultyObservation( std::vector> &track, const std::vector>::iterator &observationAfterFastestSegment, - bool firstIterativeRemoval) const { + bool firstIterativeRemoval, const std::string trackId) const { int errorCategory = 0; util::Duration four_days{"P4D"}; auto rejectedObservation = observationAfterFastestSegment; // lambda function to "fail" an observation that should be rejected auto fail = [&rejectedObservation]( const std::vector>::iterator &iter) { - iter->get().registerCheckResult(TrackCheckUtils::CheckResult::FAILED); - iter->get().registerSweepOutcome(true); + iter->get().setRejected(true); rejectedObservation = iter; }; auto neighborObservationStatistics = [&observationAfterFastestSegment](int index) -> @@ -308,9 +310,9 @@ void TrackCheckShip::removeFaultyObservation( if (observationAfterFastestSegment == track.begin() + 1) { // Decide whether ob 0 or 1 agrees best with ob 2 if (neighborObservationStatistics(0).speedAveraged <= - options_->maxSpeed && + options_.core.maxSpeed && (neighborObservationStatistics(1).speed > - options_->maxSpeed || + options_.core.maxSpeed || neighborObservationStatistics(1).angle > 45.0)) { fail(observationAfterFastestSegment); errorCategory = 2; @@ -320,9 +322,9 @@ void TrackCheckShip::removeFaultyObservation( } } else if (observationAfterFastestSegment == track.end() - 1) { if (neighborObservationStatistics(-1).speedAveraged <= - options_->maxSpeed && + options_.core.maxSpeed && (neighborObservationStatistics(-1).speed > - options_->maxSpeed || + options_.core.maxSpeed || neighborObservationStatistics(-2).angle > 45.0)) { fail(observationAfterFastestSegment - 1); errorCategory = 2; @@ -331,22 +333,22 @@ void TrackCheckShip::removeFaultyObservation( errorCategory = 1; } } else if (neighborObservationStatistics(-1).speed > - options_->maxSpeed) { + options_.core.maxSpeed) { fail(observationAfterFastestSegment - 1); errorCategory = 4; // Category 4: both segments surrounding observation have excessive speed } else if (neighborObservationStatistics(1).speed > - options_->maxSpeed) { + options_.core.maxSpeed) { fail(observationAfterFastestSegment); errorCategory = 4; } else if (neighborObservationStatistics(0).speedAveraged > - options_->maxSpeed) { + options_.core.maxSpeed) { fail(observationAfterFastestSegment - 1); errorCategory = 5; // Category 5: observation before fastest segment would still begin a fast segment // if observation after fastest segment were removed } else if (neighborObservationStatistics(-1).speedAveraged > - options_->maxSpeed) { + options_.core.maxSpeed) { fail(observationAfterFastestSegment); errorCategory = 5; // Category 5: observation after fastest segment would still end a fast segment if @@ -393,7 +395,7 @@ void TrackCheckShip::removeFaultyObservation( neighborObservationStatistics(0).distanceAveraged; util::Duration timeSum = (observationAfterFastestSegment + 1)->get().getTime() - ( observationAfterFastestSegment - 2)->get().getTime(); - if (options_->testingMode.value()) { + if (options_.testingMode.value()) { diagnostics_->storeDistanceSum(distanceSum); diagnostics_->storeDistancePrevObsOmitted(distancePrevObsOmitted); diagnostics_->storeDistanceCurrentObsOmitted(distanceCurrentObsOmitted); @@ -401,12 +403,12 @@ void TrackCheckShip::removeFaultyObservation( diagnostics_->storeTimeSum(timeDouble); } if (distancePrevObsOmitted < distanceCurrentObsOmitted - std::max( - options_->spatialResolution.value(), 0.1 * distanceSum)) { + options_.core.spatialResolution.value(), 0.1 * distanceSum)) { fail(observationAfterFastestSegment - 1); errorCategory = 9; } else if (distanceCurrentObsOmitted < ( distancePrevObsOmitted - std::max( - options_->spatialResolution.value(), 0.1 * distanceSum))) { + options_.core.spatialResolution.value(), 0.1 * distanceSum))) { fail(observationAfterFastestSegment); errorCategory = 9; } else if (timeSum <= four_days && timeSum.toSeconds() > 0 && @@ -427,7 +429,7 @@ void TrackCheckShip::removeFaultyObservation( static_cast(((observationAfterFastestSegment->get().getTime()) - (observationAfterFastestSegment - 2)->get().getTime()).toSeconds()) / timeSum.toSeconds(); - if (options_->testingMode.value()) { + if (options_.testingMode.value()) { diagnostics_->storePreviousSegmentDistanceProportion(previousSegmentDistanceProportion); diagnostics_->storePreviousObservationDistanceAveragedProportion( previousObservationDistanceAveragedProportion); @@ -463,24 +465,27 @@ void TrackCheckShip::removeFaultyObservation( } if (errorCategory == 9 || std::min(distancePrevObsOmitted, distanceCurrentObsOmitted) == 0.0) { oops::Log::trace() << "CheckShipTrack: Dist check, station id: " << - options_->stationIdVariable.value().get().variable() << std::endl << + trackId << std::endl << " error category: " << errorCategory << std::endl << " distances: " << distanceSum * 0.001 << " " << distancePrevObsOmitted * 0.001 << " " << distanceCurrentObsOmitted * 0.001 << " " << (distancePrevObsOmitted - distanceCurrentObsOmitted) * 0.001 << - " " << std::max(options_->spatialResolution.value(), + " " << std::max(options_.core.spatialResolution.value(), 0.1 * distanceSum) * 0.001 << "[km]" << std::endl; } } if (errorCategory == 0 || ((rejectedObservation->get().getObservationStatistics(). speedAveraged) > - options_->maxSpeed.value())) { - oops::Log::trace() << "CheckShipTrack: cannot decide between observation and previous, " << - "rejecting both." << std::endl; + options_.core.maxSpeed.value())) { + oops::Log::trace() << "CheckShipTrack: cannot decide between station id " << + trackId << " observations " << + (observationAfterFastestSegment - 1)->get().getObservationNumber() << + " " << observationAfterFastestSegment->get().getObservationNumber() << + " rejecting both." << std::endl; errorCategory += 100; - if (options_->testingMode.value() && firstIterativeRemoval) { + if (options_.testingMode.value() && firstIterativeRemoval) { std::vector observationNumbersAroundFastest{ (observationAfterFastestSegment - 1)->get().getObservationNumber(), observationAfterFastestSegment->get().getObservationNumber()}; @@ -494,16 +499,28 @@ void TrackCheckShip::removeFaultyObservation( } track.erase(observationAfterFastestSegment - 1, observationAfterFastestSegment); } else { - if (options_->testingMode.value() && firstIterativeRemoval) { + if (options_.testingMode.value() && firstIterativeRemoval) { std::vector rejectedObservationNumber{rejectedObservation->get(). getObservationNumber()}; diagnostics_->storeFirstIterativeRemovalInfo( std::make_pair(rejectedObservationNumber, errorCategory)); } + oops::Log::trace() << "CheckShipTrack: rejecting station " << trackId << " observation " << + rejectedObservation->get().getObservationNumber() << "\n" << + "Error category: " << errorCategory << "\n" << + "rejection candidates: " << + (observationAfterFastestSegment - 1)->get().getObservationNumber() << + " " << observationAfterFastestSegment->get().getObservationNumber() << + "\n" << "speeds: " << (observationAfterFastestSegment - 1)->get(). + getObservationStatistics().speed << " " << + observationAfterFastestSegment->get().getObservationStatistics().speed << + "\n" << (observationAfterFastestSegment - 1)->get(). + getObservationStatistics().angle << " " << + observationAfterFastestSegment->get(). + getObservationStatistics().angle << "\n"; track.erase(rejectedObservation); } - oops::Log::trace() << "Error category: " << errorCategory << std::endl; } /// \todo Trace output will need to be changed to match that of OPS (indices, LWin) @@ -521,24 +538,25 @@ void TrackCheckShip::TrackObservation::calculateTwoObservationValues( TrackObservation& prevObs, bool firstIteration, const TrackCheckShipParameters &options) { this->setDistance(TrackCheckShip::distance(prevObs, *this)); - (this->observationStatistics_.distance > options.spatialResolution) ? + (this->observationStatistics_.distance > options.core.spatialResolution) ? this->setSpeed(TrackCheckShip::speedEstimate(*this, prevObs, options)) : this->setSpeed(0.0); this->setTimeDifference(getTime() - prevObs.getTime()); - if (!(this->observationStatistics_.timeDifference.toSeconds())) { - if (this->observationStatistics_.distance > - options.spatialResolution) { - prevObs.setSimultaneous(true); - } - setSimultaneous(true); - } if (firstIteration) { adjustTwoObservationStatistics(options); } } +void TrackCheckShip::TrackObservation::resetObservationCalculations() { + this->setDistance(0.0); + this->setSpeed(0.0); + this->setDistanceAveraged(0.0); + this->setSpeedAveraged(0.0); + this->setAngle(0.0); +} + /// Calculates all of the statistics that require three -/// adjacent \p TrackObservations, +/// consecutive \p TrackObservations, /// storing within the middle observation. This includes /// distance between two alternating observations, /// speed between these alternating observations (if the middle @@ -552,7 +570,7 @@ void TrackCheckShip::TrackObservation::calculateThreeObservationValues( this->setSpeedAveraged(speedEstimate(prevObs, nextObs, options)); if (std::min(this->observationStatistics_.distance, nextObs.observationStatistics_.distance) > - options.spatialResolution) { + options.core.spatialResolution) { this->observationStatistics_.angle = angle(prevObs, *this, nextObs); } if (firstIteration) { @@ -568,14 +586,16 @@ void TrackCheckShip::calculateTrackSegmentProperties( const std::vector> &trackObservations, CalculationMethod calculationMethod) const { if (trackObservations.size()) { + if (calculationMethod == MAINLOOP) + trackObservations[0].get().resetObservationCalculations(); for (size_t obsIdx = 1; obsIdx < trackObservations.size(); obsIdx++) { TrackObservation &obs = trackObservations[obsIdx].get(); TrackObservation &prevObs = trackObservations[obsIdx - 1].get(); - obs.calculateTwoObservationValues(prevObs, calculationMethod == FIRSTITERATION, *options_); + obs.calculateTwoObservationValues(prevObs, calculationMethod == FIRSTITERATION, options_); if (obsIdx > 1) { const TrackObservation &prevPrevObs = trackObservations[obsIdx - 2].get(); prevObs.calculateThreeObservationValues(prevPrevObs, obs, - calculationMethod == FIRSTITERATION, *options_); + calculationMethod == FIRSTITERATION, options_); } if (calculationMethod == FIRSTITERATION && (obsIdx == trackObservations.size() - 1)) { int potentialDenominator = trackObservations.size() - 1 - @@ -584,7 +604,7 @@ void TrackCheckShip::calculateTrackSegmentProperties( std::max(1, potentialDenominator); } } - if (options_->testingMode.value() && calculationMethod != MAINLOOP) { + if (options_.testingMode.value() && calculationMethod != MAINLOOP) { std::vector obsStats; for (size_t obsIdx = 0; obsIdx < trackObservations.size(); ++obsIdx) { obsStats.push_back(trackObservations[obsIdx].get().getObservationStatistics()); @@ -592,8 +612,6 @@ void TrackCheckShip::calculateTrackSegmentProperties( auto trackStats = *(trackObservations[0].get().getFullTrackStatistics()); if (calculationMethod == FIRSTITERATION) diagnostics_->storeInitialCalculationResults(std::make_pair(obsStats, trackStats)); - else if (calculationMethod == SIMULTANEOUSDEFERRAL) - diagnostics_->storeCalculatedResultsSimultaneousDeferred(obsStats); } } } @@ -603,11 +621,9 @@ void TrackCheckShip::calculateTrackSegmentProperties( void TrackCheckShip::TrackObservation::adjustTwoObservationStatistics (const TrackCheckShipParameters &options) const { util::Duration hour{"PT1H"}; - if (getObservationStatistics().distance == 0.0) - getFullTrackStatistics()->numDist0_++; if (getObservationStatistics().timeDifference < hour) { getFullTrackStatistics()->numShort_++; - } else if (getObservationStatistics().speed >= options.maxSpeed) { + } else if (getObservationStatistics().speed >= options.core.maxSpeed) { getFullTrackStatistics()->numFast_++; } else { getFullTrackStatistics()->sumSpeed_ += getObservationStatistics().speed; @@ -632,14 +648,6 @@ TrackCheckShip::TrackObservation::getFullTrackStatistics() const return fullTrackStatistics_; } -/// Sets the calling observation as simultaneous and increments the track-wise -/// \p numSimultaneous_ counter. -void TrackCheckShip::TrackObservation::setSimultaneous(bool simul) { - assert(simul); // reverting a simultaneous obs to normal is not yet implemented - if (!this->observationStatistics_.simultaneous) - this->fullTrackStatistics_->numSimultaneous_++; - this->observationStatistics_.simultaneous = simul; -} void TrackCheckShip::TrackObservation::setDistance(double dist) { this->observationStatistics_.distance = dist; } diff --git a/src/ufo/filters/TrackCheckShip.h b/src/ufo/filters/TrackCheckShip.h index 4bb13f1e3..d01d6f5c0 100644 --- a/src/ufo/filters/TrackCheckShip.h +++ b/src/ufo/filters/TrackCheckShip.h @@ -26,6 +26,7 @@ #include "oops/util/ObjectCounter.h" #include "ufo/filters/FilterBase.h" #include "ufo/filters/QCflags.h" +#include "ufo/filters/TrackCheckShipParameters.h" #include "ufo/filters/TrackCheckUtils.h" namespace eckit { @@ -40,27 +41,32 @@ class ObsSpace; namespace ufo { class TrackCheckShipDiagnostics; -class TrackCheckShipParameters; class RecursiveSplitter; + /// \brief Checks tracks of ships and buoys, rejecting observations whose locations -/// and timestamps make them inconsistent with the rest of the track. +/// and timestamps make them inconsistent with the rest of the track. The full track is rejected +/// if there are too many inconsistent observations. /// /// Each track is checked separately. The algorithm will first calculate speeds and distances /// between every two adjacent and alternating observations, and angles between any three adjacent /// observations. Based on these values, it will increment a set of "error counters" that reflect -/// how many errors exist within the track. By default, if the "error counters" sum up to a value -/// greater than or equal to half of the segments, the filter will be stopped. -/// -/// \todo the implementation of the remainder of the filter is still in progress. +/// how many errors exist within the track. If the "early break check" parameter is set to true and +/// the "error counters" sum up to a value greater than or equal to half of the segments, the +/// filter will ignore the current track and move on to the next one. Otherwise, observations will +/// be iteratively removed based on the location of the fastest segment until all segments between +/// observations are slower than the speed set by the "max speed" parameter (or 80% of it if angles +/// between segments are large). /// /// See TrackCheckShipsParameters for the documentation of this filter's parameters. /// class TrackCheckShip: public FilterBase, private util::ObjectCounter { public: + typedef TrackCheckShipParameters Parameters_; + static const std::string classname() {return "ufo::TrackCheckShip";} - TrackCheckShip(ioda::ObsSpace &obsdb, const eckit::Configuration &config, + TrackCheckShip(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, std::shared_ptr > flags, std::shared_ptr > obserr); @@ -69,11 +75,6 @@ class TrackCheckShip: public FilterBase, /// \brief Relevant calculated values that are specific to each pair or triplet /// of adjacent/alternating observations struct ObservationStatistics { - /// Simultaneous holds \p true if the same-index observation is - /// occurring at the same time-stamp as either - /// the previous-index observation or the next-index observation - /// (if the observations are at different locations) - bool simultaneous{}; /// \brief \p timeDifference between the same-index observation and the /// previous one. util::Duration timeDifference{}; @@ -112,11 +113,6 @@ class TrackCheckShip: public FilterBase, /// \p numBends_ is a count of 3-consecutive-observation track segments /// in which the track shows a 90-degree or greater turn. int numBends_{}; - /// \p numDist0_ is the count of consecutive observations with a - /// cartesian distance that measures 0.0 metres. - int numDist0_{}; - /// \p numSimultaneous_ is the count of observations deemed simultaneous. - int numSimultaneous_{}; /// \p sumSpeed_ is the sum of all speed values recorded for observations /// that have neither been deemed "fast" or "short" double sumSpeed_{}; @@ -138,7 +134,6 @@ class TrackCheckShip: public FilterBase, const util::DateTime& getTime() const { return obsLocationTime_.time(); } - void setSimultaneous(bool simul); void setDistance(double dist); void setTimeDifference(util::Duration tDiff); void setSpeed(double speed); @@ -149,6 +144,7 @@ class TrackCheckShip: public FilterBase, void calculateTwoObservationValues( TrackObservation& prevObs, bool firstIteration, const TrackCheckShipParameters& options); + void resetObservationCalculations(); void calculateThreeObservationValues( const TrackObservation& prevObs, const TrackObservation& nextObs, bool firstIteration, const TrackCheckShipParameters& options); @@ -159,8 +155,7 @@ class TrackCheckShip: public FilterBase, const ObservationStatistics &getObservationStatistics() const; - void registerCheckResult(const TrackCheckUtils::CheckResult &result); - void registerSweepOutcome(bool rejectedInSweep) { + void setRejected(bool rejectedInSweep) { rejected_ = rejectedInSweep; } @@ -172,7 +167,6 @@ class TrackCheckShip: public FilterBase, private: std::shared_ptr fullTrackStatistics_; - std::shared_ptr checkCounter_; ObservationStatistics observationStatistics_; TrackCheckUtils::ObsLocationTime obsLocationTime_; size_t observationNumber_; @@ -196,7 +190,7 @@ class TrackCheckShip: public FilterBase, const TrackCheckShipDiagnostics* diagnostics() const; private: - std::unique_ptr options_; + Parameters_ options_; std::unique_ptr diagnostics_; void flagRejectedTrackObservations( @@ -211,7 +205,7 @@ class TrackCheckShip: public FilterBase, std::vector> &) const override; int qcFlag() const override {return QCflags::track;} - enum CalculationMethod { FIRSTITERATION, SIMULTANEOUSDEFERRAL, MAINLOOP }; + enum CalculationMethod { FIRSTITERATION, MAINLOOP }; void calculateTrackSegmentProperties( const std::vector> &trackObservations, @@ -223,14 +217,13 @@ class TrackCheckShip: public FilterBase, const std::vector &validObsIds, const TrackCheckUtils::ObsGroupLocationTimes &obsLoc) const; - std::vector> removeSimultaneousObservations( - const std::vector> &trackObs) const; - bool earlyBreak(const std::vector> &trackObs) const; + bool earlyBreak(const std::vector> &trackObs, + const std::string trackId) const; void removeFaultyObservation( std::vector > &track, const std::vector >::iterator &it, - bool firstIterativeRemoval = false) const; + bool firstIterativeRemoval, const std::string trackId) const; }; } // namespace ufo diff --git a/src/ufo/filters/TrackCheckShipDiagnostics.h b/src/ufo/filters/TrackCheckShipDiagnostics.h index 14b5f5f1e..52670984b 100644 --- a/src/ufo/filters/TrackCheckShipDiagnostics.h +++ b/src/ufo/filters/TrackCheckShipDiagnostics.h @@ -20,7 +20,6 @@ class TrackCheckShipDiagnostics typedef std::pair SingleTrackInitialCalculationResults; typedef std::pair, int> FirstIterativeRemovalInfo; std::vector multipleTrackInitialCalculationResults_; - std::vector calculatedResultsSimultaneousDeferred_; std::vector earlyBreaks_; std::vector firstIterativeRemovalInfo_; std::vector distanceSum_, distancePrevObsOmitted_, distanceCurrentObsOmitted_, @@ -52,19 +51,6 @@ class TrackCheckShipDiagnostics return earlyBreaks_; } - /// \brief Stores the recalculations of values after deferring simultaneous observations. - /// - /// Does not store counter values, because those are not updated after the first iteration. - void storeCalculatedResultsSimultaneousDeferred(ObsStatsVec obsStatsVec) { - calculatedResultsSimultaneousDeferred_.push_back(obsStatsVec); - } - - /// \brief Returns the recalculated values calculated after deferring simultaneous - /// observations - const std::vector &getCalculatedResultsSimultaneousDeferred() const { - return calculatedResultsSimultaneousDeferred_; - } - /// \brief Stores the observation(s) removed on the first iteration of the main removal loop. void storeFirstIterativeRemovalInfo( const FirstIterativeRemovalInfo &firstIterativeRemovalInfo) diff --git a/src/ufo/filters/TrackCheckShipParameters.h b/src/ufo/filters/TrackCheckShipParameters.h index 5a1be6c12..3358d34e6 100644 --- a/src/ufo/filters/TrackCheckShipParameters.h +++ b/src/ufo/filters/TrackCheckShipParameters.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2020 Met Office UK + * (C) Copyright 2021 Met Office UK * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -15,37 +15,57 @@ #include "oops/util/parameters/Parameter.h" #include "oops/util/parameters/Parameters.h" #include "oops/util/parameters/ParameterTraits.h" +#include "oops/util/parameters/RequiredParameter.h" #include "ufo/filters/TrackCheckUtilsParameters.h" - - namespace ufo { -/// \brief Options controlling the operation of the ship track check filter. -class TrackCheckShipParameters : public TrackCheckUtilsParameters { +class TrackCheckShipCoreParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(TrackCheckShipCoreParameters, Parameters) + public: /// Assumed temporal resolution of the observations, i.e. absolute accuracy of the reported /// observation times. - oops::Parameter temporalResolution{ - "temporal resolution", util::Duration("PT10M"), this + oops::RequiredParameter temporalResolution { + "temporal resolution", this }; - /// Assumed spatial resolution of the observations (in km), i.e. absolute accuracy of the + /// Assumed spatial resolution (km) of the observations, i.e. absolute accuracy of the /// reported positions. /// /// Instantaneous speeds are estimated conservatively with the formula /// /// speed_estimate = (reported_distance - spatial resolution) / /// (reported_time + temporal resolution). - oops::Parameter spatialResolution { - "spatial resolution", 1.0, this + oops::RequiredParameter spatialResolution { + "spatial resolution (km)", this + }; + + /// Maximum speed (before marking as fast) in m/s + oops::RequiredParameter maxSpeed { + "max speed (m/s)", this + }; + + /// The fraction of track observations that must be flagged in the track filter + /// for the full track to be rejected. + oops::RequiredParameter rejectionThreshold { + "rejection threshold", this }; - /// Maximum speed (before marking as fast) in km/s - oops::Parameter maxSpeed { - "max speed", 1.0, this + /// \brief If \p earlyBreakCheck set to true, check will stop early based on the number + /// of short-spaced, fast, and bended segments of the track + oops::RequiredParameter earlyBreakCheck { + "early break check", this }; +}; + +/// \brief Options controlling the operation of the ship track check filter. +class TrackCheckShipParameters : public TrackCheckUtilsParameters { + OOPS_CONCRETE_PARAMETERS(TrackCheckShipParameters, TrackCheckUtilsParameters) + + public: + TrackCheckShipCoreParameters core{this}; /// The start of an observation window where trace output should be produced. If blank, /// the start of the track will be treated as the start of this window. @@ -60,27 +80,20 @@ class TrackCheckShipParameters : public TrackCheckUtilsParameters { }; /// The type of input source. This affects the treatment of tracks - /// with large numbers of simultaneous observations. - oops::Parameter inputCategory { - "input category", 2, this // 1 for buoy/other fixed input; 2 for ship; 3 for ship_auto - }; - - /// \brief If \p deferredCheckSimultaneous set to true, check of simultaneous - /// observations will be deferred until the end, at which point flagged observations - /// may be reinstated. - oops::Parameter deferredCheckSimultaneous { - "deferred check simultaneous", false, this + /// with large numbers of short segments between observations. + oops::Parameter inputCategory { + "input category", SurfaceObservationSubtype::SHPSYN, this }; - /// \brief If \p earlyBreakCheck set to true, check will stop early based on the number - /// of short-spaced, fast, and bended segments of the track - oops::Parameter earlyBreakCheck { - "early break check", true, this - }; /// \brief To be set to \p true if the filter's unit tests are being run oops::Parameter testingMode { - "testing mode", false, this + "unit testing mode", false, this + }; + + /// \brief To be set to \p true if the filter's single-segment comparison test is being run. + oops::Parameter comparisonTesting { + "comparison test", false, this }; }; diff --git a/src/ufo/filters/TrackCheckUtils.cc b/src/ufo/filters/TrackCheckUtils.cc index 84bdfbee8..5d985561b 100644 --- a/src/ufo/filters/TrackCheckUtils.cc +++ b/src/ufo/filters/TrackCheckUtils.cc @@ -14,17 +14,17 @@ #include "eckit/geometry/Point2.h" #include "eckit/geometry/Point3.h" #include "eckit/geometry/Sphere.h" -#include "ioda/ObsDataVector.h" #include "ioda/ObsSpace.h" #include "oops/util/DateTime.h" #include "oops/util/sqr.h" +#include "ufo/filters/ObsAccessor.h" #include "ufo/filters/QCflags.h" #include "ufo/filters/TrackCheckUtils.h" -#include "ufo/filters/TrackCheckUtilsParameters.h" #include "ufo/utils/Constants.h" #include "ufo/utils/RecursiveSplitter.h" namespace ufo { + namespace { /// \brief Returns the square of the distance between the two \p Point arguments. float distance2(const TrackCheckUtils::Point &a, const TrackCheckUtils::Point &b) { @@ -52,120 +52,41 @@ float TrackCheckUtils::distance(const Point &a, const Point &b) { return std::sqrt(distance2(a, b)); } -/// Return the vector of elements of \p categories with indices \p validObsIds. -template -std::vector getValidObservationCategories(const std::vector &categories, - const std::vector validObsIds) { - std::vector validObsCategories(validObsIds.size()); - for (size_t validObsIndex = 0; validObsIndex < validObsIds.size(); ++validObsIndex) { - validObsCategories[validObsIndex] = categories[validObsIds[validObsIndex]]; - } - return validObsCategories; -} - -std::vector TrackCheckUtils::getValidObservationIds( - const std::vector &apply, const std::shared_ptr> &flags) { - std::vector validObsIds; - for (size_t obsId = 0; obsId < apply.size(); ++obsId) - if (apply[obsId] && (*(flags))[0][obsId] == QCflags::pass) - validObsIds.push_back(obsId); - return validObsIds; -} - -void TrackCheckUtils::groupObservationsByStation(const std::vector &validObsIds, - RecursiveSplitter &splitter, - const eckit::Configuration &config, - const ioda::ObsSpace &obsdb) { - std::unique_ptr baseOptions_; - baseOptions_.reset(new TrackCheckUtilsParameters()); - baseOptions_->deserialize(config); - if (baseOptions_->stationIdVariable.value() == boost::none) { - if (obsdb.obs_group_var().empty()) { - // Observations were not grouped into records. - // Assume all observations were taken during the same station. - return; - } else { - groupObservationsByRecordNumber(validObsIds, splitter, obsdb); - } +ObsAccessor TrackCheckUtils::createObsAccessor(const boost::optional &stationIdVariable, + const ioda::ObsSpace &obsdb) { + if (stationIdVariable != boost::none) { + return ObsAccessor::toObservationsSplitIntoIndependentGroupsByVariable( + obsdb, *stationIdVariable); + } else if (!obsdb.obs_group_vars().empty()) { + // Assume observations were grouped into records by station IDs + return ObsAccessor::toObservationsSplitIntoIndependentGroupsByRecordId(obsdb); } else { - groupObservationsByVariable(*baseOptions_->stationIdVariable.value(), - validObsIds, splitter, obsdb); + // Observations were not grouped into records and no station ID variable was provided. + // Assume all observations were taken by the same station. + return ObsAccessor::toAllObservations(obsdb); } } -void TrackCheckUtils::groupObservationsByRecordNumber(const std::vector &validObsIds, - RecursiveSplitter &splitter, - const ioda::ObsSpace &obsdb) { - const std::vector &obsCategories = obsdb.recnum(); - std::vector validObsCategories = getValidObservationCategories( - obsCategories, validObsIds); - splitter.groupBy(validObsCategories); -} - -void TrackCheckUtils::groupObservationsByVariable(const Variable &variable, - const std::vector &validObsIds, - RecursiveSplitter &splitter, - const ioda::ObsSpace &obsdb) { - switch (obsdb.dtype(variable.group(), variable.variable())) { - case ioda::ObsDtype::Integer: - groupObservationsByTypedVariable(variable, validObsIds, splitter, obsdb); - break; - - case ioda::ObsDtype::String: - groupObservationsByTypedVariable(variable, validObsIds, splitter, obsdb); - break; - - default: - throw eckit::UserError( - "Only integer and string variables may be used as station IDs", Here()); - } -} -template -void TrackCheckUtils::groupObservationsByTypedVariable(const Variable &variable, - const std::vector &validObsIds, - RecursiveSplitter &splitter, - const ioda::ObsSpace &obsdb) { - std::vector obsCategories(obsdb.nlocs()); - obsdb.get_db(variable.group(), variable.variable(), obsCategories); - std::vector validObsCategories = getValidObservationCategories( - obsCategories, validObsIds); - - splitter.groupBy(validObsCategories); -} void TrackCheckUtils::sortTracksChronologically(const std::vector &validObsIds, - RecursiveSplitter &splitter, - const ioda::ObsSpace &obsdb) { - std::vector times(obsdb.nlocs()); - obsdb.get_db("MetaData", "datetime", times); + const ObsAccessor &obsAccessor, + RecursiveSplitter &splitter) { + const std::vector times = obsAccessor.getDateTimeVariableFromObsSpace( + "MetaData", "datetime"); splitter.sortGroupsBy([×, &validObsIds](size_t obsIndexA, size_t obsIndexB) { return times[validObsIds[obsIndexA]] < times[validObsIds[obsIndexB]]; }); } TrackCheckUtils::ObsGroupLocationTimes - TrackCheckUtils::collectObservationsLocations(const ioda::ObsSpace &obsdb) { + TrackCheckUtils::collectObservationsLocations(const ObsAccessor &obsAccessor) { ObsGroupLocationTimes locationTimes; - locationTimes.latitudes.resize(obsdb.nlocs()); - obsdb.get_db("MetaData", "latitude", locationTimes.latitudes); - - locationTimes.longitudes.resize(obsdb.nlocs()); - obsdb.get_db("MetaData", "longitude", locationTimes.longitudes); - - locationTimes.datetimes.resize(obsdb.nlocs()); - obsdb.get_db("MetaData", "datetime", locationTimes.datetimes); + locationTimes.latitudes = obsAccessor.getFloatVariableFromObsSpace("MetaData", "latitude"); + locationTimes.longitudes = obsAccessor.getFloatVariableFromObsSpace("MetaData", "longitude"); + locationTimes.datetimes = obsAccessor.getDateTimeVariableFromObsSpace("MetaData", "datetime"); return locationTimes; } -void TrackCheckUtils::flagRejectedObservations(const std::vector &isRejected, - std::vector > - &flagged) { - for (std::vector & variableFlagged : flagged) - for (size_t obsId = 0; obsId < isRejected.size(); ++obsId) - if (isRejected[obsId]) - variableFlagged[obsId] = true; -} - TrackCheckUtils::ObsLocationTime::ObsLocationTime(float latitude, float longitude, const util::DateTime &time) : location_(pointFromLatLon(latitude, longitude)), time_(time) @@ -174,9 +95,11 @@ TrackCheckUtils::ObsLocationTime::ObsLocationTime(float latitude, float longitud float TrackCheckUtils::CheckCounter::failedChecksFraction() const { return numChecks_ != 0 ? static_cast(numFailedChecks_) / numChecks_ : 0.0f; } + TrackCheckUtils::CheckCounter::CheckCounter() : numFailedChecks_(0), numChecks_(0) {} + void TrackCheckUtils::CheckCounter::registerCheckResult(const CheckResult &result) { if (result != CheckResult::SKIPPED) ++numChecks_; diff --git a/src/ufo/filters/TrackCheckUtils.h b/src/ufo/filters/TrackCheckUtils.h index 9db30874f..f3b95d5cb 100644 --- a/src/ufo/filters/TrackCheckUtils.h +++ b/src/ufo/filters/TrackCheckUtils.h @@ -12,6 +12,8 @@ #include #include +#include + #include "eckit/config/Configuration.h" #include "oops/util/DateTime.h" #include "oops/util/Duration.h" @@ -32,6 +34,7 @@ inline util::Duration abs(const util::Duration &duration) { return duration.toSeconds() >= 0 ? duration : -duration; } +class ObsAccessor; class RecursiveSplitter; namespace TrackCheckUtils { @@ -78,37 +81,28 @@ class CheckCounter { int numChecks_; }; -std::vector getValidObservationIds( - const std::vector &apply, - const std::shared_ptr> &flags); - -void groupObservationsByStation(const std::vector &validObsIds, - RecursiveSplitter &splitter, - const eckit::Configuration &config, - const ioda::ObsSpace &obsdb); - -void groupObservationsByRecordNumber(const std::vector &validObsIds, - RecursiveSplitter &splitter, - const ioda::ObsSpace &obsdb); - -void groupObservationsByVariable(const Variable &variable, - const std::vector &validObsIds, - RecursiveSplitter &splitter, const ioda::ObsSpace &obsdb); - -template -void groupObservationsByTypedVariable(const Variable &variable, - const std::vector &validObsIds, - RecursiveSplitter &splitter, - const ioda::ObsSpace &obsdb); +/// \brief Create an ObsAccessor object providing access to observations that need to be checked +/// by the current MPI task. +/// +/// The composition of this set of observations depends on the value of \p stationIdVariable. +/// +/// If \p stationIdVariable is empty, observations are assumed to be grouped into tracks by the +/// record ID. Each MPI rank is guaranteed to hold either all or no observations from a given +/// record. Thus the returned ObsAccessor gives access to all observations from the records held on +/// the current MPI rank. +/// +/// Otherwise, observations are assumed to be grouped into tracks by the variable \p +/// *stationIdVariable. If this variable was also used to group observations into records, the +/// returned ObsAccessor is constructed as if \p stationIdVariable was empty; otherwise, it gives +/// access to observations held on all MPI ranks. +ObsAccessor createObsAccessor(const boost::optional &stationIdVariable, + const ioda::ObsSpace &obsdb); void sortTracksChronologically(const std::vector &validObsIds, - RecursiveSplitter &splitter, - const ioda::ObsSpace &obsdb); - -ObsGroupLocationTimes collectObservationsLocations(const ioda::ObsSpace &obsdb); + const ObsAccessor &obsAccessor, + RecursiveSplitter &splitter); -void flagRejectedObservations(const std::vector &isRejected, - std::vector > &flagged); +ObsGroupLocationTimes collectObservationsLocations(const ObsAccessor &obsAccessor); } // namespace TrackCheckUtils diff --git a/src/ufo/filters/TrackCheckUtilsParameters.cc b/src/ufo/filters/TrackCheckUtilsParameters.cc new file mode 100644 index 000000000..868f3e8e0 --- /dev/null +++ b/src/ufo/filters/TrackCheckUtilsParameters.cc @@ -0,0 +1,16 @@ +/* + * (C) 2021 Crown Copyright Met Office. All rights reserved. + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ +#include + +#include "ufo/filters/TrackCheckUtilsParameters.h" + +namespace ufo { + +constexpr char SurfaceObservationSubtypeParameterTraitsHelper::enumTypeName[]; +constexpr util::NamedEnumerator + SurfaceObservationSubtypeParameterTraitsHelper::namedValues[]; + +} // namespace ufo diff --git a/src/ufo/filters/TrackCheckUtilsParameters.h b/src/ufo/filters/TrackCheckUtilsParameters.h index ef4936b61..83abe7e5e 100644 --- a/src/ufo/filters/TrackCheckUtilsParameters.h +++ b/src/ufo/filters/TrackCheckUtilsParameters.h @@ -1,6 +1,5 @@ /* - * (C) Copyright 2019 Met Office UK - * + * (C) 2021 Crown Copyright Met Office. All rights reserved. * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ @@ -10,20 +9,51 @@ #include "oops/util/parameters/OptionalParameter.h" #include "oops/util/parameters/Parameters.h" +#include "ufo/filters/FilterParametersBase.h" #include "ufo/utils/parameters/ParameterTraitsVariable.h" namespace eckit { class Configuration; } +namespace ufo { + + enum class SurfaceObservationSubtype { + LNDSYN, SHPSYN, BUOY, MOBSYN, OPENROAD, TEMP, BATHY, TESAC, BUOYPROF, LNDSYB, SHPSYB + }; + struct SurfaceObservationSubtypeParameterTraitsHelper { + typedef SurfaceObservationSubtype EnumType; + static constexpr char enumTypeName[] = "SurfaceObservationSubtype"; + static constexpr util::NamedEnumerator namedValues[] = { + { SurfaceObservationSubtype::LNDSYN, "LNDSYN" }, + { SurfaceObservationSubtype::SHPSYN, "SHPSYN" }, + { SurfaceObservationSubtype::BUOY, "BUOY" }, + { SurfaceObservationSubtype::MOBSYN, "MOBSYN" }, + { SurfaceObservationSubtype::OPENROAD, "OPENROAD" }, + { SurfaceObservationSubtype::TEMP, "TEMP" }, + { SurfaceObservationSubtype::BATHY, "BATHY" }, + { SurfaceObservationSubtype::TESAC, "TESAC" }, + { SurfaceObservationSubtype::BUOYPROF, "BUOYPROF" }, + { SurfaceObservationSubtype::LNDSYB, "LNDSYB" }, + { SurfaceObservationSubtype::SHPSYB, "SHPSYB" } + }; + }; +} // namespace ufo + +namespace oops { + +template<> +struct ParameterTraits : + public EnumParameterTraits +{}; + +} // namespace oops + namespace ufo { /// \brief Options controlling the operation of the track check filter. -class TrackCheckUtilsParameters : public oops::Parameters { - // TODO(wsmigaj/aweinbren): Refactor TrackCheckUtils::groupObservationsByStation to avoid creating - // a TrackCheckUtilsParameters object and then replace OOPS_CONCRETE_PARAMETERS with - // OOPS_ABSTRACT_PARAMETERS. - OOPS_CONCRETE_PARAMETERS(TrackCheckUtilsParameters, Parameters) +class TrackCheckUtilsParameters : public FilterParametersBase { + OOPS_ABSTRACT_PARAMETERS(TrackCheckUtilsParameters, FilterParametersBase) public: /// Variable storing integer-valued or string-valued station IDs. @@ -35,7 +65,7 @@ class TrackCheckUtilsParameters : public oops::Parameters { /// assumed to have been taken by a single station. /// /// Note: the variable used to group observations into records can be set with the - /// \c obs space.obsdatain.obsgrouping.group_variable YAML option. + /// \c obs space.obsdatain.obsgrouping.groupvariable YAML option. oops::OptionalParameter stationIdVariable{ "station_id_variable", this}; }; diff --git a/src/ufo/filters/Variable.cc b/src/ufo/filters/Variable.cc index eaeee6f17..11c7597d1 100644 --- a/src/ufo/filters/Variable.cc +++ b/src/ufo/filters/Variable.cc @@ -32,8 +32,7 @@ Variable::Variable(const eckit::Configuration & conf) : varname_(), grpname_(), channels_(), options_(conf.getSubConfiguration("options")) { oops::Log::trace() << "ufo::Variable(config) start " << conf << std::endl; - std::string fullname; - conf.get("name", fullname); + std::string fullname = conf.getString("name"); splitVarGroup(fullname, varname_, grpname_); // read channels if available if (conf.has("channels")) { diff --git a/src/ufo/filters/VariableAssignment.cc b/src/ufo/filters/VariableAssignment.cc new file mode 100644 index 000000000..1dbf7790c --- /dev/null +++ b/src/ufo/filters/VariableAssignment.cc @@ -0,0 +1,270 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/VariableAssignment.h" + +#include +#include +#include +#include + +#include + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" + +#include "oops/util/IntSetParser.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" + +#include "ufo/filters/processWhere.h" +#include "ufo/utils/StringUtils.h" + +namespace ufo { + +namespace { + +/// Convert \p valueAsString to `VariableType` and for each vector in \p values assign that value +/// at locations selected by the `where` statement. +template +void assignValue(const std::string &valueAsString, + const std::vector &apply, + ioda::ObsDataVector &values) { + VariableType newValue; + if (!boost::conversion::try_lexical_convert(valueAsString, newValue)) + throw eckit::BadCast("Value '" + valueAsString + + "' could not be converted to the required type", Here()); + + for (size_t ival = 0; ival < values.nvars(); ++ival) { + std::vector ¤tValues = values[ival]; + for (size_t iloc = 0; iloc < apply.size(); ++iloc) + if (apply[iloc]) + currentValues[iloc] = newValue; + } +} + +/// Evaluate the ObsFunction \p function and assign the vectors it produced to successive vectors +/// in \p values (only at locations selected by the `where` statement). +template +void assignFunction(const ufo::Variable &function, + const ufo::Variable &variable, + const std::vector &apply, + const ObsFilterData &data, + ioda::ObsDataVector &values) { + ioda::ObsDataVector newValues(data.obsspace(), variable.toOopsVariables()); + data.get(function, newValues); + + const VariableType missing = util::missingValue(VariableType()); + const float missingfloat = util::missingValue(float()); + for (size_t ival = 0; ival < values.nvars(); ++ival) { + std::vector ¤tValues = values[ival]; + const ioda::ObsDataRow ¤tNewValues = newValues[ival]; + for (size_t iloc = 0; iloc < apply.size(); ++iloc) { + if (apply[iloc] && currentNewValues[iloc] != missingfloat) + currentValues[iloc] = static_cast(currentNewValues[iloc]); + if (apply[iloc] && currentNewValues[iloc] == missingfloat) + currentValues[iloc] = missing; + } + } +} + +/// Assign values to a numeric variable (of type float or int). +template +void assignNumericValues(const AssignmentParameters ¶ms, + const ufo::Variable &variable, + const std::vector &apply, + const ObsFilterData &data, + ioda::ObsDataVector &values) { + if (params.value_.value() != boost::none) { + assignValue(*params.value_.value(), apply, values); + } else { + ASSERT(params.function.value() != boost::none); + assignFunction(*params.function.value(), variable, apply, data, values); + } +} + +/// Assign values to a non-numeric variable (of type string or DateTime). +template +void assignNonnumericValues(const AssignmentParameters ¶ms, + const std::vector &apply, + ioda::ObsDataVector &values) { + if (params.value_.value() != boost::none) { + assignValue(*params.value_.value(), apply, values); + } else { + ASSERT(params.function.value() != boost::none); + throw eckit::BadValue("ObsFunction values cannot be assigned to non-numeric variables", Here()); + } +} + +/// Retrieve and return the current values of the variable \p variable from \p obsdb (as +/// vectors). Variables that aren't currently stored in \p obsdb are treated as if they consisted +/// entirely of missing values. +template +ioda::ObsDataVector getCurrentValues(const ufo::Variable &variable, + ioda::ObsSpace &obsdb) { + ioda::ObsDataVector values(obsdb, variable.toOopsVariables()); + for (size_t ich = 0; ich < variable.size(); ++ich) { + const std::string variableWithChannel = variable.variable(ich); + if (obsdb.has(variable.group(), variableWithChannel)) { + // Variable exists -- retrieve its values from the ObsSpace + obsdb.get_db(variable.group(), variableWithChannel, values[ich]); + } else { + // Variable doesn't exist yet -- fill the vector with missing values + values[ich].assign(obsdb.nlocs(), util::missingValue(VariableType())); + } + } + return values; +} + +/// Save the values \p values of variable \p variable to \p obsdb. +template +void saveValues(const ufo::Variable &variable, + const ioda::ObsDataVector &values, + ioda::ObsSpace &obsdb) { + for (size_t ich = 0; ich < variable.size(); ++ich) + obsdb.put_db(variable.group(), variable.variable(ich), values[ich]); +} + +/// Retrieve the current values of a numeric variable \p variable from \p obsdb (or if it doesn't +/// already exist, fill it with missing values), assign new values to elements selected by the +/// `where` clause and save the results to \p obsdb. +template +void assignToNumericVariable(const ufo::Variable &variable, + const AssignmentParameters ¶ms, + const std::vector &apply, + const ObsFilterData &data, + ioda::ObsSpace &obsdb) { + ioda::ObsDataVector values = getCurrentValues(variable, obsdb); + assignNumericValues(params, variable, apply, data, values); + saveValues(variable, values, obsdb); +} + +/// Retrieve the current values of a non-numeric variable \p variable from \p obsdb (or if it +/// doesn't already exist, fill it with missing values), assign new values to elements selected by +/// the `where` clause and save the results to \p obsdb. +template +void assignToNonnumericVariable(const ufo::Variable &variable, + const AssignmentParameters ¶ms, + const std::vector &apply, + ioda::ObsSpace &obsdb) { + ioda::ObsDataVector values = getCurrentValues(variable, obsdb); + assignNonnumericValues(params, apply, values); + saveValues(variable, values, obsdb); +} + +/// Delegate work to an appropriate function depending on whether \p dtype is a numeric +/// or non-numeric type. +void assignToVariable(const ufo::Variable &variable, + ioda::ObsDtype dtype, + const AssignmentParameters ¶ms, + const std::vector &apply, + const ObsFilterData &data, + ioda::ObsSpace &obsdb) { + switch (dtype) { + case ioda::ObsDtype::Float: + assignToNumericVariable(variable, params, apply, data, obsdb); + break; + case ioda::ObsDtype::Integer: + assignToNumericVariable(variable, params, apply, data, obsdb); + break; + case ioda::ObsDtype::String: + assignToNonnumericVariable(variable, params, apply, obsdb); + break; + case ioda::ObsDtype::DateTime: + assignToNonnumericVariable(variable, params, apply, obsdb); + break; + default: + ASSERT_MSG(false, "Unrecognized data type"); + } +} + +/// Return the variable to which new values will be assigned. +ufo::Variable getVariable(const AssignmentParameters ¶ms) { + const std::set setChannels = oops::parseIntSet(params.channels); + std::vector vecChannels(setChannels.begin(), setChannels.end()); + const ufo::Variable variable(params.name, vecChannels); + if (variable.group() == "ObsValue") + throw eckit::BadValue("Assignment to variables from the ObsValue group is not allowed", + Here()); + return variable; +} + +/// Return the data type of the variable to which new values will be assigned. +ioda::ObsDtype getDataType(boost::optional dtypeParam, + const ufo::Variable &variable, + const ioda::ObsSpace &obsdb) { + if (dtypeParam != boost::none) { + // If the dtype option has been set, return its value. + return *dtypeParam; + } else { + // Otherwise, check if the variable to which new values should be assigned already + // exists and if so, return its data type. + for (size_t ich = 0; ich < variable.size(); ++ich) { + const std::string variableWithChannel = variable.variable(ich); + if (obsdb.has(variable.group(), variableWithChannel)) + return obsdb.dtype(variable.group(), variableWithChannel); + } + // The variable doesn't exist yet. + throw eckit::BadParameter("You need to specify the type of the variable to be created " + "by setting the 'type' option of the filter to 'float', 'int', " + "'string' or 'datetime'."); + } +} + +} // namespace + + +void AssignmentParameters::deserialize(util::CompositePath &path, + const eckit::Configuration &config) { + oops::Parameters::deserialize(path, config); + + // These checks should really be done at the validation stage (using JSON Schema), + // but this isn't supported yet, so this is better than nothing. + if ((value_.value() == boost::none && function.value() == boost::none) || + (value_.value() != boost::none && function.value() != boost::none)) + throw eckit::UserError(path.path() + + ": Exactly one of the 'value' and 'function' options must be present"); +} + + +VariableAssignment::VariableAssignment(ioda::ObsSpace & obsdb, const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : ObsProcessorBase(obsdb, parameters.deferToPost, std::move(flags), std::move(obserr)), + parameters_(parameters) +{ + oops::Log::debug() << "VariableAssignment: config = " << parameters_ << std::endl; + allvars_ += getAllWhereVariables(parameters.where); + + for (const AssignmentParameters &assignment : parameters.assignments.value()) { + if (assignment.function.value() != boost::none) { + allvars_ += *assignment.function.value(); + } + } +} + +void VariableAssignment::doFilter() const { + oops::Log::trace() << "VariableAssignment doFilter begin" << std::endl; + + // Select locations at which the filter will be applied + const std::vector apply = processWhere(parameters_.where, data_); + + // Assign values to successive sets of variables + for (const AssignmentParameters &assignment : parameters_.assignments.value()) { + const ufo::Variable variable = getVariable(assignment); + const ioda::ObsDtype dtype = getDataType(assignment.type, variable, obsdb_); + assignToVariable(variable, dtype, assignment, apply, data_, obsdb_); + } + + oops::Log::trace() << "VariableAssignment doFilter end" << std::endl; +} + +void VariableAssignment::print(std::ostream & os) const { + os << "VariableAssignment: config = " << parameters_ << std::endl; +} + +} // namespace ufo diff --git a/src/ufo/filters/VariableAssignment.h b/src/ufo/filters/VariableAssignment.h new file mode 100644 index 000000000..4d87f462b --- /dev/null +++ b/src/ufo/filters/VariableAssignment.h @@ -0,0 +1,152 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_VARIABLEASSIGNMENT_H_ +#define UFO_FILTERS_VARIABLEASSIGNMENT_H_ + +#include +#include +#include +#include + +#include "eckit/config/LocalConfiguration.h" +#include "ioda/core/ParameterTraitsObsDtype.h" +#include "oops/base/ObsFilterParametersBase.h" +#include "oops/util/ObjectCounter.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "ufo/filters/ObsProcessorBase.h" +#include "ufo/filters/processWhere.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +/// Parameters controlling assignment of new values to a variable. +class AssignmentParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(AssignmentParameters, Parameters) + + public: + /// Name of the variable to which new values should be assigned. + oops::RequiredParameter name{"name", this}; + + /// Set of channels to which new values should be assigned. + oops::Parameter channels{"channels", "", this}; + + /// Value to be assigned to the specified variable (at all locations selected be the `where` + /// statement, if present). + /// + /// Exactly one of the `value` and `function` options must be given. + oops::OptionalParameter value_{"value", this}; + + /// Variable (typically an ObsFunction) that should be evaluated and assigned to the specified + /// variable (at all locations selected be the `where` statement, if present). + /// + /// Exactly one of the `value` and `function` options must be given. + oops::OptionalParameter function{"function", this}; + + /// Type (int, float, string or datetime) of the variable to which new values should be assigned. + /// + /// This option must be provided if the variable doesn't exist yet. If this option is provided + /// and the variable already exists, its type must match the value of this option, + /// otherwise an exception will be thrown. + oops::OptionalParameter type{"type", this}; + + /// This function is overridden to check that mutually exclusive options aren't specified + /// together, throwing an exception otherwise. + void deserialize(util::CompositePath &path, const eckit::Configuration &config) override; +}; + +/// Parameters controlling the VariableAssignment filter. +class VariableAssignmentParameters : public oops::ObsFilterParametersBase { + OOPS_CONCRETE_PARAMETERS(VariableAssignmentParameters, ObsFilterParametersBase) + + public: + /// One or more sets of options controlling the values assigned to a particular variable. + oops::Parameter> assignments{"assignments", {}, this}; + + /// Conditions used to select locations where variable assignment should be performed. + /// If not specified, variable assignment will be performed at all locations. + oops::Parameter> where{"where", {}, this}; + + /// If set to true, variable assignment will be done after the obs operator has been invoked + /// (even if the filter doesn't require any variables from the GeoVaLs or HofX groups). + oops::Parameter deferToPost{"defer to post", false, this}; +}; + +/// \brief Assigns specified values to elements of specified variables selected by the where +/// statement. +/// +/// The values can be constants or vectors generated by ObsFunctions. If the variables don't exist +/// yet, they will be created; in this case elements not selected by the where clause will be +/// initialized with the missing value markers. +/// +/// Example 1: Create new variables `air_temperature@GrossErrorProbability` and +/// `relative_humidity@GrossErrorProbability` and set them to 0.1 at all locations. +/// +/// filter: Variable Assignment +/// assignments: +/// - name: air_temperature@GrossErrorProbability +/// type: float # type must be specified if the variable doesn't already exist +/// value: 0.1 +/// - name: relative_humidity@GrossErrorProbability +/// type: float +/// value: 0.1 +/// +/// Example 2: Set `air_temperature@GrossErrorProbability` to 0.05 at all locations in the tropics. +/// +/// filter: Variable Assignment +/// where: +/// - variable: +/// name: latitude@MetaData +/// minvalue: -30 +/// maxvalue: 30 +/// assignments: +/// - name: air_temperature@GrossErrorProbability +/// value: 0.05 +/// +/// Example 3: Set `relative_humidity@GrossErrorProbability` to values computed by an ObsFunction +/// (0.1 in the southern extratropics and 0.05 in the northern extratropics, with a linear +/// transition in between). +/// +/// filter: Variable Assignment +/// assignments: +/// - name: relative_humidity@GrossErrorProbability +/// function: +/// name: ObsErrorModelRamp@ObsFunction +/// options: +/// xvar: +/// name: latitude@MetaData +/// x0: [-30] +/// x1: [30] +/// err0: [0.1] +/// err1: [0.05] +/// +class VariableAssignment : public ObsProcessorBase, + private util::ObjectCounter { + public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef VariableAssignmentParameters Parameters_; + + static const std::string classname() {return "ufo::VariableAssignment";} + + VariableAssignment(ioda::ObsSpace & obsdb, const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr); + + private: + void print(std::ostream &) const override; + void doFilter() const override; + + Parameters_ parameters_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_VARIABLEASSIGNMENT_H_ diff --git a/src/ufo/filters/VariableTransforms.cc b/src/ufo/filters/VariableTransforms.cc new file mode 100644 index 000000000..63278f29d --- /dev/null +++ b/src/ufo/filters/VariableTransforms.cc @@ -0,0 +1,75 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include +#include +#include +#include + +#include "eckit/config/Configuration.h" + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" + +#include "oops/interface/ObsFilter.h" +#include "oops/util/abor1_cpp.h" +#include "oops/util/Logger.h" + +#include "ufo/filters/VariableTransforms.h" +#include "ufo/filters/VariableTransformsParameters.h" +#include "ufo/variabletransforms/TransformBase.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- + +VariableTransforms::VariableTransforms( + ioda::ObsSpace& obsdb, const eckit::Configuration& config, + std::shared_ptr> flags, + std::shared_ptr> obserr) + : FilterBase(obsdb, config, flags, obserr) +{ + options_.reset(new VariableTransformsParameters()); + options_->deserialize(config); + allvars_ += Variables(filtervars_); + + oops::Log::debug() << "VariableTransforms: config = " << config << std::endl; +} + +// ----------------------------------------------------------------------------- + +VariableTransforms::~VariableTransforms() {} + +// ----------------------------------------------------------------------------- + +void VariableTransforms::applyFilter( + const std::vector& apply, const Variables& filtervars, + std::vector>& flagged) const { + print(oops::Log::trace()); + std::cout << " --> In variabletransforms::applyFilter" << std::endl; + std::cout << " --> set Transform object" << std::endl; + + // Run all calculations requested + for (const auto& cal_ : options_->Transform.value()) { + std::cout << " estimate: " << cal_ << std::endl; + std::unique_ptr Transform = + TransformFactory::create(cal_, *options_, obsdb_, flags_); + + Transform->runTransform(); + } +} + +// ----------------------------------------------------------------------------- + +void VariableTransforms::print(std::ostream& os) const { + os << "VariableTransforms: config = " << config_ << std::endl; +} +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/VariableTransforms.h b/src/ufo/filters/VariableTransforms.h new file mode 100644 index 000000000..957d0582d --- /dev/null +++ b/src/ufo/filters/VariableTransforms.h @@ -0,0 +1,68 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_VARIABLETRANSFORMS_H_ +#define UFO_FILTERS_VARIABLETRANSFORMS_H_ + +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/QCflags.h" + +namespace eckit { +class Configuration; +} + +namespace ioda { +template +class ObsDataVector; +class ObsSpace; +} + +namespace ufo { +class VariableTransformsParameters; +} + +namespace ufo { + +/// \brief Main filter to apply some variable convertion. +/// +/// See variabletransformsParameters for the documentation of the available +/// parameters and options. +/// +/// \par Important: +/// Any new variable created is assigned to the observation space with the +/// "@DerivedValue" tag. +/// +class VariableTransforms : public FilterBase, + private util::ObjectCounter { + public: + static const std::string classname() { return "ufo::VariableTransforms"; } + // This Constructor function initializes an instance of the + // filter based on options specified in the YAML configuration file. + VariableTransforms(ioda::ObsSpace &, const eckit::Configuration &, + std::shared_ptr>, + std::shared_ptr>); + // Destructor + ~VariableTransforms(); + + private: + /// Configurable options + std::unique_ptr options_; + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override { return QCflags::pass; } +}; + +} // namespace ufo + +#endif // UFO_FILTERS_VARIABLETRANSFORMS_H_ diff --git a/src/ufo/filters/VariableTransformsParameters.h b/src/ufo/filters/VariableTransformsParameters.h new file mode 100644 index 000000000..f41876568 --- /dev/null +++ b/src/ufo/filters/VariableTransformsParameters.h @@ -0,0 +1,76 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_VARIABLETRANSFORMSPARAMETERS_H_ +#define UFO_FILTERS_VARIABLETRANSFORMSPARAMETERS_H_ + +#include +#include + +#include "eckit/exception/Exceptions.h" + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/utils/Constants.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace eckit { +class Configuration; +} + +namespace ufo { + +/// \brief Options controlling the operation of the variablestansform filter. +class VariableTransformsParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(VariableTransformsParameters, Parameters) + + public: // variables + //=== Generic parameters ===// + + /// List of possible transformatioins: + /// - Pressure from height: + /// - \e "PressureFromHeightForProfile":\n + /// Retrieve pressure from observation height. + /// This conversion require a vertical profile. See + /// Cal_PressureFromHeightForProfile for details. + /// - \e "PressureFromHeightForICAO": \n + /// Retrieve pressure from observation height using ICAO standard. + /// See Cal_PressureFromHeightForICAO for details. + /// - \e "WindSpeedAndDirection": \n + /// Retrieve wind speed and direction from the eastward(u), and + /// northward (v) wind components. + /// - \e "WindComponents": \n + /// Retrieve the eastward(u), and northward (v) wind components from + /// wind speed and direction + /// - \e "SpecificHumidity": \n + /// Retrieve the specific humidity from relative humidity + /// - \e "RelativeHumidity": \n + /// Retrieve the relative humidity from specific humidity + oops::RequiredParameter> Transform{"Transform", + this}; + + /// Method used for calculation [Optional]: + /// Related to Met Center - See ReadTheDoc for more details. + oops::Parameter Method{"Method", "default", this}; + + /// Formulation possible [Optional]: + /// By default \e formulation is set to \e the Method. + /// See ReadTheDoc for more details + oops::Parameter Formulation{"Formulation", "", this}; + + /// Should we use only the valid data? [Optional]: + /// By default \e UseValidDataOnly is set to \e true. + /// See ReadTheDoc for more details + oops::Parameter UseValidDataOnly{"UseValidDataOnly", true, this}; +}; +} // namespace ufo + +#endif // UFO_FILTERS_VARIABLETRANSFORMSPARAMETERS_H_ + diff --git a/src/ufo/filters/Variables.h b/src/ufo/filters/Variables.h index db99209a9..91eaf98e7 100644 --- a/src/ufo/filters/Variables.h +++ b/src/ufo/filters/Variables.h @@ -34,12 +34,17 @@ class Variables: public util::Printable { Variables & operator+=(const Variables &); Variables & operator+=(const Variable &); + /// \brief Return the number of constituent Variable objects (some of which may contain multiple + /// channels). size_t size() const; + /// \brief Return a given constituent Variable (which may contain multiple channels). const Variable & operator[](const size_t) const; // the below two functions are for compatibility with oops::Variables and should // eventually be removed + /// \brief Return the number of constituent "primitive" (single-channel) variables. size_t nvars() const; + /// \brief Return a given constituent "primitive" (single-channel) variable. Variable variable(const size_t) const; Variables allFromGroup(const std::string &) const; diff --git a/src/ufo/filters/actions/AcceptObs.cc b/src/ufo/filters/actions/AcceptObs.cc new file mode 100644 index 000000000..ceebb2563 --- /dev/null +++ b/src/ufo/filters/actions/AcceptObs.cc @@ -0,0 +1,50 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/actions/AcceptObs.h" + +#include "ioda/ObsDataVector.h" +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/QCflags.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- + +static FilterActionMaker acceptObsMaker_("accept"); + +// ----------------------------------------------------------------------------- + +AcceptObs::AcceptObs(const Parameters_ &) + : allvars_() { +} + +// ----------------------------------------------------------------------------- + +void AcceptObs::apply(const Variables & vars, + const std::vector> & flagged, + const ObsFilterData &, + int /*filterQCflag*/, + ioda::ObsDataVector & flags, + ioda::ObsDataVector &) const { + for (size_t ifiltervar = 0; ifiltervar < vars.nvars(); ++ifiltervar) { + const size_t iallvar = flags.varnames().find(vars.variable(ifiltervar).variable()); + for (size_t jobs = 0; jobs < flags.nlocs(); ++jobs) { + if (flagged[ifiltervar][jobs]) { + int ¤tFlag = flags[iallvar][jobs]; + if (currentFlag != QCflags::missing && + currentFlag != QCflags::preQC && + currentFlag != QCflags::Hfailed) + currentFlag = QCflags::pass; + } + } + } +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/actions/AcceptObs.h b/src/ufo/filters/actions/AcceptObs.h new file mode 100644 index 000000000..ede840ebf --- /dev/null +++ b/src/ufo/filters/actions/AcceptObs.h @@ -0,0 +1,51 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_ACTIONS_ACCEPTOBS_H_ +#define UFO_FILTERS_ACTIONS_ACCEPTOBS_H_ + +#include + +#include "ufo/filters/actions/FilterActionBase.h" +#include "ufo/filters/Variables.h" + +namespace ufo { + +class ObsFilterData; + +// ----------------------------------------------------------------------------- + +class AcceptObsParameters : public FilterActionParametersBase { + OOPS_CONCRETE_PARAMETERS(AcceptObsParameters, FilterActionParametersBase); + // No extra parameters needed +}; + +// ----------------------------------------------------------------------------- + +/// Reset the QC flag of observations flagged by the filter to 'pass' except for those whose current +/// QC flag is 'missing', 'preQC' or 'Hfailed'. +class AcceptObs : public FilterActionBase { + public: + typedef AcceptObsParameters Parameters_; + + explicit AcceptObs(const Parameters_ &); + + void apply(const Variables &, const std::vector> &, + const ObsFilterData &, int, + ioda::ObsDataVector &, ioda::ObsDataVector &) const override; + + const ufo::Variables & requiredVariables() const override {return allvars_;} + + private: + Variables allvars_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_ACTIONS_ACCEPTOBS_H_ diff --git a/src/ufo/filters/actions/AssignError.cc b/src/ufo/filters/actions/AssignError.cc index d5ba97553..5781ebb47 100644 --- a/src/ufo/filters/actions/AssignError.cc +++ b/src/ufo/filters/actions/AssignError.cc @@ -12,11 +12,9 @@ #include "ioda/ObsDataVector.h" #include "oops/base/Variables.h" -#include "oops/util/IntSetParser.h" #include "oops/util/missingValues.h" #include "ufo/filters/ObsFilterData.h" #include "ufo/filters/QCflags.h" -#include "ufo/utils/StringUtils.h" namespace ufo { @@ -26,26 +24,41 @@ static FilterActionMaker makerAssignErr_("assign error"); // ----------------------------------------------------------------------------- -AssignError::AssignError(const eckit::Configuration & conf) - : allvars_(), conf_(conf) { - if (conf_.has("error function")) { - allvars_ += Variable(conf_.getSubConfiguration("error function")); +void AssignErrorParameters::deserialize(util::CompositePath &path, + const eckit::Configuration &config) { + oops::Parameters::deserialize(path, config); + + // These checks should really be done at the validation stage (using JSON Schema), + // but this isn't supported yet, so this is better than nothing. + if ((errorParameter.value() == boost::none && errorFunction.value() == boost::none) || + (errorParameter.value() != boost::none && errorFunction.value() != boost::none)) + throw eckit::UserError(path.path() + + ": Exactly one of the 'error parameter' and 'error function' " + "options must be present"); +} + +// ----------------------------------------------------------------------------- + +AssignError::AssignError(const Parameters_ & parameters) + : allvars_(), parameters_(parameters) { + if (parameters_.errorFunction.value() != boost::none) { + allvars_ += *parameters_.errorFunction.value(); } - ASSERT(conf_.has("error function") || conf_.has("error parameter")); } // ----------------------------------------------------------------------------- void AssignError::apply(const Variables & vars, - const std::vector> &, - const ObsFilterData & data, - ioda::ObsDataVector & flags, - ioda::ObsDataVector & obserr) const { + const std::vector> &, + const ObsFilterData & data, + int /*filterQCflag*/, + ioda::ObsDataVector & flags, + ioda::ObsDataVector & obserr) const { oops::Log::debug() << " AssignError input obserr: " << obserr << std::endl; const float missing = util::missingValue(missing); // If float error is specified - if (conf_.has("error parameter")) { - float error = conf_.getFloat("error parameter"); + if (parameters_.errorParameter.value() != boost::none) { + float error = *parameters_.errorParameter.value(); for (size_t jv = 0; jv < vars.nvars(); ++jv) { size_t iv = obserr.varnames().find(vars.variable(jv).variable()); size_t kv = flags.varnames().find(vars.variable(jv).variable()); @@ -53,12 +66,12 @@ void AssignError::apply(const Variables & vars, if (flags[kv][jobs] == QCflags::pass) obserr[iv][jobs] = error; } } - // If variable is specified - } else if (conf_.has("error function")) { - Variable errorvar(conf_.getSubConfiguration("error function")); + // If variable is specified + } else if (parameters_.errorFunction.value() != boost::none) { + const Variable &errorvar = *parameters_.errorFunction.value(); ASSERT(errorvar.size() == 1 || errorvar.size() == vars.nvars()); ioda::ObsDataVector errors(data.obsspace(), errorvar.toOopsVariables(), - errorvar.group(), false); + errorvar.group(), false); data.get(errorvar, errors); // if assigned error function is 1D variable, apply the same error to all variables diff --git a/src/ufo/filters/actions/AssignError.h b/src/ufo/filters/actions/AssignError.h index 08f1fa196..a8805c5f6 100755 --- a/src/ufo/filters/actions/AssignError.h +++ b/src/ufo/filters/actions/AssignError.h @@ -11,8 +11,11 @@ #include #include -#include "ioda/ObsDataVector.h" +#include "oops/util/parameters/OptionalParameter.h" #include "ufo/filters/actions/FilterActionBase.h" +#include "ufo/filters/Variable.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" namespace ufo { @@ -20,19 +23,36 @@ class ObsFilterData; // ----------------------------------------------------------------------------- +class AssignErrorParameters : public FilterActionParametersBase { + OOPS_CONCRETE_PARAMETERS(AssignErrorParameters, FilterActionParametersBase); + + public: + oops::OptionalParameter errorParameter{"error parameter", this}; + oops::OptionalParameter errorFunction{"error function", this}; + + /// This function is overridden to check that either `error parameter` or `error function` + /// is specified, but not both. + void deserialize(util::CompositePath &path, const eckit::Configuration &config) override; +}; + +// ----------------------------------------------------------------------------- + class AssignError : public FilterActionBase { public: - explicit AssignError(const eckit::Configuration &); + /// The type of parameters accepted by the constructor of this action. + /// This typedef is used by the FilterActionFactory. + typedef AssignErrorParameters Parameters_; + + explicit AssignError(const Parameters_ &); ~AssignError() {} void apply(const Variables &, const std::vector> &, - const ObsFilterData &, + const ObsFilterData &, int, ioda::ObsDataVector &, ioda::ObsDataVector &) const override; const ufo::Variables & requiredVariables() const override {return allvars_;} private: Variables allvars_; - const std::string strerror_; - const eckit::LocalConfiguration conf_; + const Parameters_ parameters_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/actions/FilterAction.cc b/src/ufo/filters/actions/FilterAction.cc index 314e7d9f0..17a47ded3 100644 --- a/src/ufo/filters/actions/FilterAction.cc +++ b/src/ufo/filters/actions/FilterAction.cc @@ -18,8 +18,8 @@ namespace ufo { // ----------------------------------------------------------------------------- -FilterAction::FilterAction(const eckit::Configuration & conf) - : action_(FilterActionFactory::create(conf)) +FilterAction::FilterAction(const FilterActionParametersBase & parameters) + : action_(FilterActionFactory::create(parameters)) {} // ----------------------------------------------------------------------------- @@ -29,9 +29,9 @@ FilterAction::~FilterAction() {} // ----------------------------------------------------------------------------- void FilterAction::apply(const Variables & vars, const std::vector> & mask, - const ObsFilterData & data, - ioda::ObsDataVector & flag, ioda::ObsDataVector & err) const { - action_->apply(vars, mask, data, flag, err); + const ObsFilterData & data, int filterQCflag, + ioda::ObsDataVector & flags, ioda::ObsDataVector & err) const { + action_->apply(vars, mask, data, filterQCflag, flags, err); } // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/actions/FilterAction.h b/src/ufo/filters/actions/FilterAction.h index 9139303bb..ff5ace66d 100644 --- a/src/ufo/filters/actions/FilterAction.h +++ b/src/ufo/filters/actions/FilterAction.h @@ -14,9 +14,7 @@ #include -#include "ioda/ObsDataVector.h" #include "ufo/filters/actions/FilterActionBase.h" -#include "ufo/filters/Variables.h" namespace eckit { class Configuration; @@ -30,13 +28,28 @@ class ObsFilterData; class FilterAction : private boost::noncopyable { public: - explicit FilterAction(const eckit::Configuration &); + explicit FilterAction(const FilterActionParametersBase &); ~FilterAction(); - void apply(const ufo::Variables &, const std::vector> &, - const ObsFilterData &, - ioda::ObsDataVector &, ioda::ObsDataVector &) const; + /// \param vars + /// The list of filter variables. + /// \param flagged + /// If flagged[i][j] is true, it means that the action should be performed on jth observation + /// of ith filter variable. + /// \param data + /// Accessor to obs filter data. + /// \param filterQCflag + /// QC flag identifying observations rejected by the type of filter performing the action. + /// (Relevant only for actions rejecting observations.) + /// \param flags + /// QC flags of all "simulated variables". + /// \param obserr + /// Obs error estimates of all "simulated variables". + void apply(const ufo::Variables &vars, const std::vector> &flagged, + const ObsFilterData &data, int filterQCflag, + ioda::ObsDataVector &flags, ioda::ObsDataVector &obserr) const; virtual const ufo::Variables & requiredVariables() const; + private: std::unique_ptr action_; }; diff --git a/src/ufo/filters/actions/FilterActionBase.cc b/src/ufo/filters/actions/FilterActionBase.cc index d1ca2e6dc..22f0ea18b 100644 --- a/src/ufo/filters/actions/FilterActionBase.cc +++ b/src/ufo/filters/actions/FilterActionBase.cc @@ -27,17 +27,30 @@ FilterActionFactory::FilterActionFactory(const std::string & name) { // ----------------------------------------------------------------------------- -FilterActionBase * FilterActionFactory::create(const eckit::Configuration & conf) { +std::unique_ptr FilterActionFactory::create( + const FilterActionParametersBase & parameters) { oops::Log::trace() << "FilterActionBase::create starting" << std::endl; - const std::string name = conf.getString("name", "reject"); + const std::string &name = parameters.name.value().value(); typename std::map::iterator jloc = getMakers().find(name); if (jloc == getMakers().end()) { oops::Log::error() << name << " does not exist in ufo::FilterActionFactory." << std::endl; ABORT("Element does not exist in ufo::FilterActionFactory."); } - FilterActionBase * ptr = jloc->second->make(conf); + std::unique_ptr action = jloc->second->make(parameters); oops::Log::trace() << "FilterActionBase::create done" << std::endl; - return ptr; + return action; +} + +// ----------------------------------------------------------------------------- + +std::unique_ptr FilterActionFactory::createParameters( + const std::string &name) { + typename std::map::iterator it = + getMakers().find(name); + if (it == getMakers().end()) { + throw std::runtime_error(name + " does not exist in FilterActionFactory"); + } + return it->second->makeParameters(); } // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/actions/FilterActionBase.h b/src/ufo/filters/actions/FilterActionBase.h index 078c1525f..16e81f66c 100644 --- a/src/ufo/filters/actions/FilterActionBase.h +++ b/src/ufo/filters/actions/FilterActionBase.h @@ -9,45 +9,110 @@ #define UFO_FILTERS_ACTIONS_FILTERACTIONBASE_H_ #include +#include #include #include - +#include #include #include "eckit/config/Configuration.h" #include "ioda/ObsDataVector.h" -#include "ufo/filters/Variables.h" +#include "oops/util/AssociativeContainers.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/PolymorphicParameter.h" + +namespace ioda { +template class ObsDataVector; +} // namespace ioda namespace ufo { +class FilterActionFactory; class ObsFilterData; +class Variables; // ----------------------------------------------------------------------------- -/// Base class for computing obs diagnostics +/// Parameters controlling a filter action. +class FilterActionParametersBase : public oops::Parameters { + OOPS_ABSTRACT_PARAMETERS(FilterActionParametersBase, Parameters) + + public: + /// \brief Name of the action to be performed. + /// + /// \note This parameter is marked as optional because it may not be required in certain + /// circumstances, e.g. in tests instantiating a particular FilterAction directly (rather than + /// via the FilterActionFactory). FilterActionFactory will throw an exception if this parameter is + /// not provided. + oops::OptionalParameter name{"name", this}; +}; +// ----------------------------------------------------------------------------- +/// Base class for actions performed on observations flagged by filters. +/// +/// Note: each concrete implementation should typedef `Parameters_` to the name of a subclass of +/// FilterActionParametersBase encapsulating its configuration options. It should also provide +/// a constructor with the following signature: +/// +/// FilterActionBase(const Parameters_ &); class FilterActionBase : private boost::noncopyable { public: FilterActionBase() {} virtual ~FilterActionBase() {} -/// compute the diagnostic - virtual void apply(const Variables &, const std::vector> &, - const ObsFilterData &, - ioda::ObsDataVector &, ioda::ObsDataVector &) const = 0; + /// \param vars + /// The list of filter variables. + /// \param flagged + /// If flagged[i][j] is true, it means that the action should be performed on jth observation + /// of ith filter variable. + /// \param data + /// Accessor to obs filter data. + /// \param filterQCflag + /// QC flag identifying observations rejected by the type of filter performing the action. + /// (Relevant only for actions rejecting observations.) + /// \param flags + /// QC flags of all "simulated variables". + /// \param obserr + /// Obs error estimates of all "simulated variables". + virtual void apply(const ufo::Variables &vars, const std::vector> &flagged, + const ObsFilterData &data, int filterQCflag, + ioda::ObsDataVector &flags, ioda::ObsDataVector &obserr) const = 0; + virtual const Variables & requiredVariables() const = 0; }; // ----------------------------------------------------------------------------- -/// Obs Operator Factory +/// Filter action factory. class FilterActionFactory { public: - static FilterActionBase * create(const eckit::Configuration &); virtual ~FilterActionFactory() = default; + + /// \brief Create and return a new filter action. + /// + /// The action's type is determined by the \c name attribute of \p parameters. + /// \p parameters must be an instance of the subclass of FilterActionParametersBase + /// associated with that action type, otherwise an exception will be thrown. + static std::unique_ptr create(const FilterActionParametersBase ¶meters); + + /// \brief Create and return an instance of the subclass of FilterActionParametersBase + /// storing parameters of actions of the specified type. + static std::unique_ptr createParameters(const std::string &name); + + /// \brief Return the names of all actions that can be created by one of the registered makers. + static std::vector getMakerNames() { + return oops::keys(getMakers()); + } + protected: - explicit FilterActionFactory(const std::string &); + /// \brief Register a maker able to create actions of type \p name. + explicit FilterActionFactory(const std::string &name); + private: - virtual FilterActionBase * make(const eckit::Configuration &) = 0; + virtual std::unique_ptr make(const FilterActionParametersBase &) = 0; + + virtual std::unique_ptr makeParameters() const = 0; + static std::map < std::string, FilterActionFactory * > & getMakers() { static std::map < std::string, FilterActionFactory * > makers_; return makers_; @@ -58,8 +123,17 @@ class FilterActionFactory { template class FilterActionMaker : public FilterActionFactory { - virtual FilterActionBase * make(const eckit::Configuration & conf) - { return new T(conf); } + typedef typename T::Parameters_ Parameters_; + + std::unique_ptr make(const FilterActionParametersBase & parameters) override { + const auto &stronglyTypedParameters = dynamic_cast(parameters); + return boost::make_unique(stronglyTypedParameters); + } + + std::unique_ptr makeParameters() const override { + return boost::make_unique(); + } + public: explicit FilterActionMaker(const std::string & name) : FilterActionFactory(name) {} }; diff --git a/src/ufo/filters/actions/InflateError.cc b/src/ufo/filters/actions/InflateError.cc index a69ea384a..fdd71e351 100644 --- a/src/ufo/filters/actions/InflateError.cc +++ b/src/ufo/filters/actions/InflateError.cc @@ -8,14 +8,12 @@ #include "ufo/filters/actions/InflateError.h" #include -#include #include "ioda/ObsDataVector.h" #include "oops/base/Variables.h" -#include "oops/util/IntSetParser.h" #include "ufo/filters/ObsFilterData.h" #include "ufo/filters/QCflags.h" -#include "ufo/utils/StringUtils.h" +#include "ufo/filters/Variables.h" namespace ufo { @@ -25,60 +23,81 @@ static FilterActionMaker makerInflateErr_("inflate error"); // ----------------------------------------------------------------------------- -InflateError::InflateError(const eckit::Configuration & conf) - : allvars_(), conf_(conf) { - if (conf_.has("inflation variable")) { - allvars_ += Variable(conf_.getSubConfiguration("inflation variable")); +void InflateErrorParameters::deserialize(util::CompositePath &path, + const eckit::Configuration &config) { + oops::Parameters::deserialize(path, config); + + // These checks should really be done at the validation stage (using JSON Schema), + // but this isn't supported yet, so this is better than nothing. + if ((inflationFactor.value() == boost::none && inflationVariable.value() == boost::none) || + (inflationFactor.value() != boost::none && inflationVariable.value() != boost::none)) + throw eckit::UserError(path.path() + + ": Exactly one of the 'inflation factor' and 'inflation variable' " + "options must be present"); +} + +// ----------------------------------------------------------------------------- + +InflateError::InflateError(const Parameters_ & parameters) + : allvars_(), parameters_(parameters) { + if (parameters_.inflationVariable.value() != boost::none) { + allvars_ += *parameters_.inflationVariable.value(); } - ASSERT(conf_.has("inflation variable") || conf_.has("inflation factor")); } // ----------------------------------------------------------------------------- +/// Inflate ObsError by either a constant inflation factor, or by a varying +/// inflation variable. +/// \param vars variables that need to be inflated in the ObsError (filter variables) +/// \param flagged result of "where" statement: which variables/locations need to be +/// updated (has the same variables as \p vars, in the same order) +/// \param data accessor to obs filter data +/// \param flags QC flags (for all "simulated variables") +/// \param obserr ObsError (for all "simulated variables") void InflateError::apply(const Variables & vars, const std::vector> & flagged, const ObsFilterData & data, + int /*filterQCflag*/, ioda::ObsDataVector & flags, ioda::ObsDataVector & obserr) const { oops::Log::debug() << " InflateError input obserr: " << obserr << std::endl; // If float factor is specified - if (conf_.has("inflation factor")) { - float factor = conf_.getFloat("inflation factor"); - for (size_t jv = 0; jv < vars.nvars(); ++jv) { - size_t iv = obserr.varnames().find(vars.variable(jv).variable()); - size_t kv = flags.varnames().find(vars.variable(jv).variable()); + if (parameters_.inflationFactor.value() != boost::none) { + float factor = *parameters_.inflationFactor.value(); + for (size_t ifiltervar = 0; ifiltervar < vars.nvars(); ++ifiltervar) { + size_t iallvar = obserr.varnames().find(vars.variable(ifiltervar).variable()); for (size_t jobs = 0; jobs < obserr.nlocs(); ++jobs) { - if (flagged[iv][jobs] && flags[kv][jobs] == QCflags::pass) { - obserr[iv][jobs] *= factor; + if (flagged[ifiltervar][jobs] && flags[iallvar][jobs] == QCflags::pass) { + obserr[iallvar][jobs] *= factor; } } } // If variable is specified - } else if (conf_.has("inflation variable")) { - Variable factorvar(conf_.getSubConfiguration("inflation variable")); + } else if (parameters_.inflationVariable.value() != boost::none) { + const Variable &factorvar = *parameters_.inflationVariable.value(); ASSERT(factorvar.size() == 1 || factorvar.size() == vars.nvars()); ioda::ObsDataVector factors(data.obsspace(), factorvar.toOopsVariables()); data.get(factorvar, factors); // if inflation factor is 1D variable, apply the same inflation factor to all variables - // factor_jv = {0, 0, 0, ..., 0} for all nvars - std::vector factor_jv(vars.nvars(), 0); + // factor_indices = {0, 0, 0, ..., 0} for all nvars + std::vector factor_indices(vars.nvars(), 0); // if multiple variables are in the inflation factor, apply different factors to different // variables - // factor_jv = {0, 1, 2, ..., nvars-1} + // factor_indices = {0, 1, 2, ..., nvars-1} if (factorvar.size() == vars.nvars()) { - std::iota(factor_jv.begin(), factor_jv.end(), 0); + std::iota(factor_indices.begin(), factor_indices.end(), 0); } // loop over all variables to update - for (size_t jv = 0; jv < vars.nvars(); ++jv) { + for (size_t ifiltervar = 0; ifiltervar < vars.nvars(); ++ifiltervar) { // find current variable index in obserr - size_t iv = obserr.varnames().find(vars.variable(jv).variable()); - size_t kv = flags.varnames().find(vars.variable(jv).variable()); + size_t iallvar = obserr.varnames().find(vars.variable(ifiltervar).variable()); for (size_t jobs = 0; jobs < obserr.nlocs(); ++jobs) { - if (flagged[iv][jobs] && flags[kv][jobs] == QCflags::pass) { - obserr[iv][jobs] *= factors[factor_jv[jv]][jobs]; + if (flagged[ifiltervar][jobs] && flags[iallvar][jobs] == QCflags::pass) { + obserr[iallvar][jobs] *= factors[factor_indices[ifiltervar]][jobs]; } } } diff --git a/src/ufo/filters/actions/InflateError.h b/src/ufo/filters/actions/InflateError.h index 188c78420..ae0693c70 100644 --- a/src/ufo/filters/actions/InflateError.h +++ b/src/ufo/filters/actions/InflateError.h @@ -12,7 +12,11 @@ #include #include "ioda/ObsDataVector.h" +#include "oops/util/parameters/OptionalParameter.h" #include "ufo/filters/actions/FilterActionBase.h" +#include "ufo/filters/Variable.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" namespace ufo { @@ -20,19 +24,45 @@ class ObsFilterData; // ----------------------------------------------------------------------------- +class InflateErrorParameters : public FilterActionParametersBase { + OOPS_CONCRETE_PARAMETERS(InflateErrorParameters, FilterActionParametersBase); + + public: + oops::OptionalParameter inflationFactor{"inflation factor", this}; + oops::OptionalParameter inflationVariable{"inflation variable", this}; + + /// This function is overridden to check that either `inflation factor` or `inflation variable` + /// is specified, but not both. + void deserialize(util::CompositePath &path, const eckit::Configuration &config) override; +}; + +// ----------------------------------------------------------------------------- +/// \brief Observation error inflation action. +/// \details Inflates Observation error for filter variables by: +/// - constant (if "inflation factor" is specified in yaml) +/// - spatially varying filter data (if "inflation variable" is specified in yaml). +/// If inflation variable is the same size as filter variables, inflation is done +/// variable by variable (e.g. inflation variable 1 is used to inflate filter +/// variable 1; inflation variable 2 is used to inflate filter variable 2, etc). +/// If inflation variable is of size 1, the same inflation variable is used for +/// updating all filter variables. class InflateError : public FilterActionBase { public: - explicit InflateError(const eckit::Configuration &); - ~InflateError() {} + /// The type of parameters accepted by the constructor of this action. + /// This typedef is used by the FilterActionFactory. + typedef InflateErrorParameters Parameters_; + + explicit InflateError(const Parameters_ &); void apply(const Variables &, const std::vector> &, - const ObsFilterData &, + const ObsFilterData &, int, ioda::ObsDataVector &, ioda::ObsDataVector &) const override; + const ufo::Variables & requiredVariables() const override {return allvars_;} + private: - Variables allvars_; - const std::string strfactor_; - const eckit::LocalConfiguration conf_; + Variables allvars_; /// variables required to compute inflation + Parameters_ parameters_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/actions/RejectObs.cc b/src/ufo/filters/actions/RejectObs.cc index e4275f1ad..22342a2ea 100644 --- a/src/ufo/filters/actions/RejectObs.cc +++ b/src/ufo/filters/actions/RejectObs.cc @@ -19,8 +19,8 @@ static FilterActionMaker makerRejectObs_("reject"); // ----------------------------------------------------------------------------- -RejectObs::RejectObs(const eckit::Configuration & conf) - : allvars_(), conf_(conf) { +RejectObs::RejectObs(const RejectObsParameters ¶meters) + : allvars_(), parameters_(parameters) { } // ----------------------------------------------------------------------------- @@ -28,13 +28,14 @@ RejectObs::RejectObs(const eckit::Configuration & conf) void RejectObs::apply(const Variables & vars, const std::vector> & flagged, const ObsFilterData &, + int filterQCflag, ioda::ObsDataVector & flags, ioda::ObsDataVector &) const { - int flag = conf_.getInt("flag"); for (size_t jv = 0; jv < vars.nvars(); ++jv) { size_t iv = flags.varnames().find(vars.variable(jv).variable()); for (size_t jobs = 0; jobs < flags.nlocs(); ++jobs) { - if (flagged[jv][jobs] && flags[iv][jobs] == QCflags::pass) flags[iv][jobs] = flag; + if (flagged[jv][jobs] && flags[iv][jobs] == QCflags::pass) + flags[iv][jobs] = filterQCflag; } } } diff --git a/src/ufo/filters/actions/RejectObs.h b/src/ufo/filters/actions/RejectObs.h index ec996a6fd..3a8ed423c 100644 --- a/src/ufo/filters/actions/RejectObs.h +++ b/src/ufo/filters/actions/RejectObs.h @@ -10,8 +10,9 @@ #include -#include "ioda/ObsDataVector.h" +#include "oops/util/parameters/OptionalParameter.h" #include "ufo/filters/actions/FilterActionBase.h" +#include "ufo/filters/Variables.h" namespace ufo { @@ -19,18 +20,31 @@ class ObsFilterData; // ----------------------------------------------------------------------------- +class RejectObsParameters : public FilterActionParametersBase { + OOPS_CONCRETE_PARAMETERS(RejectObsParameters, FilterActionParametersBase); + + // No extra parameters needed. +}; + +// ----------------------------------------------------------------------------- + +/// The default action of a QC filter: reject observations flagged by the filter. class RejectObs : public FilterActionBase { public: - explicit RejectObs(const eckit::Configuration &); + /// The type of parameters accepted by the constructor of this action. + /// This typedef is used by the FilterActionFactory. + typedef RejectObsParameters Parameters_; + + explicit RejectObs(const Parameters_ &); ~RejectObs() {} void apply(const Variables &, const std::vector> &, - const ObsFilterData &, + const ObsFilterData &, int, ioda::ObsDataVector &, ioda::ObsDataVector &) const override; const ufo::Variables & requiredVariables() const override {return allvars_;} private: Variables allvars_; - const eckit::Configuration & conf_; + Parameters_ parameters_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/filters/gnssroonedvarcheck/CMakeLists.txt b/src/ufo/filters/gnssroonedvarcheck/CMakeLists.txt new file mode 100644 index 000000000..740da9e46 --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/CMakeLists.txt @@ -0,0 +1,28 @@ +# (C) Copyright 2017-2020 Met Office. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( gnssroonedvarcheck_files + GNSSROOneDVarCheck.h + GNSSROOneDVarCheck.cc + GNSSROOneDVarCheck.interface.F90 + GNSSROOneDVarCheck.interface.h + ufo_gnssroonedvarcheck_do1dvar_mod.f90 + ufo_gnssroonedvarcheck_eval_derivs_mod.f90 + ufo_gnssroonedvarcheck_get_bmatrix_mod.f90 + ufo_gnssroonedvarcheck_humidcheck_mod.f90 + ufo_gnssroonedvarcheck_mod.f90 + ufo_gnssroonedvarcheck_pen_mod.f90 + ufo_gnssroonedvarcheck_rootsolv_mod.f90 + ufo_gnssroonedvarcheck_setom1_mod.f90 + ufo_gnssroonedvarcheck_utils_mod.f90 +) + +PREPEND( _p_gnssroonedvarcheck_files "gnssroonedvarcheck" ${gnssroonedvarcheck_files} ) + +set ( gnssroonedvarcheck_src_files + ${_p_gnssroonedvarcheck_files} + PARENT_SCOPE +) + diff --git a/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.cc b/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.cc new file mode 100644 index 000000000..bf2098b2b --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.cc @@ -0,0 +1,91 @@ +/* + * (C) Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +/* 1-D var qc + * J(x) = (x-xb)T B-1 (x-xb) + (y-H(x))T R-1 (y-H(x)) + * Code adapted from Met Office OPS System + */ + +#include +#include +#include +#include +#include + +#include "ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.h" +#include "ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.interface.h" +#include "ufo/GeoVaLs.h" + +#include "eckit/config/Configuration.h" + +#include "oops/util/IntSetParser.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- + +GNSSROOneDVarCheck::GNSSROOneDVarCheck(ioda::ObsSpace & obsdb, const eckit::Configuration & config, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, config, flags, obserr), config_(config) +{ + oops::Log::debug() << "GNSSROOneDVarCheck contructor starting" << std::endl; + + // Setup fortran object + const eckit::Configuration * conf = &config_; + ufo_gnssroonedvarcheck_create_f90(key_, obsdb, conf, GNSSROOneDVarCheck::qcFlag()); + + oops::Log::debug() << "GNSSROOneDVarCheck contructor complete. " << std::endl; +} + +// ----------------------------------------------------------------------------- + +GNSSROOneDVarCheck::~GNSSROOneDVarCheck() { + ufo_gnssroonedvarcheck_delete_f90(key_); + oops::Log::trace() << "GNSSROOneDVarCheck destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void GNSSROOneDVarCheck::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> & flagged) const { + oops::Log::trace() << "GNSSROOneDVarCheck Filter starting" << std::endl; + + // Get GeoVaLs + const ufo::GeoVaLs * gvals = data_.getGeoVaLs(); + + // Convert apply to char for passing to fortran + std::vector apply_char(apply.size(), 'F'); + for (size_t i = 0; i < apply_char.size(); i++) { + if (apply[i]) {apply_char[i]='T';} + } + + // Save qc flags to database for retrieval in fortran - needed for channel selection + flags_->save("FortranQC"); // temporary measure as per ROobserror qc + + // Pass it all to fortran + const eckit::Configuration * conf = &config_; + ufo_gnssroonedvarcheck_apply_f90(key_, + gvals->toFortran(), + apply_char.size(), apply_char[0]); + + // Read qc flags from database + flags_->read("FortranQC"); // temporary measure as per ROobserror qc + + oops::Log::trace() << "GNSSROOneDVarCheck Filter complete" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void GNSSROOneDVarCheck::print(std::ostream & os) const { + os << "GNSSROOneDVarCheck::print not yet implemented "; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.h b/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.h new file mode 100644 index 000000000..a2c697392 --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.h @@ -0,0 +1,70 @@ +/* + * (C) Copyright 2017-2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_GNSSROONEDVARCHECK_GNSSROONEDVARCHECK_H_ +#define UFO_FILTERS_GNSSROONEDVARCHECK_GNSSROONEDVARCHECK_H_ + +#include +#include +#include +#include +#include + +#include "boost/shared_ptr.hpp" + +#include "oops/util/ObjectCounter.h" +#include "oops/util/Printable.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.interface.h" +#include "ufo/filters/QCflags.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} + +namespace ufo { + +//! \brief GNSSROOneDVarCheck +//! +//! \details GNSSROOneDVarCheck performs a 1D-Var minimization for satellite using the Met +//! Office's GNSS-RO forward operator. If a profile does not converge the all observations +//! in this profile are flagged. The code is based on the Met Office 1D-Var scheme and thus +//! is predominently in Fortran. +//! +//! \author Met Office +//! +//! \date 16/11/2020 +//! + +class GNSSROOneDVarCheck : public FilterBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::GNSSROOneDVarCheck";} + + GNSSROOneDVarCheck(ioda::ObsSpace &, const eckit::Configuration &, + std::shared_ptr >, + std::shared_ptr >); + ~GNSSROOneDVarCheck(); + + private: + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override {return QCflags::onedvar;} + + F90onedvarcheck key_; + const eckit::LocalConfiguration config_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_GNSSROONEDVARCHECK_GNSSROONEDVARCHECK_H_ diff --git a/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.interface.F90 b/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.interface.F90 new file mode 100644 index 000000000..dcf0fbac7 --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.interface.F90 @@ -0,0 +1,123 @@ + +! (C) Copyright 2017-2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +module ufo_gnssroonedvarcheck_mod_c + +use fckit_configuration_module, only: fckit_configuration +use iso_c_binding +use oops_variables_mod +use ufo_geovals_mod +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_gnssroonedvarcheck_mod + +implicit none +private + +#define LISTED_TYPE ufo_gnssroonedvarcheck + +!> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + +!> Global registry +type(registry_t) :: ufo_gnssroonedvarcheck_registry + +! ------------------------------------------------------------------------------------------------ +contains +! ------------------------------------------------------------------------------------------------ +!> Linked list implementation +#include "oops/util/linkedList_c.f" +! ------------------------------------------------------------------------------------------------ + +subroutine ufo_gnssroonedvarcheck_create_c(c_self, c_obspace, c_conf, c_onedvarflag) & + bind(c,name='ufo_gnssroonedvarcheck_create_f90') + +!> \brief Interface to the Fortran create method +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +implicit none +integer(c_int), intent(inout) :: c_self !< self - inout +type(c_ptr), value, intent(in) :: c_obspace !< obsspace - input +type(c_ptr), value, intent(in) :: c_conf !< yaml configuration - input +integer(c_int), intent(in) :: c_onedvarflag !< flag for qc manager logging - input + +type(ufo_gnssroonedvarcheck), pointer :: self +type(fckit_configuration) :: f_conf + +call ufo_gnssroonedvarcheck_registry%setup(c_self, self) +f_conf = fckit_configuration(c_conf) + +call ufo_gnssroonedvarcheck_create(self, c_obspace, f_conf, c_onedvarflag) + +end subroutine ufo_gnssroonedvarcheck_create_c + +! ------------------------------------------------------------------------------------------------ + +subroutine ufo_gnssroonedvarcheck_delete_c(c_self) & + bind(c,name='ufo_gnssroonedvarcheck_delete_f90') + +!> \brief Interface to the Fortran delete method +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! + +implicit none +integer(c_int), intent(inout) :: c_self !< self - input + +type(ufo_gnssroonedvarcheck), pointer :: self + +call ufo_gnssroonedvarcheck_registry%get(c_self, self) +call ufo_gnssroonedvarcheck_delete(self) +call ufo_gnssroonedvarcheck_registry%delete(c_self, self) + +end subroutine ufo_gnssroonedvarcheck_delete_c + +! ------------------------------------------------------------------------------------------------ + +subroutine ufo_gnssroonedvarcheck_apply_c(c_self, c_geovals, c_nobs, c_apply) & + bind(c,name='ufo_gnssroonedvarcheck_apply_f90') + +!> \brief Interface to filter apply method +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! + +implicit none +integer(c_int), intent(in) :: c_self !< self - input +integer(c_int), intent(in) :: c_geovals !< Geovals - input +integer(c_int), intent(in) :: c_nobs !< number of observations - input +character(c_char), intent(in) :: c_apply(c_nobs) !< apply flag (converted to logical) - input + +type(ufo_gnssroonedvarcheck), pointer :: self +type(oops_variables) :: vars +type(oops_variables) :: retrieval_vars +type(ufo_geovals), pointer :: geovals +integer :: ii +logical :: apply(c_nobs) + +call ufo_gnssroonedvarcheck_registry%get(c_self, self) +call ufo_geovals_registry%get(c_geovals, geovals) + +! Convert character to logical for passing to Fortran +apply(:) = .false. +where (c_apply == 'T') + apply = .true. +end where + +call ufo_gnssroonedvarcheck_apply(self, geovals, apply) + +end subroutine ufo_gnssroonedvarcheck_apply_c + +! ------------------------------------------------------------------------------------------------ + +end module ufo_gnssroonedvarcheck_mod_c + diff --git a/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.interface.h b/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.interface.h new file mode 100644 index 000000000..bdb5094ef --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.interface.h @@ -0,0 +1,38 @@ +/* + * (C) Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_GNSSROONEDVARCHECK_GNSSROONEDVARCHECK_INTERFACE_H_ +#define UFO_FILTERS_GNSSROONEDVARCHECK_GNSSROONEDVARCHECK_INTERFACE_H_ + +#include "../../Fortran.h" + +// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + +typedef int F90onedvarcheck; + +/// Interface to Fortran routines + +extern "C" { + void ufo_gnssroonedvarcheck_create_f90(F90onedvarcheck &, const ioda::ObsSpace &, + const eckit::Configuration *, const int &); + void ufo_gnssroonedvarcheck_delete_f90(F90onedvarcheck &); + void ufo_gnssroonedvarcheck_apply_f90(const F90onedvarcheck &, const F90goms &, const int &, + const char &); +} // extern C + +} // namespace ufo + +#endif // UFO_FILTERS_GNSSROONEDVARCHECK_GNSSROONEDVARCHECK_INTERFACE_H_ diff --git a/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_do1dvar_mod.f90 b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_do1dvar_mod.f90 new file mode 100644 index 000000000..0f0756a6a --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_do1dvar_mod.f90 @@ -0,0 +1,365 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +!> Fortran module for gnssro bending angle Met Office forward operator + +module ufo_gnssroonedvarcheck_do1dvar_mod + +use kinds, only: kind_real +use missing_values_mod, only: missing_value +use fckit_log_module, only: fckit_log + +private +public :: Ops_GPSRO_Do1DVar_BA + +contains + +!------------------------------------------------------------------------------- +! Find a solution to the satellite sounding inverse problem +!------------------------------------------------------------------------------- +SUBROUTINE Ops_GPSRO_Do1DVar_BA (nlevp, & + nlevq, & + BM1, & + Bsig, & + Back, & + Ob, & + GPSRO_pseudo_ops, & + GPSRO_vert_interp_ops, & + GPSRO_min_temp_grad, & + GPSRO_Zmin, & + GPSRO_Zmax, & + GPSRO_cost_funct_test, & ! Threshold value for the cost function convergence test + GPSRO_y_test, & ! Threshold value for the yobs-ysol tes + GPSRO_n_iteration_test, & ! Maximum number of iterations + GPSRO_Delta_factor, & ! Delta + GPSRO_Delta_ct2, & ! Delta observations + GPSRO_OB_test, & ! Threshold value for the O-B test + capsupersat, & + BAerr, & + Tb, & + Ts, & + O_Bdiff, & + DFS) + +use ufo_gnssroonedvarcheck_utils_mod, only: & + singlebg_type, & + singleob_type + +use ufo_gnssroonedvarcheck_rootsolv_mod, only: & + Ops_GPSRO_rootsolv_BA + +use ufo_utils_refractivity_calculator, only: & + ufo_calculate_refractivity + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: nlevp +INTEGER, INTENT(IN) :: nlevq +REAL(kind_real), INTENT(IN) :: BM1(:,:) +REAL(kind_real), INTENT(IN) :: Bsig(:) +TYPE (SingleBg_type), INTENT(INOUT) :: Back +TYPE (SingleOb_type), INTENT(INOUT) :: Ob +LOGICAL, INTENT(IN) :: GPSRO_pseudo_ops +LOGICAL, INTENT(IN) :: GPSRO_vert_interp_ops +REAL(kind_real), INTENT(IN) :: GPSRO_min_temp_grad +REAL(kind_real), INTENT(IN) :: GPSRO_Zmin +REAL(kind_real), INTENT(IN) :: GPSRO_Zmax +REAL(kind_real), INTENT(IN) :: GPSRO_cost_funct_test +REAL(kind_real), INTENT(IN) :: GPSRO_y_test +INTEGER, INTENT(IN) :: GPSRO_n_iteration_test +REAL(kind_real), INTENT(IN) :: GPSRO_Delta_ct2 +REAL(kind_real), INTENT(IN) :: GPSRO_Delta_factor +REAL(kind_real), INTENT(IN) :: GPSRO_OB_test +LOGICAL, INTENT(IN) :: capsupersat +LOGICAL, INTENT(OUT) :: BAerr +REAL(kind_real), INTENT(INOUT) :: Tb(nlevq) +REAL(kind_real), INTENT(INOUT) :: Ts(nlevq) +REAL(kind_real), INTENT(INOUT) :: O_Bdiff ! measure of O-B for whole profile +REAL(kind_real), INTENT(INOUT) :: DFS ! measure of degrees of freedom of signal for whole profile + +! Local parameters +CHARACTER(len=*), PARAMETER :: RoutineName = "Ops_GPSRO_Do1DVar_BA" +INTEGER, PARAMETER :: max_string = 800 + +! Local variables +INTEGER :: nobs ! size of the 1DVar observation vector +INTEGER :: i +INTEGER :: j +INTEGER :: it +LOGICAL :: OM1_error +LOGICAL :: converged +LOGICAL :: Do1DVar_error +LOGICAL :: ran_iteration +REAL(kind_real) :: J_pen +REAL(kind_real) :: xb(nlevp+nlevq) ! background profile used in the 1D-Var +REAL(kind_real) :: x(nlevp+nlevq) ! 1Dvar solution profile +REAL(kind_real) :: Amat(nlevp+nlevq,nlevp+nlevq) ! solultion error cov matrix +REAL(kind_real), ALLOCATABLE :: zobs(:) +REAL(kind_real), ALLOCATABLE :: yobs(:) +REAL(kind_real), ALLOCATABLE :: yb(:) +REAL(kind_real), ALLOCATABLE :: ycalc(:) +REAL(kind_real), ALLOCATABLE :: Om1(:,:) +INTEGER, ALLOCATABLE :: index_packed(:) +REAL(kind_real), ALLOCATABLE :: model_heights(:) ! Heights of model and pseudo-levels +REAL(kind_real), ALLOCATABLE :: refractivity(:) ! Refractivity on model and pseudo_levels +INTEGER :: nRefLevels ! Number of levs to calculate ref on +CHARACTER(LEN=max_string) :: message + +REAL(kind_real) :: temp_rad_curv ! Temporary store of the earth's radius of curvature +REAL(kind_real) :: temp_latitude ! Temporary store of the observation's latitude +REAL(kind_real) :: temp_undulation ! Temporary store of the undulation + +!-------------- +! 1. Initialise +!-------------- + +Do1DVar_error = .FALSE. +BAerr = .FALSE. +ran_iteration = .FALSE. +OM1_error = .FALSE. + +! Set all the PGE values to gross error + +Ob % BendingAngle(:) % PGEFinal = 1.0 + +! Calculate refractivity on theta levels, to find appropriate +! impact height vertical range + +CALL ufo_calculate_refractivity (nlevp, & + nlevq, & + Back % za, & + Back % zb, & + Back % p, & + Back % q, & + GPSRO_pseudo_ops, & + GPSRO_vert_interp_ops, & + GPSRO_min_temp_grad, & + BAerr, & + nRefLevels, & + refractivity, & + model_heights) + +! Set the background vector +xb(1:nlevp) = 1.0E-2 * Back % p(:) ! in hPa +xb(nlevp + 1:nlevp+nlevq) = 1.0E3 * Back % q(:) ! in g/kg + +! Set size of obs vector used in 1D- Var + +nobs = COUNT (Ob % BendingAngle(:) % value /= missing_value(Ob % BendingAngle(1) % value) .AND. & ! not missing bending angle + Ob % ImpactParam(:) % value /= missing_value(Ob % ImpactParam(1) % value) .AND. & ! not missing impact parameter + Ob % qc_flags(:) == 0) + +WRITE (message, '(A,I0)') 'size of input obs vector ', SIZE (Ob % BendingAngle(:) % value) +CALL fckit_log % info(message) +WRITE (message, '(A,I0)') 'size of packed obs vector ', nobs +CALL fckit_log % info(message) + +! Only continue if we have some observations to process +IF (nobs > 0) THEN + + ! calculate an array of indices of the packed elements + + ALLOCATE (index_packed(nobs)) ! allocate the packed index vector + index_packed = missing_value(index_packed(1)) ! initialise + + ! Allocate arrays used in 1D-Var after test to stop allocating size nobs=0 + ALLOCATE (om1(nobs,nobs)) + ALLOCATE (yobs(nobs)) + ALLOCATE (zobs(nobs)) + ALLOCATE (yb(nobs)) + ALLOCATE (ycalc(nobs)) + + ! Pack observation arrays for valid values + ! Note: This hard-codes the R-matrix to be diagonal, since that is all that + ! is currently available in JEDI. This will need to be revisted once the + ! full capability is available. + + om1 = 0 + j = 1 + DO i = 1, SIZE (Ob % BendingAngle(:) % Value) + IF (Ob % BendingAngle(i) % Value /= missing_value(Ob % BendingAngle(i) % Value) .AND. & + Ob % ImpactParam(i) % value /= missing_value(Ob % ImpactParam(i) % value) .AND. & + Ob % qc_flags(i) == 0) THEN + index_packed(j) = i + zobs(j) = Ob % ImpactParam(i) % value + yobs(j) = Ob % BendingAngle(i) % Value + om1(j,j) = (Ob % BendingAngle(i) % oberr)**(-2) + j = j + 1 + END IF + END DO + + !----------------------------------------------- + ! 2. If no errors so far, call the 1DVar routine + !----------------------------------------------- + + IF (ALL(zobs(:) /= missing_value(zobs(1))) .AND. & + ALL(yobs(:) /= missing_value(yobs(1))) .AND. & + .NOT. OM1_error) THEN + + temp_rad_curv = Ob % RO_Rad_Curv % Value + temp_latitude = Ob % Latitude + temp_undulation = Ob % RO_geoid_und % value + CALL Ops_GPSRO_rootsolv_BA (nlevp+nlevq, & ! size of state vector + nlevp, & ! no. of press. levels + nlevq, & ! no. of theta levels + nlevq, & ! no. of q levels + Nobs, & ! no of obs + Back % za, & ! height of rho levels + Back % zb, & ! height of theta levels + xb, & ! background vector + yobs, & ! ob. vector + zobs, & ! ob. impact parameters + Bsig, & ! standard dev. of B errors + Bm1, & ! Inverse Back. cov matrix + Om1, & ! Inverse Ob cov matrix + it, & ! no of iterations + x, & ! solution vector + yb, & ! obs at first guess + ycalc, & ! obs at solution + J_pen, & ! penalty value + Amat, & ! solution cov. matrix + converged, & ! convergence flag + BAerr, & ! error flag + Do1DVar_error, & ! error flag + GPSRO_n_iteration_test, & ! + GPSRO_Delta_factor, & ! + GPSRO_Delta_ct2, & ! + GPSRO_pseudo_ops, & + GPSRO_vert_interp_ops, & + GPSRO_min_temp_grad, & + capsupersat, & + O_Bdiff, & ! observed -background BA value + temp_rad_curv, & ! Radius of curvature of ellipsoid + temp_latitude, & ! Latitude of occ + temp_undulation, & ! geoid undulation + Tb, & + Ts, & + DFS) + ran_iteration = .TRUE. + ELSE + + Do1DVar_error = .TRUE. + + Ob % BendingAngle(:) % PGEFinal = 0.99 + + END IF + + IF (.NOT. Do1DVar_error) THEN + + ! store iteration and cost + + Ob % Niter = it + + Ob % Jcost = 2.0_kind_real * J_pen / REAL (nobs) + + ! map the retrieval information back into the ob structures + + Ob % p(:) % Value = 1.0E2 * x(1:nlevp) + + DO i = 1, nlevp + + Ob % p(i) % ObErr = 1.0E2 * SQRT(Amat(i,i)) ! error. est. from cov + + END DO + + Ob % q(:) % Value = 1.0E-3 * x(nlevp + 1:nlevp+nlevq) + + DO i = 1, nlevq + + j = i + nlevp + + Ob % q(i) % ObErr = 1.0E-3 * SQRT(Amat(j,j)) ! error est. from cov + + END DO + + ! Bending angle calculated with solution + + Ob % SolutBendingAngle(index_packed) = ycalc(1:nobs) + +! ! store the bending angle calculated from the background + +! Back % BendingAngle(index_packed) = yb(1:nobs) + + ! PROBABILTY OF GROSS ERROR. Use the cost function value. + + IF (J_pen > GPSRO_cost_funct_test * REAL(NOBS, kind=kind_real)) THEN + + ! the cost function for profile is too high - GROSS ERROR - + ! set all pge's in profile to 0.8 + + Ob % BendingAngle(:) % PGEFinal = 0.8 + + ELSE + + ! For each value in profile, estimate probability of gross error + ! from difference between solution and observed value + + DO i = 1,nobs + + IF (ABS (Ob % BendingAngle(index_packed(i)) % Value - Ob % SolutBendingAngle(index_packed(i))) < & + (GPSRO_y_test * Ob % BendingAngle(index_packed(i)) % ObErr)) THEN + Ob % BendingAngle(index_packed(i)) % PGEFinal = 0.1 + ELSE + Ob % BendingAngle(index_packed(i)) % PGEFinal = 0.7 + END IF + + END DO + + END IF + + ! if the initial 2J/m value exceeds read-in value then flag + IF (O_Bdiff > GPSRO_OB_test) THEN + Ob % BendingAngle(:) % PGEFinal = 0.6 + END IF + + ! check for the BAerr being set + IF (BAerr) THEN + Ob % BendingAngle(:) % PGEFinal = 0.58 ! flag BAerr + END IF + + ELSE ! Do 1D-Var_error + + IF (ran_iteration) THEN + Ob % BendingAngle(:) % PGEFinal = 0.9 ! flag lack of convergence + + ! Bending angle calculated with solution + Ob % SolutBendingAngle(index_packed) = ycalc(1:nobs) + + ! map the retrieval information back into the ob structures + Ob % p(:) % Value = 1.0E2 * x(1:nlevp) + Ob % q(:) % Value = 1.0E-3 * x(nlevp + 1:nlevp+nlevq) + + ! store iterations and cost + Ob % Niter = it + Ob % Jcost = 2.0_kind_real * J_pen / nobs + + ELSE IF (OM1_error) THEN + + Ob % BendingAngle(:) % PGEFinal = 0.85 ! Flag error in getting + ! observation error inverse + END IF + + END IF + +ELSE + IF (nobs <= 10) THEN + WRITE (message, '(A)') 'nobs is less than 10: exit Ops_GPSRO_Do1DVar_BA' + CALL fckit_log % info(message) + Ob % BendingAngle(:) % PGEFinal = 0.55 ! flag lack of observation data + END IF + + IF (BAerr) THEN + WRITE (message, '(A)') 'Error in Ops_Refractivity: exit Ops_GPSRO_Do1DVar_BA' + CALL fckit_log % info(message) + Ob % BendingAngle(:) % PGEFinal = 0.58 ! flag BAerr + END IF +END IF + +END SUBROUTINE Ops_GPSRO_Do1DVar_BA + +end module ufo_gnssroonedvarcheck_do1dvar_mod diff --git a/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_eval_derivs_mod.f90 b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_eval_derivs_mod.f90 new file mode 100644 index 000000000..156a0c942 --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_eval_derivs_mod.f90 @@ -0,0 +1,108 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +!> Evaluate the 1st and 2nd deriv. of the cost function. + +module ufo_gnssroonedvarcheck_eval_derivs_mod + +use kinds, only: kind_real +use missing_values_mod, only: missing_value + +private +public :: Ops_GPSRO_eval_derivs_BA + +contains + +SUBROUTINE Ops_GPSRO_eval_derivs_BA (Nstate, & ! size ot state vector + Nobs, & ! no of obs + x, & ! current est. of soln + xb, & ! background vector + yobs, & ! obs. vector + ycalc, & ! y(x) + BM1, & ! inverse .of bsck cov matrix + OM1, & ! inv. of obs+forw cov matrix + Kmat, & ! gradient matrix + dJ_dx, & ! -ve of first deriv. of cost function + d2J_dx2, & ! second deriv. of cost function. + diag_d2J) ! vector containing the diagonal values of the matrix above + + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: Nstate +INTEGER, INTENT(IN) :: Nobs +REAL(kind_real), INTENT(IN) :: x(:) +REAL(kind_real), INTENT(IN) :: xb(:) +REAL(kind_real), INTENT(IN) :: yobs(:) +REAL(kind_real), INTENT(IN) :: ycalc(:) +REAL(kind_real), INTENT(IN) :: BM1(:,:) +REAL(kind_real), INTENT(IN) :: OM1(:,:) +REAL(kind_real), INTENT(IN) :: Kmat(:,:) +REAL(kind_real), INTENT(OUT) :: dJ_dx(:) +REAL(kind_real), INTENT(OUT) :: d2J_dx2(:,:) +REAL(kind_real), INTENT(OUT) :: diag_d2J(:) + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "Ops_GPSRO_eval_derivs_BA" +INTEGER :: i +REAL(kind_real) :: dx(Nstate) +REAL(kind_real) :: dy(Nobs) +REAL(kind_real) :: Bdx(Nstate) +REAL(kind_real) :: KO(Nstate,Nobs) + +!-------------------------------------------------------- +! 1. Evaluate the 1st and 2nd deriv. of the cost function +!-------------------------------------------------------- + +! Deviation from background + +dx(:) = x(:) - xb(:) + +! Obs. meas-calc + +dy(:) = yobs(:) - ycalc(:) + +! If absolute difference is greater than 1 radian set difference to 0 + +WHERE (ABS (dy(:)) > 1.0) + + dy(:) = 0.0 + +END WHERE + +! calc. Bdx matrix ie B^-1(x-xb) + +Bdx(:) = MATMUL (BM1(:,:), dx(:)) + +! K^T O^-1 matrix + +KO(:,:) = MATMUL (TRANSPOSE (Kmat(:,:)), OM1(:,:)) + +! Calculate -dJ_dx vector -note the NEGATIVE sign + +dJ_dx(:) = MATMUL (KO(:,:), dy(:)) - Bdx(:) +!print*, 'dJ_dx components' +!write(*,'(10E15.8)') dJ_dx(11), Bdx(11) +!write(*,'(10E15.8)') KO(11,:) +!write(*,'(10E15.8)') dy(:) + +! d2J_dx2 MATRIX + +d2J_dx2(:,:) = MATMUL (KO(:,:), Kmat(:,:)) + BM1(:,:) + +! Save the diagonal terms as a vector. + +DO i = 1,Nstate + + diag_d2J(i) = d2J_dx2(i,i) + +END DO + +END SUBROUTINE Ops_GPSRO_eval_derivs_BA + +end module ufo_gnssroonedvarcheck_eval_derivs_mod diff --git a/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_get_bmatrix_mod.f90 b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_get_bmatrix_mod.f90 new file mode 100644 index 000000000..3df85c810 --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_get_bmatrix_mod.f90 @@ -0,0 +1,147 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +!> Set up the background error covariance matrix (B matrix). + +module ufo_gnssroonedvarcheck_get_bmatrix_mod + +use kinds, only: kind_real +use fckit_log_module, only : fckit_log +use ufo_utils_mod, only: ufo_utils_iogetfreeunit + +type Bmatrix_type + INTEGER :: nlevp + INTEGER :: nlevq + INTEGER :: nstate + INTEGER :: nband + INTEGER :: nseason + REAL(kind_real), POINTER :: band_up_lim(:) ! band_up_lim(nband) + REAL(kind_real), POINTER :: sigma(:,:,:) ! sigma(nseason,nband,nstate) + REAL(kind_real), POINTER :: inverse(:,:,:,:) ! inverse(nseason,nband,nstate,nstate) +end type + +private +public :: Ops_GPSRO_GetBmatrix, Bmatrix_type + +contains + +SUBROUTINE Ops_GPSRO_GetBmatrix (filename, & + cx_nlevp, & + cx_nlevq, & + Bmatrix) + +IMPLICIT NONE + +! Subroutine arguments: +CHARACTER(LEN=*) :: filename +INTEGER, INTENT(IN) :: cx_nlevp +INTEGER, INTENT(IN) :: cx_nlevq +TYPE (Bmatrix_type), INTENT(OUT) :: Bmatrix + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "Ops_GPSRO_GetBmatrix" +INTEGER :: i +INTEGER :: j +INTEGER :: m +INTEGER :: n +INTEGER :: nlevp +INTEGER :: nlevq +INTEGER :: nstate +INTEGER :: nband +INTEGER :: nseason +INTEGER :: fileunit +CHARACTER(len=*), PARAMETER :: filetype_name = "Bmatrix" +CHARACTER(len=20) :: prefix +CHARACTER(len=256) :: ErrorMessage +INTEGER :: return_code + +!----------------------------------------------- +! 0. Determine BMatrix environment variable name +!----------------------------------------------- + +prefix = 'GPSRO_' +fileunit = ufo_utils_iogetfreeunit() + +OPEN(UNIT=fileunit, FILE=filename, ACTION='READ', STATUS='OLD', IOSTAT=return_code) +if (return_code /= 0) then + WRITE(ErrorMessage, '(3A,I0)') "Error opening ", TRIM(filename), & + ", return code = ", return_code + call abor1_ftn(ErrorMessage) +end if + +!--------------------- +! 2. Read in the file +!--------------------- + +READ (fileunit, '(5I5)') nlevp, nlevq, nstate, nband, nseason + +IF (cx_nlevp /= nlevp) THEN + + WRITE (ErrorMessage, '(A,I0,A,I0)')'nlevp = ', nlevp, ' cx_nlevp = ', cx_nlevp + call fckit_log % error(ErrorMessage) + ErrorMessage = 'no. of pressure levels in vector and bmatrix not the same' + call abor1_ftn(ErrorMessage) + +END IF + +IF (cx_nlevq /= nlevq) THEN + + WRITE (ErrorMessage, '(A,I0,A,I0)') 'nlevq = ', nlevq, ' cx_nlevq = ', cx_nlevq + call fckit_log % error(ErrorMessage) + ErrorMessage = 'no. of humidity levels in vector and bmatrix not the same' + call abor1_ftn(ErrorMessage) + +END IF + +! Allocate storage variables + +Bmatrix % nlevp = nlevp +Bmatrix % nlevq = nlevq +Bmatrix % nstate = nstate +Bmatrix % nband = nband +Bmatrix % nseason = nseason + +! Allocate the arrays in Bmatrix type + +ALLOCATE (Bmatrix % band_up_lim(nband)) +ALLOCATE (Bmatrix % sigma(nseason,nband,nstate)) +ALLOCATE (Bmatrix % inverse(nseason,nband,nstate,nstate)) + +! Read the band upper limit + +READ (fileunit, '(3F5.1)') (Bmatrix % band_up_lim(i), i = 1, nband) + +DO n = 1,nseason + + DO m = 1,nband + + READ (fileunit, *) ! space + + ! Read in the sigma values + + READ (fileunit, '(10E15.6)') (Bmatrix % sigma (n,m,i), i = 1, nstate) + + ! Read in the inverse B matrix + + DO i = 1,nstate + + READ (fileunit, *) ! space + READ (fileunit, '(10E15.6)') (Bmatrix % inverse (n,m,i,j), j = 1, nstate) + + END DO ! each B matrix + + END DO ! each band + +END DO ! each season + +! Close the file + +CLOSE(fileunit) + +END SUBROUTINE Ops_GPSRO_GetBmatrix + +end module ufo_gnssroonedvarcheck_get_bmatrix_mod diff --git a/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_humidcheck_mod.f90 b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_humidcheck_mod.f90 new file mode 100644 index 000000000..c221cd82a --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_humidcheck_mod.f90 @@ -0,0 +1,117 @@ +!------------------------------------------------------------------------------- +! (C) Crown copyright Met Office. All rights reserved. +! Refer to COPYRIGHT.txt of this distribution for details. +!------------------------------------------------------------------------------- +! Check values of humidity -limit to supersat and set <0.0 to = 0.0 +!------------------------------------------------------------------------------- + +module ufo_gnssroonedvarcheck_humidcheck_mod + +use kinds + +private +public :: Ops_GPSRO_humidcheck + +contains + +SUBROUTINE Ops_GPSRO_humidcheck (nstate, & + nlevp, & + nlevq, & + za, & + zb, & + capsupersat, & + x) + +use ufo_constants_mod, only: & + rd, & ! Gas constant for dry air + grav, & ! Gravitational field strength + c_virtual ! Related to mw_ratio + +USE ufo_utils_mod, ONLY: & + Ops_Qsat, & + Ops_QsatWat + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: nstate +INTEGER, INTENT(IN) :: nlevp +INTEGER, INTENT(IN) :: nlevq +REAL(kind_real), INTENT(IN) :: za(:) +REAL(kind_real), INTENT(IN) :: zb(:) +LOGICAL, INTENT(IN) :: capsupersat ! Whether to remove super-saturation (wrt ice?) +REAL(kind_real), INTENT(INOUT) :: x(:) + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "Ops_GPSRO_humidcheck" +INTEGER :: i +REAL(kind_real) :: P(nlevp) +REAL(kind_real) :: Q(nlevq) +REAL(kind_real) :: T(nlevq) +REAL(kind_real) :: pb(nlevq) +REAL(kind_real) :: qsaturated(nlevq) +REAL(kind_real) :: Tv +REAL(kind_real) :: pwt1 +REAL(kind_real) :: pwt2 + +!--------------------------------------------------------------------- +! 1. Check values of humidity -limit to supersat and set <0.0 to = 0.0 +!--------------------------------------------------------------------- + +! Set up the P and Q vectors from x + +P(:) = 1.0E2 * x(1:nlevp) ! in Pa +Q(:) = 1.0E-3 * x(nlevp + 1:nstate) ! in kg/kg + +DO i = 1, nlevq + + ! Calculate `mean P' for layer + + pwt1 = (za(i + 1) - zb(i)) / (za(i + 1) - za(i)) + + pwt2 = 1.0 - pwt1 + + pb(i) = EXP (pwt1 * LOG (P(i)) + pwt2 * LOG (P(i + 1))) + + ! Derive the layer mean virtual temp. using the hydrostatic relationship + + Tv = grav * (za(i + 1) - za(i)) / (rd * LOG (P(i) / P(i + 1))) + + ! Calculate the temperature + + T(i) = Tv / (1.0 + C_virtual * Q(i)) + +END DO + +! Calculate the super.sat for T and Pmean + +! For T < 0 Ops_Qsat returns the saturated specific humidity over ice. +! Supersaturation with respect to ice is possible, although is largely +! suppressed when CapSupersat is true. When CapSupersat is false the +! humidity check should be done with respect to water. + +IF (CapSupersat) THEN + CALL Ops_Qsat (qsaturated, & ! out + T, & + Pb, & + nlevq) +ELSE + CALL Ops_QsatWat (qsaturated, & ! out + T, & + Pb, & + nlevq) +END IF + +! Check no values have gone -ve +WHERE (x(nlevp + 1:nstate) < 0.0) + x(nlevp + 1:nstate) = 1.0E-4 +END WHERE + +! Limit saturated value +WHERE (x(nlevp + 1:nstate) > 1.0E3 * qsaturated(1:nlevq)) + x(nlevp + 1:nstate) = 1.0E3 * qsaturated(1:nlevq) ! in g/kg +END WHERE + +END SUBROUTINE Ops_GPSRO_humidcheck + +end module ufo_gnssroonedvarcheck_humidcheck_mod diff --git a/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_mod.f90 b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_mod.f90 new file mode 100644 index 000000000..5bd679884 --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_mod.f90 @@ -0,0 +1,317 @@ +! (C) Copyright 2017-2020 Met Office +! +! this software is licensed under the terms of the apache licence version 2.0 +! which can be obtained at http://www.apache.org/licenses/license-2.0. + +!> The main Fortran module for implementing the GNSS-RO onedvar check + +module ufo_gnssroonedvarcheck_mod + +use, intrinsic :: iso_c_binding +use fckit_configuration_module, only: fckit_configuration +use fckit_log_module, only : fckit_log +use iso_c_binding +use kinds +use missing_values_mod +use obsspace_mod +use oops_variables_mod +use ufo_geovals_mod +use ufo_vars_mod +use ufo_gnssro_bendmetoffice_mod +use ufo_gnssroonedvarcheck_utils_mod, only: & + Ops_RealSortQuick, deallocate_singleob, & + allocate_singleob, allocate_singlebg, deallocate_singlebg, find_unique, & + singlebg_type, singleob_type +use ufo_gnssroonedvarcheck_get_bmatrix_mod, only: Ops_GPSRO_GetBmatrix, bmatrix_type +use ufo_gnssroonedvarcheck_do1dvar_mod, only: Ops_GPSRO_Do1DVar_BA + +implicit none +private +public :: ufo_gnssroonedvarcheck_create +public :: ufo_gnssroonedvarcheck_delete +public :: ufo_gnssroonedvarcheck_apply + +type, public :: ufo_gnssroonedvarcheck + character(len=800) :: qcname !< name of the filter + type(c_ptr) :: obsdb !< pointer to the observation space + type(fckit_configuration) :: conf !< contents of the yaml file + integer(c_int) :: onedvarflag !< flag uased by the qc manager for a 1D-var check + logical :: capsupersat !< Whether to remove super-saturation (wrt ice?) + real(kind_real) :: cost_funct_test !< Threshold value for the cost function convergence test + real(kind_real) :: Delta_ct2 !< Threshold used in calculating convergence + real(kind_real) :: Delta_factor !< Threshold used in calculating convergence + integer :: n_iteration_test !< Maximum number of iterations in the 1DVar + real(kind_real) :: OB_test !< Threshold for the O-B throughout the profile + real(kind_real) :: y_test !< Threshold on distance between observed and solution bending angles + real(kind_real) :: Zmin !< Minimum height at for assimilation of data + real(kind_real) :: Zmax !< Maximum height at for assimilation of data + character(len=800) :: bmatrix_filename !< Location of the B-matrix file + logical :: pseudo_ops !< Whether to use pseudo levels in forward operator + logical :: vert_interp_ops !< Whether to use ln(p) or exner in vertical interpolation + real(kind_real) :: min_temp_grad !< The minimum vertical temperature gradient allowed +end type ufo_gnssroonedvarcheck + +! ------------------------------------------------------------------------------ +contains +! ------------------------------------------------------------------------------ +!> Setup the main GNSS-RO onedvar object in Fortran +!! +!! \details Makes a call to the main setup routine. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_gnssroonedvarcheck_create(self, obsspace, f_conf, onedvarflag) + + implicit none + type(ufo_gnssroonedvarcheck), intent(inout) :: self !< gnssroonedvarcheck main object + type(c_ptr), value, intent(in) :: obsspace !< observation database pointer + type(fckit_configuration), intent(in) :: f_conf !< yaml file contents + integer(c_int), intent(in) :: onedvarflag !< flag for qc manager + + character(len=:), allocatable :: temp_str1 + + self % obsdb = obsspace + self % conf = f_conf + self % onedvarflag = onedvarflag + + call f_conf%get_or_die("capsupersat", self % capsupersat) + call f_conf%get_or_die("Zmin", self % Zmin) + call f_conf%get_or_die("Zmax", self % Zmax) + call f_conf%get_or_die("cost_funct_test", self % cost_funct_test) + call f_conf%get_or_die("y_test", self % y_test) + call f_conf%get_or_die("n_iteration_test", self % n_iteration_test) + call f_conf%get_or_die("Delta_ct2", self % Delta_ct2) + call f_conf%get_or_die("Delta_factor", self % Delta_factor) + call f_conf%get_or_die("OB_test", self % OB_test) + call f_conf%get_or_die("bmatrix_filename", temp_str1) + self % bmatrix_filename = temp_str1 + call f_conf%get_or_die("pseudo_ops", self % pseudo_ops) + call f_conf%get_or_die("vert_interp_ops", self % vert_interp_ops) + call f_conf%get_or_die("min_temp_grad", self % min_temp_grad) + +end subroutine ufo_gnssroonedvarcheck_create + +! ------------------------------------------------------------------------------ +!> Delete the main GNSS-RO onedvar object in Fortran +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_gnssroonedvarcheck_delete(self) + + implicit none + type(ufo_gnssroonedvarcheck), intent(inout) :: self !< gnssroonedvarcheck main object + +end subroutine ufo_gnssroonedvarcheck_delete + +! ------------------------------------------------------------------------------ +!> The main routine that applys the GNSS-RO onedvar filter +!! +!! \details Heritage : +!! +!! This routine is called from the c++ apply method. The filter performs +!! a 1D-Var minimization +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_gnssroonedvarcheck_apply(self, geovals, apply) + + implicit none + + type(ufo_gnssroonedvarcheck), intent(inout) :: self !< gnssroonedvarcheck main object + type(ufo_geovals), intent(in) :: geovals !< model values at observation space + logical, intent(in) :: apply(:) !< qc manager flags + + integer :: nobs ! Number of observations to be processed + type(ufo_geoval), pointer :: q ! Model background values of specific humidity + type(ufo_geoval), pointer :: prs ! Model background values of air pressure + type(ufo_geoval), pointer :: theta_heights ! Model heights of levels containing specific humidity + type(ufo_geoval), pointer :: rho_heights ! Model heights of levels containing air pressure + type(singlebg_type) :: Back ! Model background fields + type(singleob_type) :: Ob ! The profile of observations + type(bmatrix_type) :: b_matrix ! Background-error covariance matrix + real(kind_real), allocatable :: obsLat(:) ! Latitude of the observation + real(kind_real), allocatable :: obsLon(:) ! Longitude of the observation + real(kind_real), allocatable :: impact_param(:) ! Impact parameter of the observation + real(kind_real), allocatable :: radius_curv(:) ! Earth's radius of curvature at the observation tangent point + real(kind_real), allocatable :: undulation(:) ! Undulation - height of the geoid above the ellipsoid + real(kind_real), allocatable :: obs_bending_angle(:) ! Observed bending angle + real(kind_real), allocatable :: obs_err(:) ! Observation error, taken from a previous filter + integer, allocatable :: qc_flags(:) ! QC flags to be updated + integer, allocatable :: obsSatid(:) ! Satellite identifier for each observation + integer, allocatable :: obsOrigC(:) ! Originating centre for each observation + integer(c_size_t), allocatable :: record_number(:) ! Number used to identify unique profiles in the data + real(kind_real), allocatable :: sort_key(:) ! Key for the sorting (based on record number and impact parameter) + integer, allocatable :: index_vals(:) ! Indices of sorted observation + integer, allocatable :: unique(:) ! Set of unique profile numbers + integer :: start_point ! Starting index of the current profile + integer :: current_point ! Ending index of the current profile + integer :: iprofile ! Loop variable, profile number + integer :: nobs_profile ! Number of observations in the profile + character(len=800) :: Message ! Message to be output + logical :: BAerr ! Has there been an error in the bending angle calculation? + integer :: iband ! Selected latitude band of the B-matrix + integer :: iseason ! Selected season of the B-matrix + integer :: ipoint ! Loop variable, observation point + real(kind_real) :: dfs ! Degrees of freedom for signal in profile + real(kind_real) :: O_Bdiff ! Average RMS(O-B) for profile + real(kind_real), allocatable :: Tb(:) ! Calculated background temperature (derived from p,q) + real(kind_real), allocatable :: Ts(:) ! 1DVar solution temperature + + ! Get the obs-space information + nobs = obsspace_get_nlocs(self % obsdb) + allocate(obsLon(nobs)) + allocate(obsLat(nobs)) + allocate(impact_param(nobs)) + allocate(radius_curv(nobs)) + allocate(undulation(nobs)) + allocate(obs_bending_angle(nobs)) + allocate(record_number(nobs)) + allocate(obsSatid(nobs)) + allocate(obsOrigC(nobs)) + allocate(qc_flags(nobs)) + allocate(obs_err(nobs)) + + call obsspace_get_db(self % obsdb, "MetaData", "longitude", obsLon) + call obsspace_get_db(self % obsdb, "MetaData", "latitude", obsLat) + call obsspace_get_db(self % obsdb, "MetaData", "impact_parameter", impact_param) + call obsspace_get_db(self % obsdb, "MetaData", "earth_radius_of_curvature", radius_curv) + call obsspace_get_db(self % obsdb, "MetaData", "geoid_height_above_reference_ellipsoid", undulation) + call obsspace_get_db(self % obsdb, "ObsValue", "bending_angle", obs_bending_angle) + call obsspace_get_recnum(self % obsdb, record_number) + call obsspace_get_db(self % obsdb, "MetaData", "occulting_sat_id", obsSatid) + call obsspace_get_db(self % obsdb, "MetaData", "originating_center", obsOrigC) + call obsspace_get_db(self % obsdb, "FortranQC", "bending_angle", qc_flags) + call obsspace_get_db(self % obsdb, "FortranERR", "bending_angle", obs_err) + + ! get variables from geovals + call ufo_geovals_get_var(geovals, var_q, q) ! specific humidity + call ufo_geovals_get_var(geovals, var_prsi, prs) ! pressure + call ufo_geovals_get_var(geovals, var_z, theta_heights) ! Geopotential height of the normal model levels + call ufo_geovals_get_var(geovals, var_zi, rho_heights) ! Geopotential height of the pressure levels + + ! Read in the B-matrix and background profiles + call Ops_GPSRO_GetBmatrix(self % bmatrix_filename, prs % nval, q % nval, b_matrix) + call allocate_singlebg(Back, prs % nval, q % nval) + allocate(Tb(q % nval), Ts(q % nval)) + + ! Read through the record numbers in order to find a profile of observations + ! Each profile shares the same record number + allocate(sort_key(nobs)) + sort_key = record_number + (impact_param / MAXVAL(impact_param)) + call Ops_RealSortQuick(sort_key, index_vals) + call find_unique(record_number, unique) + + ! For every profile that we have found, perform a 1DVar minimisation + current_point = 1 + do iprofile = 1, size(unique) + start_point = current_point + WRITE (Message, '(A,I0)') 'ObNumber ', iprofile + call fckit_log % info(Message) + WRITE (Message, '(A,F12.2)') 'Latitude ', obsLat(index_vals(start_point)) + call fckit_log % info(Message) + WRITE (Message, '(A,F12.2)') 'Longitude ', obsLon(index_vals(start_point)) + call fckit_log % info(Message) + WRITE (Message, '(A,I0)') 'Processing centre ', obsOrigC(index_vals(start_point)) + call fckit_log % info(Message) + WRITE (Message, '(A,I0)') 'Sat ID ', obsSatid(index_vals(start_point)) + call fckit_log % info(Message) + WRITE (Message, '(A,F12.2)') 'GPSRO_Zmin ', self % Zmin + call fckit_log % info(Message) + WRITE (Message, '(A,F12.2)') 'GPSRO_Zmax ', self % Zmax + call fckit_log % info(Message) + + ! Work out which observations belong to the current profile + do current_point = start_point, nobs + if (unique(iprofile) /= record_number(index_vals(current_point))) exit + end do + + ! Load the geovals into the background structure + Back % za(:) = rho_heights % vals(:, index_vals(start_point)) + Back % zb(:) = theta_heights % vals(:, index_vals(start_point)) + Back % p(:) = prs % vals(:, index_vals(start_point)) + Back % q(:) = q % vals(:, index_vals(start_point)) + + ! Allocate the observations structure + nobs_profile = current_point - start_point + call allocate_singleob(Ob, nobs_profile, prs % nval, q % nval) + + ! Load the observations information into the obsevations structure + Ob % id = record_number(index_vals(start_point)) + Ob % latitude = obsLat(index_vals(start_point)) + Ob % longitude = obsLon(index_vals(start_point)) + Ob % niter = 0 ! We haven't yet run 1DVar + Ob % jcost = missing_value(Ob % jcost) + Ob % bendingangle(:) % value = obs_bending_angle(index_vals(start_point:current_point-1)) + Ob % bendingangle(:) % oberr = obs_err(index_vals(start_point:current_point-1)) + Ob % impactparam(:) % value = impact_param(index_vals(start_point:current_point-1)) + Ob % qc_flags(:) = qc_flags(index_vals(start_point:current_point-1)) + Ob % ro_rad_curv % value = radius_curv(index_vals(start_point)) + Ob % ro_geoid_und % value = undulation(index_vals(start_point)) + + ! Choose the latitude band and season of the B-matrix information + iseason = 1 ! Temporary -only one season at present! + iband = 1 + DO + IF (b_matrix % band_up_lim(iband) > Ob % latitude .OR. & + iband == b_matrix % nband) EXIT + iband = iband + 1 + END DO + +! Call the code to set up the 1D-Var calculation + call Ops_GPSRO_Do1DVar_BA(prs % nval, & ! Number of pressure levels + q % nval, & ! Number of specific humidity levels + b_matrix % inverse(iseason,iband,:,:), & ! Inverse of the b-matrix + b_matrix % sigma(iseason,iband,:), & ! Standard deviations of the b-matrix + Back, & ! Structure containing the model background information + Ob, & ! Structure containing the observation information + self % pseudo_ops, & ! Whether to use pseudo-levels in calculation + self % vert_interp_ops, & ! Whether to interpolate using ln(p) or exner + self % min_temp_grad, & ! Minimum vertical temperature gradient allowed + self % Zmin, & ! Minimum vertical level to accept observations + self % Zmax, & ! Maximum vertical level to accept observations + self % cost_funct_test, & ! Threshold value for the cost function convergence test + self % y_test, & ! Threshold value for the yobs-ysol tes + self % n_iteration_test, & ! Maximum number of iterations + self % Delta_factor, & ! Delta + self % Delta_ct2, & ! Delta observations + self % OB_test, & ! Threshold value for the O-B test + self % capsupersat, & ! Whether to remove super-saturation + BAerr, & ! Whether there are errors in the bending angle calculation + Tb, & ! Calculated background temperature + Ts, & ! 1DVar solution temperature + O_Bdiff, & ! Difference between observations and background for profile + DFS) ! Estimated degrees of freedom for signal + + ! Flag bad profiles + do ipoint = 0, nobs_profile-1 + if (qc_flags(start_point + ipoint) > 0) then + ! Do nothing, since the data are already flagged + else if (Ob % bendingangle(ipoint+1) % PGEFinal > 0.5) then + qc_flags(start_point + ipoint) = self % onedvarflag + Ob % qc_flags(ipoint+1) = self % onedvarflag + end if + end do + + write(Message,'(A,2I5,2F10.3,I5,F16.6)') 'Profile stats: ', obsSatid(index_vals(start_point)), & + obsOrigC(index_vals(start_point)), Ob % latitude, Ob % longitude, & + Ob % niter, Ob % jcost + call fckit_log % debug(Message) + do ipoint = 0, nobs_profile-1, 100 + write(Message,'(100I5)') qc_flags(index_vals(start_point+ipoint: & + min(start_point+ipoint+99, current_point-1))) + call fckit_log % debug(Message) + end do + + call deallocate_singleob(Ob) + end do + call obsspace_put_db(self % obsdb, "FortranQC", "bending_angle", qc_flags) + +end subroutine ufo_gnssroonedvarcheck_apply + +end module ufo_gnssroonedvarcheck_mod diff --git a/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_pen_mod.f90 b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_pen_mod.f90 new file mode 100644 index 000000000..a5c75836c --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_pen_mod.f90 @@ -0,0 +1,97 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +!> Calculates GPSRO penalty. + +module ufo_gnssroonedvarcheck_pen_mod + +use kinds, only: kind_real +use missing_values_mod, only: missing_value + +private +public :: Ops_GPSRO_pen + +contains + +SUBROUTINE Ops_GPSRO_pen (Nstate, & ! size of state vec. + Nobs, & ! size of obs vec. + x, & ! current estimate of soltution + xb, & ! background + yobs, & ! observed values + ycalc, & ! y(x) + BM1, & ! inverse .of bsck cov matrix + OM1, & ! inv. of obs+forw cov matrix + pen_ob, & ! obs penalty + pen_back, & ! back penalty + pen_func) ! total penalty + + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: Nstate +INTEGER, INTENT(IN) :: Nobs +REAL(kind_real), INTENT(IN) :: x(:) +REAL(kind_real), INTENT(IN) :: xb(:) +REAL(kind_real), INTENT(IN) :: yobs(:) +REAL(kind_real), INTENT(IN) :: ycalc(:) +REAL(kind_real), INTENT(IN) :: BM1(:,:) +REAL(kind_real), INTENT(IN) :: OM1(:,:) +REAL(kind_real), INTENT(OUT) :: pen_ob +REAL(kind_real), INTENT(OUT) :: pen_back +REAL(kind_real), INTENT(OUT) :: pen_func + +! Local declarations: +REAL(kind_real) :: dx(Nstate) +REAL(kind_real) :: dy(Nobs) +REAL(kind_real) :: Bdx(Nstate) +REAL(kind_real) :: Ody(Nobs) +REAL(kind_real) :: J_back +REAL(kind_real) :: J_obs +CHARACTER(len=*), PARAMETER :: RoutineName = "Ops_GPSRO_pen" + +! deviation from background + +dx(:) = x(:) - xb(:) + +! calc. Bdx matrix ie B^-1(x-xb) + +Bdx(:) = MATMUL (BM1(:,:), dx(:)) + +! background term (scalar) + +J_back = DOT_PRODUCT (dx(:), Bdx(:)) + +! obs. meas-calc + +dy(:) = yobs(:) - ycalc(:) + +!make sure missing data is not included +WHERE (ycalc(:) == missing_value(ycalc(1)) .OR. & + yobs(:) == missing_value(yobs(1))) + + dy(:) = 0.0 +END WHERE + +! Ody O^-1 (ymeas-ycalc) + +Ody(:) = MATMUL (OM1(:,:), dy (:)) + +! observation term. (scalar) + +J_obs = DOT_PRODUCT (dy(:), Ody(:)) + +! SCALAR value required + +pen_func = 0.5 * (J_back + J_obs) + +pen_ob = 0.5 * J_obs +pen_back = 0.5 * J_back + +END SUBROUTINE Ops_GPSRO_pen + +end module ufo_gnssroonedvarcheck_pen_mod diff --git a/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_rootsolv_mod.f90 b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_rootsolv_mod.f90 new file mode 100644 index 000000000..731ad6b35 --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_rootsolv_mod.f90 @@ -0,0 +1,542 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +!> Fortran module for gnssro bending angle Met Office forward operator + +module ufo_gnssroonedvarcheck_rootsolv_mod + +use kinds, only: kind_real +use missing_values_mod, only: missing_value +use fckit_log_module, only: fckit_log + +private +public :: Ops_GPSRO_rootsolv_BA + +contains + +!------------------------------------------------------------------------------- +! Solve the 1dvar problem +!------------------------------------------------------------------------------- + +SUBROUTINE Ops_GPSRO_rootsolv_BA (nstate, & ! size of state vector + nlevp, & ! no. of press. levels + nb, & ! no. of theta levels + nlevq, & ! no. of q levels + Nobs, & ! no of obs + za, & ! height of rho levels + zb, & ! height of theta levels + xb, & ! background vector + yobs, & ! ob. vector + zobs, & ! ob. impact parameters + Bsig, & ! standard dev. of b errors + Bm1, & ! Inverse Back. cov matrix + Om1, & ! Inverse Ob cov matrix + it, & ! no of iterations + x, & ! solution vector + yb, & ! obs first guess + ycalc, & ! obs at solution + J_pen, & ! penalty value + Amat, & ! solution cov. matrix + converged, & ! convergence flag + BAerr, & ! error flag + Do1DVar_error, & ! error flag + Iter_max, & ! Max number of iterations + Delta, & ! + Delta_ct2, & ! + GPSRO_pseudo_ops, & ! Whether to use pseudo levels + GPSRO_vert_interp_ops, & ! Whether to vertically interpolate using exner or ln(p) + GPSRO_min_temp_grad, & ! Minimum vertical temperature gradient allowed + capsupersat, & + O_Bdiff, & ! observed -background bending angle value + RO_Rad_Curv, & ! Radius of curvature of ellipsoid + Latitude, & ! Latitude of occ + RO_geoid_und, & ! geoid undulation + Tb, & + Ts, & + DFS) + + +USE ufo_gnssro_ukmo1d_utils_mod, only: & + Ops_GPSROcalc_alpha, & + Ops_GPSROcalc_nr + +USE ufo_gnssro_bendmetoffice_tlad_utils_mod, only: & + Ops_GPSROcalc_alphaK, & + Ops_GPSROcalc_nrK + +USE ufo_gnssroonedvarcheck_humidcheck_mod, only: & + Ops_GPSRO_humidcheck + +USE ufo_gnssroonedvarcheck_pen_mod, only: & + Ops_GPSRO_pen + +USE ufo_gnssroonedvarcheck_eval_derivs_mod, only: & + Ops_GPSRO_eval_derivs_BA + +USE ufo_utils_mod, only: & + InvertMatrix, Ops_Cholesky + +use ufo_utils_refractivity_calculator, only: & + ufo_calculate_refractivity, & + ufo_refractivity_kmat + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: nstate +INTEGER, INTENT(IN) :: nlevp +INTEGER, INTENT(IN) :: nb +INTEGER, INTENT(IN) :: nlevq +INTEGER, INTENT(IN) :: Nobs +INTEGER, INTENT(IN) :: Iter_max +REAL(kind_real), INTENT(IN) :: Delta_ct2 +REAL(kind_real), INTENT(IN) :: za(:) +REAL(kind_real), INTENT(IN) :: zb(:) +REAL(kind_real), INTENT(IN) :: xb(:) +REAL(kind_real), INTENT(IN) :: yobs(:) +REAL(kind_real), INTENT(IN) :: zobs(:) +REAL(kind_real), INTENT(IN) :: Bsig(:) +REAL(kind_real), INTENT(IN) :: Bm1(:,:) +REAL(kind_real), INTENT(IN) :: Om1(:,:) +REAL(kind_real), INTENT(IN) :: Delta +LOGICAL, INTENT(IN) :: GPSRO_pseudo_ops +LOGICAL, INTENT(IN) :: GPSRO_vert_interp_ops +REAL(kind_real), INTENT(IN) :: GPSRO_min_temp_grad +LOGICAL, INTENT(IN) :: capsupersat +INTEGER, INTENT(OUT) :: it +REAL(kind_real), INTENT(OUT) :: x(:) +REAL(kind_real), INTENT(OUT) :: yb(:) +REAL(kind_real), INTENT(OUT) :: ycalc(:) +REAL(kind_real), INTENT(OUT) :: J_pen +REAL(kind_real), INTENT(OUT) :: Amat(:,:) +LOGICAL, INTENT(OUT) :: Converged +LOGICAL, INTENT(OUT) :: BAerr +LOGICAL, INTENT(OUT) :: Do1DVar_Error +REAL(kind_real), INTENT(OUT) :: O_Bdiff ! Measure of O-B for whole profile +REAL(kind_real), INTENT(IN) :: RO_Rad_Curv +REAL(kind_real), INTENT(IN) :: Latitude +REAL(kind_real), INTENT(IN) :: RO_geoid_und +REAL(kind_real), INTENT(INOUT) :: Tb(nlevq) +REAL(kind_real), INTENT(INOUT) :: Ts(nlevq) +REAL(kind_real), INTENT(INOUT) :: DFS ! Measure of degrees of freesom of signal for whole profile + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "Ops_GPSRO_rootsolv_BA" +INTEGER, PARAMETER :: max_string = 800 + +LOGICAL :: MARQ +INTEGER :: i +INTEGER :: it_marq +INTEGER :: ErrorCode +REAL(kind_real) :: J_old +REAL(kind_real) :: J_min +REAL(kind_real) :: pen_ob +REAL(kind_real) :: pen_back +REAL(kind_real) :: xold(nstate) +REAL(kind_real) :: x_temp(nstate) +REAL(kind_real) :: xmin(nstate) +REAL(kind_real) :: ymin(nobs) +REAL(kind_real) :: lambda +REAL(kind_real) :: lamp1 +REAL(kind_real) :: d2J_dx2(nstate,nstate) +REAL(kind_real) :: dJ_dx(nstate) +REAL(kind_real) :: diag_d2J(nstate) +REAL(kind_real) :: Kmat(Nobs,nstate) +REAL(kind_real) :: dx(nstate) +REAL(kind_real) :: Conv_Test +REAL(kind_real) :: ct2 +REAL(kind_real) :: ct3 +REAL(kind_real) :: d2 ! measure of step taken +REAL(kind_real) :: sdx(nstate) +REAL(kind_real) :: OK(Nobs,nstate) ! O^-1 * Kmat +REAL(kind_real) :: KOK(nstate,nstate) ! KT *O^-1 *K +REAL(kind_real) :: AKOK(nstate,nstate) ! Amat * above +REAL(kind_real), ALLOCATABLE :: nr(:) +REAL(kind_real), ALLOCATABLE :: dref_dp(:,:) +REAL(kind_real), ALLOCATABLE :: dref_dq(:,:) +REAL(kind_real), ALLOCATABLE :: dnr_dref(:,:) +REAL(kind_real), ALLOCATABLE :: dalpha_dref(:,:) +REAL(kind_real), ALLOCATABLE :: dalpha_dnr(:,:) +REAL(kind_real), ALLOCATABLE :: m1(:,:) +REAL(kind_real) :: T(nlevq) +REAL(kind_real) :: pressure(1:nlevp) +REAL(kind_real) :: humidity(1:nlevq) + +REAL(kind_real), ALLOCATABLE :: model_heights(:) +REAL(kind_real), ALLOCATABLE :: refractivity(:) +INTEGER :: nRefLevels +CHARACTER(LEN=max_string) :: message + +!-------------- +! 1. Initialise +!-------------- + +IF (GPSRO_pseudo_ops) THEN + ALLOCATE(nr(2 * nlevq - 1)) + ALLOCATE(dref_dp(2 * nlevq - 1,nlevp)) + ALLOCATE(dref_dq(2 * nlevq - 1,nlevq)) + ALLOCATE(dnr_dref(2 * nlevq - 1,2 * nlevq - 1)) + ALLOCATE(dalpha_dref(nobs,2 * nlevq - 1)) + ALLOCATE(dalpha_dnr(nobs,2 * nlevq - 1)) + ALLOCATE(m1(nobs,2 * nlevq - 1)) +ELSE + ALLOCATE(nr(nlevq)) + ALLOCATE(dref_dp(nlevq,nlevp)) + ALLOCATE(dref_dq(nlevq,nlevq)) + ALLOCATE(dnr_dref(nlevq,nlevq)) + ALLOCATE(dalpha_dref(nobs,nlevq)) + ALLOCATE(dalpha_dnr(nobs,nlevq)) + ALLOCATE(m1(nobs,nlevq)) +END IF + +x(:) = xb(:) ! first guess = background +xold(:) = xb(:) +xmin(:) = xb(:) + +T(:) = missing_value(T(1)) +Tb(:) = missing_value(Tb(1)) +Ts(:) = missing_value(Ts(1)) +yb(:) = missing_value(yb(1)) +ycalc(:) = missing_value(ycalc(1)) + +! Logicals + +Converged = .FALSE. +Do1DVar_Error = .FALSE. +MARQ = .FALSE. + +! Set convergence + penalty values to a large no. + +Conv_Test = 1.0E30 +ct2 = 1.0E30 +ct3 = 1.0E30 +J_old = 1.0E30 +J_pen = 1.0E30 +J_min = 1.0E30 + +! Set lambda used in the Marqu. Lev. soln -initially a small value + +lambda = 1.0E-4_kind_real + +it = 0 +it_marq = 0 ! no. of Marquardt iterations + +O_Bdiff = missing_value(O_Bdiff) +DFS = missing_value(DFS) +ycalc(:) = missing_value(ycalc(1)) +ymin(:) = missing_value(ymin(1)) +ErrorCode = missing_value(ErrorCode) + +!----------------------- +! 2. Main iteration loop +!----------------------- + +! Data to stdout on convergence of iteration loop +CALL fckit_log % info('J_pen|Conv_test|ct2|lambda|d2|(dJ/dx)^2|') + +Iteration_loop: DO + + IF (it > Iter_max .OR. & + (Conv_test < Delta .AND. & + ct2 < Delta_ct2 * (Nobs / 200.0_kind_real))) EXIT ! exit the iteration + + ! Count no. of iterations + + it = it + 1 + + ! Calculate the bending angle profile for current guess x + + ! Call the 1D bending angle forward model + + ! 1. First calculate model refractivity on theta levels + + ! Unpack the solution to p and q, changing units + pressure = 100 * x(1:nlevp) + humidity = 0.001 * x(nlevp+1:nlevp+nlevq) + + CALL ufo_calculate_refractivity (nlevp, & + nlevq, & + za, & + zb, & + pressure, & + humidity, & + GPSRO_pseudo_ops, & + GPSRO_vert_interp_ops, & + GPSRO_min_temp_grad, & + BAerr, & + nRefLevels, & + refractivity, & + model_heights, & + T) + + ! no point proceeding further if ... + IF (BAerr) EXIT + + ! 2. Calculate the refractive index * radius on theta model levels (or model impact parameter) + CALL Ops_GPSROcalc_nr (nRefLevels, & ! number of refractivity levels + model_heights, & ! geopotential heights of refractivity levels + refractivity, & ! calculated refractivity + RO_Rad_Curv, & ! radius of curvature of earth at observation + Latitude, & ! latitude at observation + RO_geoid_und, & ! geoid undulation above WGS-84 + nr) ! Calculated model impact parameters + + ! 3. Calculate model bending angle on observation impact parameters + CALL Ops_GPSROcalc_alpha (nobs, & ! size of ob. vector + nRefLevels, & ! no. of refractivity levels + zobs, & ! obs impact parameters + refractivity, & ! refractivity values on model+pseudo levels + nr, & ! index * radius product + ycalc) ! forward modelled bending angle + + ! Store the bending angle values calculated with `x(:)=xb(:)' + + IF (it == 1) yb(:) = ycalc(:) + + ! For writing the background temperature to a file + IF (it == 1) Tb(:) = T(:) + + ! Calculate the penalty (cost) function + + CALL Ops_GPSRO_pen (nstate, & + nobs, & + x, & + xb, & + yobs, & + ycalc, & + BM1, & + OM1, & + pen_ob, & !out obs penalty + pen_back, & !out back penalty + J_pen) + + !store O-B value i.e. 2J/m on first iteration when dx=0 + IF (it == 1) O_Bdiff = 2.0_kind_real * J_pen / REAL (nobs) + + ! store the lowest cost value calculated + + IF (J_min > J_pen) THEN + + J_min = J_pen + xmin(:) = x(:) + ymin(:) = ycalc(:) + + END IF + + ! Convergence test + + Conv_test = ABS ((J_old - J_pen) / MIN ( J_old, REAL(NOBS, kind=kind_real))) + + IF (it == 1 .OR. J_pen - J_old < 0.1_kind_real .OR. lambda > 1.0E6_kind_real) THEN + + ! Normal Newtonian iteration + + J_old = J_pen + + IF (.NOT. MARQ) lambda = 0.1_kind_real * lambda + + MARQ = .FALSE. + + ! Evaluate the K matrix for current x + ! 1. Calculate the gradient of ref wrt p (on rho levels) and q (on theta levels) + ! Note: pressure and humidity were unpacked from x earlier in the loop + + CALL ufo_refractivity_kmat(nlevp, & + nlevq, & + nRefLevels, & + za, & + zb, & + pressure, & + humidity, & + GPSRO_pseudo_ops, & + GPSRO_vert_interp_ops, & + GPSRO_min_temp_grad, & + dref_dP, & + dref_dq) + + ! Change the units for the K-matrices + dref_dP(:,:) = 1.0E2 * dref_dP(:,:) ! hPa + dref_dq(:,:) = 1.0E-3 * dref_dq(:,:) ! g/kg + + ! 2. Calculate the gradient of nr wrt ref + CALL Ops_GPSROcalc_nrK (model_heights, & ! geopotential heights of pseudo levels + nRefLevels, & ! number of pseudo levels + RO_Rad_Curv, & ! radius of curvature of earth at observation + Latitude, & ! latitude at observation + RO_geoid_und, & ! geoid undulation above WGS-84 + refractivity, & ! refractivity of model on pseudo levels + dnr_dref) ! out + + ! 3. Calculate the gradient of bending angle wrt ref and nr + CALL Ops_GPSROcalc_alphaK (nobs, & ! size of ob. vector + nRefLevels, & ! no. of refractivity pseudo levels + zobs, & ! obs impact parameters + refractivity, & ! refractivity values on pseudo levels + nr, & ! index * radius product + dalpha_dref, & ! out + dalpha_dnr) ! out + + ! Calculate overall gradient of bending angle wrt p and q + + m1 = MATMUL (dalpha_dnr,dnr_dref) + Kmat(1:nobs,1:nlevp) = MATMUL (dalpha_dref,dref_dp) + MATMUL (m1,dref_dp) !P part + Kmat(1:nobs,nlevp + 1:nstate) = MATMUL (dalpha_dref,dref_dq) + MATMUL (m1,dref_dq) !q part + + ! Store the state vector in xold + + xold(:) = x(:) + + ! Evaluate the -dJ_dx(vector NOTE SIGN!!) and d2J_dx2(matrix) at x + + CALL Ops_GPSRO_eval_derivs_BA (nstate, & + nobs, & + x, & + xb, & + yobs, & + ycalc, & + BM1, & + OM1, & + Kmat, & + dJ_dx, & + d2J_dx2, & + diag_d2J) + + ! Store inverse of soln. cov matrix + Amat(:,:) = d2J_dx2(:,:) + + ELSE + + ! M.Lev iteration.The previous increment increased the value + ! of the penalty function Use previous values of -dJ_dx, d2J_dx2 and xold + + MARQ = .TRUE. + it_marq = it_marq + 1 + lambda = 10.0_kind_real * lambda + + END IF + + ! Marq.Lev adjustment to diagonal terms + + lamp1 = lambda + 1.0_kind_real + + DO i = 1,nstate + + d2J_dx2(i,i) = diag_d2J(i) + + ! Marq.Lev. modification to diagonals of Hessian + + d2J_dx2(i,i) = d2J_dx2(i,i) * lamp1 + + END DO + + ! Solve the matrix equation + ! + ! d2J_dx2(:,:) . dx (:) = - dJ_dx (:) + ! + ! to find dx(:) using the Cholesky decomposition routine + ! + + CALL Ops_Cholesky (d2J_dx2, & + dJ_dx, & + nstate, & + dx, & ! The answer, i.e. the "increment" + ErrorCode) + + IF (ErrorCode /= 0) EXIT + + ! Update estimate, but limit magnitude of increment with expected background + ! error + + x(:) = xold(:) + SIGN (MIN (ABS (dx(:)), 2.0_kind_real * Bsig(:), xold(:) / 2.0_kind_real), dx(:)) + + ! Check values of humidity -limit to supersat and set <0.0 to = 0.0 + + CALL Ops_GPSRO_humidcheck (nstate, & + nlevp, & + nlevq, & + za, & + zb, & + capsupersat, & + x) + + ! Second convergence criteria in terms of gradient + + ct2 = DOT_PRODUCT (ABS (x(:) - xold(:)), ABS (dJ_dx(:))) + ct3 = DOT_PRODUCT (dJ_dx(:), dJ_dx(:)) + + !----------------------------------------- + ! Save iteration info to standard output + !----------------------------------------- + sdx(:) = MATMUL (Amat(:,:) , (x(:) - xold(:))) !S^-1.dx + d2 = DOT_PRODUCT ((x(:) - xold(:)) , Sdx(:)) !d^2=dx(S^-1)dx, size of step normalized by error size + + WRITE (message,'(6E14.6)') J_pen, Conv_test, ct2, lambda, d2, ct3 + CALL fckit_log % info(message) + +END DO Iteration_loop + +Ts(:) = T(:) !1DVAR solution temperature + +WRITE (message, '(A,I0)') 'Number of iterations ', it !write out number of iterations done +CALL fckit_log % info(message) +WRITE (message, '(A,F16.4)') 'O-B size ', O_Bdiff +CALL fckit_log % info(message) + +! Output the x(:) that gave the lowest cost function + +IF (J_min < J_pen) THEN + + J_pen = J_min + x(:) = xmin(:) + ycalc(:) = ymin(:) + +END IF + +IF (it <= Iter_max) Converged = .TRUE. + +! Calculate the solution error cov. matrix + +IF (ErrorCode == 0 .AND. it <= iter_max) THEN + + CALL InvertMatrix (nstate, & + nstate, & + Amat(:,:), & + ErrorCode) + + ! Check there isn't an error during the matrix inversion + + IF (ErrorCode /= 0) Do1DVar_Error = .TRUE. + + !calculate degrees of freedom for signal + OK = MATMUL (Om1(:,:) , Kmat(:,:)) ! O^-1K + KOK = MATMUL (TRANSPOSE (Kmat(:,:)) , OK(:,:)) ! KTO^-1K + AKOK = MATMUL (Amat(:,:) , KOK(:,:)) ! AKTO^-1K + DFS = 0.0 ! initialise DFS + !DFS is equal to trace of AKOK + DO i = 1, nstate + DFS = DFS + AKOK(i,i) + END DO + + WRITE (message,'(A,F16.4)') 'DFS', DFS + CALL fckit_log % info(message) +ELSE + + Do1DVar_Error = .TRUE. + +END IF + +IF (ALLOCATED (nr)) DEALLOCATE (nr) +IF (ALLOCATED (dref_dp)) DEALLOCATE (dref_dp) +IF (ALLOCATED (dref_dq)) DEALLOCATE (dref_dq) +IF (ALLOCATED (dnr_dref)) DEALLOCATE (dnr_dref) +IF (ALLOCATED (dalpha_dref)) DEALLOCATE (dalpha_dref) +IF (ALLOCATED (dalpha_dnr)) DEALLOCATE (dalpha_dnr) +IF (ALLOCATED (m1)) DEALLOCATE (m1) + +END SUBROUTINE Ops_GPSRO_rootsolv_BA + +end module ufo_gnssroonedvarcheck_rootsolv_mod diff --git a/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_setom1_mod.f90 b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_setom1_mod.f90 new file mode 100644 index 000000000..eb877e374 --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_setom1_mod.f90 @@ -0,0 +1,137 @@ +!------------------------------------------------------------------------------- +! (C) Crown copyright Met Office. All rights reserved. +! Refer to COPYRIGHT.txt of this distribution for details. +!------------------------------------------------------------------------------- +! Find a solution to the GPSRO inverse problem. +!------------------------------------------------------------------------------- + +module ufo_gnssroonedvarcheck_setom1_mod + +use kinds +use missing_values_mod +use ufo_roobserror_utils_mod, only: Rmatrix_type + +private +public :: Ops_GPSRO_setOM1 + +contains + +SUBROUTINE Ops_GPSRO_setOM1 (nobs, & + zobs, & + yobs, & + Rmatrix, & + OSigma, & + OM1, & + OM1_error) + +USE ufo_utils_mod, ONLY: InvertMatrix + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: nobs +REAL(kind_real), INTENT(IN) :: zobs(:) +REAL(kind_real), INTENT(IN) :: yobs(:) +TYPE (rmatrix_type), INTENT(IN) :: Rmatrix +REAL(kind_real), INTENT(OUT) :: OSigma(:) +REAL(kind_real), INTENT(OUT) :: OM1(:,:) +LOGICAL, INTENT(OUT) :: OM1_error + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "Ops_GPSRO_setOM1" +INTEGER :: n +INTEGER :: i +INTEGER :: ReturnCode +REAL(kind_real) :: frac_err +REAL(kind_real) :: omat(nobs,nobs) +REAL(kind_real), ALLOCATABLE :: gradient(:) + +IF (Rmatrix % satid <= 0) THEN + ! Rmatrix has not been set up + OM1_error = .TRUE. + OSigma(:) = 1 + OM1(:,:) = 1 +ELSE + ALLOCATE (gradient(Rmatrix % num_heights - 1)) + + DO i = 1, Rmatrix % num_heights - 1 + + ! Calculate the gradient of fractional error with height + + gradient(i) = (Rmatrix % frac_err(i + 1) - Rmatrix % frac_err(i)) / & + (Rmatrix % height(i + 1) - Rmatrix % height(i)) + + END DO + + OM1_error = .FALSE. + + ! Initialise covariance matrix + + omat(:,:) = 0.0 + + ! Calculate the variance values + + DO n = 1, nobs + + i = 1 + + DO + + IF (zobs(n) < Rmatrix % height(i + 1) .OR. & + i + 1 >= Rmatrix % num_heights) THEN + EXIT + END IF + + i = i + 1 + + END DO + + ! Fractional error + + frac_err = Rmatrix % frac_err(i) + & + gradient(i) * (zobs(n) - Rmatrix % height(i)) + + ! Standard deviation + + OSigma(n) = MAX (frac_err * yobs(n), Rmatrix % min_error) + + ! Variance + + omat(n,n) = OSigma(n) ** 2 + + END DO + + ! Calculate the covariances + + DO n = 1,nobs + + DO i = n + 1,nobs + + omat(n,i) = SQRT (omat(n,n) * omat(i,i)) * & + EXP (-Rmatrix % clen * ABS (zobs(i) - zobs(n))) + + omat(i,n) = omat(n,i) + + END DO + + END DO + + ! Invert the matrix + + CALL InvertMatrix (nobs, & + nobs, & + omat, & + ReturnCode) + + ! Set the inverse + + OM1(:,:) = omat(:,:) + + IF (ReturnCode /= 0) OM1_error = .TRUE. + + DEALLOCATE (gradient) +END IF + +END SUBROUTINE Ops_GPSRO_setOM1 + +end module ufo_gnssroonedvarcheck_setom1_mod diff --git a/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_utils_mod.f90 b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_utils_mod.f90 new file mode 100644 index 000000000..d7599fd67 --- /dev/null +++ b/src/ufo/filters/gnssroonedvarcheck/ufo_gnssroonedvarcheck_utils_mod.f90 @@ -0,0 +1,310 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +module ufo_gnssroonedvarcheck_utils_mod + +use, intrinsic :: iso_c_binding +use missing_values_mod +use kinds + +implicit none +private +public :: singleob_type, singlebg_type +public :: find_unique +public :: Ops_RealSortQuick +public :: allocate_singleob, deallocate_singleob +public :: allocate_singlebg, deallocate_singlebg + +! Add the ability to hold various data and meta-data for a variable +type element_type + real(kind_real) :: value ! Observed value + real(kind_real) :: OBErr ! Observation error + real(kind_real) :: PGEFinal ! Probability of gross error +end type element_type + +! Structure for the observation information +type :: singleob_type + integer :: niter + integer :: id + real(kind_real) :: jcost + real(kind_real) :: latitude + real(kind_real) :: longitude + type (element_type), allocatable :: p(:) + type (element_type), allocatable :: q(:) + real(kind_real), allocatable :: solutbendingangle(:) + type (element_type), allocatable :: bendingangle(:) + type (element_type), allocatable :: impactparam(:) + type (element_type) :: ro_rad_curv + type (element_type) :: ro_geoid_und + integer, allocatable :: qc_flags(:) +end type + +! Structure for the background (model) information +type :: singlebg_type + real(kind_real), allocatable :: za(:) + real(kind_real), allocatable :: zb(:) + real(kind_real), allocatable :: p(:) + real(kind_real), allocatable :: q(:) +end type + +contains + +!------------------------------------------------------------------------------ +!> Find the unique entries in the input list +!! +!! \author Met Office +!! +!! \date 15/10/2020: Created +!! +subroutine find_unique(input, output) + +integer(c_size_t), intent(in) :: input(:) +integer, allocatable, intent(out) :: output(:) + +integer, allocatable :: unique_vals(:) ! The list of unique elements +integer :: nfound ! The number of unique elements found +integer :: cur_val ! The current value being considered +integer :: max_val ! The maximum value in the input list + +nfound = 0 +allocate(unique_vals(1:size(input))) + +cur_val = minval(input) - 1 +max_val = maxval(input) +do while (cur_val < max_val) + nfound = nfound + 1 + cur_val = minval(input, mask=input>cur_val) + unique_vals(nfound) = cur_val +end do +allocate(output(nfound)) +output = unique_vals(1:nfound) + +end subroutine find_unique + +!------------------------------------------------------------------------------ +!> Generates a index array pointing to the elements of the array 'key' +! in increasing order +!! +!! \author Met Office +!! +!! \date 15/10/2020: Created +!! +! +! Method: +! The heap sort invented by J.W.J.Williams is used. +! A description of the method can be found in 'Numerical Recipes' +! The group information array is used to allow easy sorting on several +! parameters of different types. For details see the Parent Module +! OpsMod_Sort +! +! Inputs: +! key : An array of character strings, to be sorted +! +! Input/Output: +! index : An integer array pointing to the sorted items. +!------------------------------------------------------------------------------- +SUBROUTINE Ops_RealSortQuick(key, & + index) + +IMPLICIT NONE + +! Subroutine arguments: +REAL(kind_real), INTENT(IN) :: key(:) +INTEGER, ALLOCATABLE, INTENT(OUT) :: index(:) + +! Local declarations: +INTEGER :: n ! The number of items +INTEGER :: head ! heaps are tree structures: head and child refer +INTEGER :: child ! to related items within the tree +INTEGER :: j +INTEGER :: dum ! used to swap index items +CHARACTER(len=*), PARAMETER :: RoutineName = 'Ops_RealSortQuick' + +! Could put in an optional mask + +n = SIZE (key) +ALLOCATE (Index(n)) +DO j = 1, n + Index(j) = j +END DO + +! Do heapsort: Create the heap... + +makeheap : DO j = n / 2, 1, -1 + head = j + sift1 : DO + + ! find the largest out of the head and its two children... + + child = head * 2 + IF (child > n) EXIT sift1 + IF (child < n) THEN + IF (key(Index(child + 1)) > key(Index(child))) child = child + 1 + END IF + + ! if the head is the largest, then sift is done... + + IF (key(Index(head)) >= key(Index(child))) EXIT sift1 + + ! otherwise swap to put the largest child at the head, + ! and prepare to repeat the procedure for the head in its new + ! subordinate position. + + dum = Index(child) + Index(child) = Index(head) + Index(head) = dum + head = child + END DO sift1 +END DO makeheap + +! Retire heads of the heap, which are the largest, and +! stack them at the end of the array. + +retire : DO j = n, 2, -1 + dum = Index(1) + Index(1) = Index(j) + Index(j) = dum + head = 1 + + ! second sift is similar to first... + + sift2: DO + child = head * 2 + IF (child > (j - 1)) EXIT sift2 + IF (child < (j - 1)) THEN + IF (key(Index(child + 1)) > key(Index(child))) child = child + 1 + END IF + IF (key(Index(head)) >= key(Index(child))) EXIT sift2 + dum = Index(child) + Index(child) = Index(head) + Index(head) = dum + head = child + END DO sift2 +END DO retire + +END SUBROUTINE Ops_RealSortQuick + + +!------------------------------------------------------------------------------ +!> Allocate the singleob_type structure, given a certain number of observations, +! and model levels for pressure and specific humidity. +!! +!! \author Met Office +!! +!! \date 15/10/2020: Created +!! +subroutine allocate_singleob(singleob, nobs, nlevp, nlevq) + +implicit none + +type(singleob_type), intent(out) :: singleob +integer, intent(in) :: nobs +integer, intent(in) :: nlevp +integer, intent(in) :: nlevq + +allocate(singleob % p(1:nlevp)) +allocate(singleob % q(1:nlevq)) +allocate(singleob % solutbendingangle(1:nobs)) +allocate(singleob % bendingangle(1:nobs)) +allocate(singleob % impactparam(1:nobs)) +allocate(singleob % qc_flags(1:nobs)) + +singleob % solutbendingangle(:) = missing_value(singleob % solutbendingangle(1)) +singleob % qc_flags(:) = 0 ! Set to unflagged + +singleob % p(:) % value = missing_value(singleob % p(1) % value) +singleob % p(:) % oberr = missing_value(singleob % p(1) % oberr) +singleob % p(:) % pgefinal = missing_value(singleob % p(1) % pgefinal) + +singleob % q(:) % value = missing_value(singleob % q(1) % value) +singleob % q(:) % oberr = missing_value(singleob % q(1) % oberr) +singleob % q(:) % pgefinal = missing_value(singleob % q(1) % pgefinal) + +singleob % bendingangle(:) % value = missing_value(singleob % bendingangle(1) % value) +singleob % bendingangle(:) % oberr = missing_value(singleob % bendingangle(1) % oberr) +singleob % bendingangle(:) % pgefinal = missing_value(singleob % bendingangle(1) % pgefinal) + +singleob % impactparam(:) % value = missing_value(singleob % impactparam(1) % value) +singleob % impactparam(:) % oberr = missing_value(singleob % impactparam(1) % oberr) +singleob % impactparam(:) % pgefinal = missing_value(singleob % impactparam(1) % pgefinal) + +end subroutine allocate_singleob + + +!------------------------------------------------------------------------------ +!> Deallocate the singleob_type structure +!! +!! \author Met Office +!! +!! \date 15/10/2020: Created +!! +subroutine deallocate_singleob(singleob) + +implicit none + +type(singleob_type), intent(inout) :: singleob + +deallocate(singleob % p) +deallocate(singleob % q) +deallocate(singleob % solutbendingangle) +deallocate(singleob % bendingangle) +deallocate(singleob % impactparam) +deallocate(singleob % qc_flags) + +end subroutine deallocate_singleob + + +!------------------------------------------------------------------------------ +!> Allocate the structure to hold background information from a single profile. +!! +!! \author Met Office +!! +!! \date 15/10/2020: Created +!! +subroutine allocate_singlebg(singlebg, nlevp, nlevq) + +implicit none + +type(singlebg_type), intent(out) :: singlebg +integer, intent(in) :: nlevp +integer, intent(in) :: nlevq + +allocate(singlebg % za(1:nlevp)) +allocate(singlebg % zb(1:nlevq)) +allocate(singlebg % p(1:nlevp)) +allocate(singlebg % q(1:nlevq)) + +singlebg % za(:) = missing_value(singlebg % za(1)) +singlebg % zb(:) = missing_value(singlebg % zb(1)) +singlebg % p(:) = missing_value(singlebg % p(1)) +singlebg % q(:) = missing_value(singlebg % q(1)) + +end subroutine allocate_singlebg + + +!------------------------------------------------------------------------------ +!> Dealloate the singlebg_type structure +!! +!! \author Met Office +!! +!! \date 15/10/2020: Created +!! +subroutine deallocate_singlebg(singlebg) + +implicit none + +type(singlebg_type), intent(inout) :: singlebg + +deallocate(singlebg % za) +deallocate(singlebg % zb) +deallocate(singlebg % p) +deallocate(singlebg % q) + +end subroutine deallocate_singlebg + +! ------------------------------------------------------------------------------ +end module ufo_gnssroonedvarcheck_utils_mod diff --git a/src/ufo/filters/obsfunctions/BennartzScatIndex.cc b/src/ufo/filters/obsfunctions/BennartzScatIndex.cc new file mode 100755 index 000000000..470185d29 --- /dev/null +++ b/src/ufo/filters/obsfunctions/BennartzScatIndex.cc @@ -0,0 +1,84 @@ +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ioda/ObsDataVector.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/obsfunctions/BennartzScatIndex.h" +#include "ufo/filters/Variable.h" + +namespace ufo { + +static ObsFunctionMaker makerBennartzScatIndex_("BennartzScatIndex"); + +BennartzScatIndex::BennartzScatIndex(const eckit::LocalConfiguration & conf) + : invars_() { + // Initialize options + options_.deserialize(conf); + + // Get channels for computing scattering index from options + channels_ = {options_.ch89.value(), options_.ch150.value()}; + + // Include list of required data from ObsSpace + invars_ += Variable("brightness_temperature@ObsValue", channels_); + if (options_.applyBias.value().size()) { + invars_ += Variable("brightness_temperature@"+options_.applyBias.value(), channels_); + } + invars_ += Variable("sensor_zenith_angle@MetaData"); +} + +// ----------------------------------------------------------------------------- + +void BennartzScatIndex::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + // Get dimension + const size_t nlocs = in.nlocs(); + const float missing = util::missingValue(missing); + + ASSERT(out.nvars() == 1); + + // Get satellite zenith angle + std::vector satzen(nlocs); + in.get(Variable("sensor_zenith_angle@MetaData"), satzen); + + // Get observation values for required channels + std::vector bt89(nlocs), bt150(nlocs); + in.get(Variable("brightness_temperature@ObsValue", channels_)[0], bt89); + in.get(Variable("brightness_temperature@ObsValue", channels_)[1], bt150); + + // Apply bias correction if apply_bias is present in filter options + std::vector bias89(nlocs), bias150(nlocs); + if (options_.applyBias.value().size()) { + in.get(Variable("brightness_temperature@"+options_.applyBias.value(), channels_)[0], bias89); + in.get(Variable("brightness_temperature@"+options_.applyBias.value(), channels_)[1], bias150); + // Apply bias correction + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + bt89[iloc] -= bias89[iloc]; + bt150[iloc] -= bias150[iloc]; + } + } + + // Compute Bennartz scattering index + float Offset; + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + Offset = options_.coeff1.value() + options_.coeff2.value()*satzen[iloc]; + if (bt89[iloc] == missing || bt150[iloc] == missing || satzen[iloc] == missing) { + out[0][iloc] = missing; + } else { + out[0][iloc] = bt89[iloc] - bt150[iloc] - Offset; + } + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & BennartzScatIndex::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/BennartzScatIndex.h b/src/ufo/filters/obsfunctions/BennartzScatIndex.h new file mode 100755 index 000000000..68701d696 --- /dev/null +++ b/src/ufo/filters/obsfunctions/BennartzScatIndex.h @@ -0,0 +1,95 @@ +/* + * (C) Copyright 2019 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_BENNARTZSCATINDEX_H_ +#define UFO_FILTERS_OBSFUNCTIONS_BENNARTZSCATINDEX_H_ + +#include +#include + +#include "oops/util/parameters/NumericConstraints.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variable.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +/// +/// \brief Options for calculating scattering index from 89 GHz and 150 GHz channels. +/// +class BennartzScatIndexParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(BennartzScatIndexParameters, Parameters) + + public: + /// \brief Channel number corresponding to 89 GHz + /// (or nearby frequency depending on sensor channel specification) + /// + /// Example: MHS channel 1 at 89 GHz + /// + /// channel_89ghz: 1 + oops::RequiredParameter ch89{"channel_89ghz", this, {oops::minConstraint(1)}}; + + /// \brief Channel number corresponding to 150 GHz + /// (or nearby frequency depending on sensor channel specification) + /// + /// Example: MHS channel 2 at 157 GHz + /// + /// channel_150ghz: 2 + oops::RequiredParameter ch150{"channel_150ghz", this, {oops::minConstraint(1)}}; + + /// \brief First coefficient used to compute scattering index + /// + /// Offset term is calculated as coeff1 + coeff2*sensor_zenith_angle + oops::RequiredParameter coeff1{"bennartz_coeff_1", this}; + + /// \brief Second coefficient used to compute scattering index + /// + /// Offset term is calculated as coeff1 + coeff2*sensor_zenith_angle + oops::RequiredParameter coeff2{"bennartz_coeff_2", this}; + + /// \brief Name of the bias correction group used to apply correction to ObsValue. + /// + /// Default (missing optional parameter) applies no bias + /// + /// Example: use ObsBias correction values + /// + /// apply_bias: ObsBias + oops::Parameter applyBias{"apply_bias", "", this}; +}; + +/// +/// \brief Calculate scattering index from 89 GHz and 150 GHz channels. +/// +/// Reference: R. Bennartz (2002) +/// Precipitation analysis using the Advanced Microwave Sounding Unit in +/// support of nowcasting applications +/// Meteorol. Appl. 9, 177-189 (2002) DOI:10.1017/S1350482702002037 + +class BennartzScatIndex : public ObsFunctionBase { + public: + explicit BennartzScatIndex(const eckit::LocalConfiguration & + = eckit::LocalConfiguration()); + void compute(const ObsFilterData &, + ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + BennartzScatIndexParameters options_; + std::vector channels_ = {0, 0}; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_BENNARTZSCATINDEX_H_ diff --git a/src/ufo/filters/obsfunctions/BgdDepartureAnomaly.cc b/src/ufo/filters/obsfunctions/BgdDepartureAnomaly.cc new file mode 100755 index 000000000..53af3073f --- /dev/null +++ b/src/ufo/filters/obsfunctions/BgdDepartureAnomaly.cc @@ -0,0 +1,132 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include + +#include "ioda/distribution/Accumulator.h" +#include "ioda/ObsDataVector.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/obsfunctions/BgdDepartureAnomaly.h" +#include "ufo/filters/Variable.h" + +namespace ufo { + +static ObsFunctionMaker makerBgdDepartureAnomaly_("BgdDepartureAnomaly"); + +BgdDepartureAnomaly::BgdDepartureAnomaly(const eckit::LocalConfiguration & conf) + : invars_() { + // Initialize options + options_.deserialize(conf); + + // Get channels for computing the background departure + channels_ = {options_.obslow.value(), options_.obshigh.value()}; + + // Use default or test version of HofX + const std::string & hofxopt = options_.testHofX.value(); + + // Include list of required data from ObsSpace + invars_ += Variable("brightness_temperature@ObsValue", channels_); + invars_ += Variable("brightness_temperature@"+hofxopt, channels_); + + if (options_.ObsBias.value().size()) { + // Get optional bias correction + const std::string & biasopt = options_.ObsBias.value(); + invars_ += Variable("brightness_temperature@"+biasopt, channels_); + } +} + +// ----------------------------------------------------------------------------- + +void BgdDepartureAnomaly::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + // Get dimension + const size_t nlocs = in.nlocs(); + const float missing = util::missingValue(missing); + + // Get obs space + auto & obsdb = in.obsspace(); + + ASSERT(channels_.size() == 2); + + // Get observation values for required channels + const std::string & hofxopt = options_.testHofX.value(); + std::vector obslow(nlocs), obshigh(nlocs); + std::vector bgdlow(nlocs), bgdhigh(nlocs); + in.get(Variable("brightness_temperature@ObsValue", channels_)[0], obslow); + in.get(Variable("brightness_temperature@ObsValue", channels_)[1], obshigh); + in.get(Variable("brightness_temperature@"+hofxopt, channels_)[0], bgdlow); + in.get(Variable("brightness_temperature@"+hofxopt, channels_)[1], bgdhigh); + + // Get bias correction if ObsBias is present in filter options + if (options_.ObsBias.value().size()) { + const std::string & biasopt = options_.ObsBias.value(); + std::vector biaslow(nlocs), biashigh(nlocs); + in.get(Variable("brightness_temperature@"+biasopt, channels_)[0], biaslow); + in.get(Variable("brightness_temperature@"+biasopt, channels_)[1], biashigh); + + // Apply bias correction + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + bgdlow[iloc] -= biaslow[iloc]; + bgdhigh[iloc] -= biashigh[iloc]; + } + } + + // Compute background departures + + // To calculate mean departures, we need to know the number of observations with non-missing + // departures (taking into account all MPI ranks)... + std::unique_ptr> countAccumulator = + obsdb.distribution()->createAccumulator(); + + // ... and the sum of all non-missing departures + enum {OMB_LOW, OMB_HIGH, NUM_OMBS}; + std::unique_ptr>> totalsAccumulator = + obsdb.distribution()->createAccumulator(NUM_OMBS); + + // First, perform local reductions... + std::vector omblow(nlocs), ombhigh(nlocs); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + if (obslow[iloc] == missing || bgdlow[iloc] == missing || + obshigh[iloc] == missing || bgdhigh[iloc] == missing) { + omblow[iloc] = missing; + ombhigh[iloc] = missing; + } else { + omblow[iloc] = obslow[iloc] - bgdlow[iloc]; + ombhigh[iloc] = obshigh[iloc] - bgdhigh[iloc]; + totalsAccumulator->addTerm(iloc, OMB_LOW, omblow[iloc]); + totalsAccumulator->addTerm(iloc, OMB_HIGH, ombhigh[iloc]); + countAccumulator->addTerm(iloc, 1); + } + } + // ... and then global reductions. + const std::size_t count = countAccumulator->computeResult(); + const std::vector totals = totalsAccumulator->computeResult(); + + // Calculate the means + const double MeanOmbLow = totals[OMB_LOW] / count; + const double MeanOmbHigh = totals[OMB_HIGH] / count; + + // Compute anomaly difference + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + if (obslow[iloc] == missing || bgdlow[iloc] == missing || + obshigh[iloc] == missing || bgdhigh[iloc] == missing) { + out[0][iloc] = missing; + } else { + out[0][iloc] = std::abs((omblow[iloc]-MeanOmbLow) - (ombhigh[iloc]-MeanOmbHigh)); + } + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & BgdDepartureAnomaly::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/BgdDepartureAnomaly.h b/src/ufo/filters/obsfunctions/BgdDepartureAnomaly.h new file mode 100755 index 000000000..dd74038c4 --- /dev/null +++ b/src/ufo/filters/obsfunctions/BgdDepartureAnomaly.h @@ -0,0 +1,87 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_BGDDEPARTUREANOMALY_H_ +#define UFO_FILTERS_OBSFUNCTIONS_BGDDEPARTUREANOMALY_H_ + +#include +#include + +#include "oops/util/parameters/NumericConstraints.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variable.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +/// +/// \brief Options for calculating the background departure anomaly between two channels. +/// +class BgdDepartureAnomalyParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(BgdDepartureAnomalyParameters, Parameters) + + public: + /// \brief Lower frequency channel number + /// + /// Example: AMSR2 channel 11 corresponding to 36.5 GHz H-pol + /// + /// channel_low_freq: 11 + oops::RequiredParameter obslow{"channel_low_freq", this, {oops::minConstraint(1)}}; + + /// \brief Higher frequency channel number + /// + /// Example: AMSR2 channel 13 corresponding to 89 GHz H-pol + /// + /// channel_high_freq: 13 + oops::RequiredParameter obshigh{"channel_high_freq", this, {oops::minConstraint(1)}}; + + /// \brief Name of the bias correction group used to apply correction to ObsValue. + /// + /// Default (missing optional parameter) applies no bias + /// + /// Example: use ObsBias correction values + /// + /// ObsBias: ObsBias + oops::Parameter ObsBias{"ObsBias", "", this}; + + /// Name of the HofX group used to replace the default group (default is HofX) + oops::Parameter testHofX{"test_hofx", "HofX", this}; +}; + +/// +/// \brief +/// Hydrometeors scatter radiation more efficiently with increasing microwave frequency. +/// For example, at 89 GHz the scattering due to clouds is sufficient to decrease the top +/// of atmosphere brightness temperature relative to clear skies. At 36 GHz the scattering +/// is reduced, and clouds appear warm relative to surface emissions. +/// +/// The anomaly (O-B minus the mean O-B) difference between these two frequencies +/// is used to diagnose cloudy scenes. +/// + +class BgdDepartureAnomaly : public ObsFunctionBase { + public: + explicit BgdDepartureAnomaly(const eckit::LocalConfiguration & = eckit::LocalConfiguration()); + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + BgdDepartureAnomalyParameters options_; + std::vector channels_ = {0, 0}; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_BGDDEPARTUREANOMALY_H_ diff --git a/src/ufo/filters/obsfunctions/CLWRetMW.cc b/src/ufo/filters/obsfunctions/CLWRetMW.cc index e465f4a17..81e7e0b37 100755 --- a/src/ufo/filters/obsfunctions/CLWRetMW.cc +++ b/src/ufo/filters/obsfunctions/CLWRetMW.cc @@ -32,22 +32,63 @@ CLWRetMW::CLWRetMW(const eckit::LocalConfiguration & conf) // Check required parameters // Get variable group types for CLW retrieval from option ASSERT(options_.varGroup.value().size() == 1 || options_.varGroup.value().size() == 2); + ASSERT((options_.ch238.value() != boost::none && options_.ch314.value() != boost::none) || + (options_.ch37v.value() != boost::none && options_.ch37h.value() != boost::none) || + (options_.ch18v.value() != boost::none && options_.ch18h.value() != boost::none && + options_.ch36v.value() != boost::none && options_.ch36h.value() != boost::none)); - // Get channels for CLW retrieval from options - const std::vector channels_ = {options_.ch238.value(), options_.ch314.value()}; - ASSERT(options_.ch238 != 0 && options_.ch314 != 0 && channels_.size() == 2); + if (options_.ch238.value() != boost::none && options_.ch314.value() != boost::none) { + // For AMSUA and ATMS retrievals + // Get channels for CLW retrieval from options + const std::vector channels = {options_.ch238.value().get(), options_.ch314.value().get()}; + ASSERT(options_.ch238.value().get() != 0 && options_.ch314.value().get() != 0 + && channels.size() == 2); + // Include list of required data from ObsSpace + for (size_t igrp = 0; igrp < options_.varGroup.value().size(); ++igrp) { + invars_ += Variable("brightness_temperature@" + options_.varGroup.value()[igrp], channels); + } + invars_ += Variable("brightness_temperature@" + options_.testBias.value(), channels); + invars_ += Variable("sensor_zenith_angle@MetaData"); - // Include list of required data from ObsSpace - for (size_t igrp = 0; igrp < options_.varGroup.value().size(); ++igrp) { - invars_ += Variable("brightness_temperature@" + options_.varGroup.value()[igrp], channels_); - } - invars_ += Variable("brightness_temperature@" + options_.testBias.value(), channels_); - invars_ += Variable("sensor_zenith_angle@MetaData"); + // Include list of required data from GeoVaLs + invars_ += Variable("average_surface_temperature_within_field_of_view@GeoVaLs"); + invars_ += Variable("water_area_fraction@GeoVaLs"); + invars_ += Variable("surface_temperature_where_sea@GeoVaLs"); + + } else if (options_.ch37v.value() != boost::none && options_.ch37h.value() != boost::none) { + // For cloud index like GMI's. + // Get channels for CLW retrieval from options + const std::vector channels = {options_.ch37v.value().get(), options_.ch37h.value().get()}; - // Include list of required data from GeoVaLs - invars_ += Variable("average_surface_temperature_within_field_of_view@GeoVaLs"); - invars_ += Variable("water_area_fraction@GeoVaLs"); - invars_ += Variable("surface_temperature_where_sea@GeoVaLs"); + ASSERT(options_.ch37v.value().get() != 0 && options_.ch37h.value().get() != 0 && + channels.size() == 2); + // Include list of required data from ObsSpace + for (size_t igrp = 0; igrp < options_.varGroup.value().size(); ++igrp) { + invars_ += Variable("brightness_temperature@" + options_.varGroup.value()[igrp], channels); + } + invars_ += Variable("brightness_temperature@" + options_.testBias.value(), channels); + // Include list of required data from ObsDiag + invars_ += Variable("brightness_temperature_assuming_clear_sky@ObsDiag" , channels); + + // Include list of required data from GeoVaLs + invars_ += Variable("water_area_fraction@GeoVaLs"); + } else if (options_.ch18v.value() != boost::none && options_.ch18h.value() != boost::none && + options_.ch36v.value() != boost::none && options_.ch36h.value() != boost::none) { + const std::vector channels = {options_.ch18v.value().get(), options_.ch18h.value().get(), + options_.ch36v.value().get(), options_.ch36h.value().get()}; + ASSERT(options_.ch18v.value().get() != 0 && options_.ch18h.value().get() != 0 && + options_.ch36v.value().get() != 0 && options_.ch36h.value().get() != 0 && + channels.size() == 4); + const std::vector &sys_bias = options_.origbias.value().get(); + ASSERT(options_.origbias.value() != boost::none); + // Include list of required data from ObsSpace + for (size_t igrp = 0; igrp < options_.varGroup.value().size(); ++igrp) { + invars_ += Variable("brightness_temperature@" + options_.varGroup.value()[igrp], channels); + } + invars_ += Variable("brightness_temperature@" + options_.testBias.value(), channels); + // Include list of required data from GeoVaLs + invars_ += Variable("water_area_fraction@GeoVaLs"); + } } // ----------------------------------------------------------------------------- @@ -60,60 +101,163 @@ void CLWRetMW::compute(const ObsFilterData & in, ioda::ObsDataVector & out) const { // Get required parameters const std::vector &vargrp = options_.varGroup.value(); - const std::vector channels_ = {options_.ch238.value(), options_.ch314.value()}; // Get dimension const size_t nlocs = in.nlocs(); const size_t ngrps = vargrp.size(); - // Get variables from ObsSpace - // Get sensor zenith angle - std::vector szas(nlocs); - in.get(Variable("sensor_zenith_angle@MetaData"), szas); - - // Get variables from GeoVaLs - // Get average surface temperature in FOV - std::vector tsavg(nlocs); - in.get(Variable("average_surface_temperature_within_field_of_view@GeoVaLs"), tsavg); - // Get area fraction of each surface type std::vector water_frac(nlocs); in.get(Variable("water_area_fraction@GeoVaLs"), water_frac); - // Calculate retrieved cloud liquid water - std::vector bt238(nlocs), bt314(nlocs); - for (size_t igrp = 0; igrp < ngrps; ++igrp) { - // Get data based on group type - in.get(Variable("brightness_temperature@" + vargrp[igrp], channels_)[channels_[0]-1], bt238); - in.get(Variable("brightness_temperature@" + vargrp[igrp], channels_)[channels_[1]-1], bt314); - // Get bias based on group type - if (options_.addBias.value() == vargrp[igrp]) { - std::vector bias238(nlocs), bias314(nlocs); - if (in.has(Variable("brightness_temperature@" + options_.testBias.value(), channels_)[0])) { - in.get(Variable("brightness_temperature@" + options_.testBias.value(), channels_) - [channels_[0]-1], bias238); - in.get(Variable("brightness_temperature@" + options_.testBias.value(), channels_) - [channels_[1]-1], bias314); - } else { - bias238.assign(nlocs, 0.0f); - bias314.assign(nlocs, 0.0f); + // --------------- amsua or atms -------------------------- + if (options_.ch238.value() != boost::none && options_.ch314.value() != boost::none) { + const std::vector channels = {options_.ch238.value().get(), options_.ch314.value().get()}; + + // Get variables from ObsSpace + // Get sensor zenith angle + std::vector szas(nlocs); + in.get(Variable("sensor_zenith_angle@MetaData"), szas); + + // Get variables from GeoVaLs + // Get average surface temperature in FOV + std::vector tsavg(nlocs); + in.get(Variable("average_surface_temperature_within_field_of_view@GeoVaLs"), tsavg); + + // Calculate retrieved cloud liquid water + std::vector bt238(nlocs), bt314(nlocs); + for (size_t igrp = 0; igrp < ngrps; ++igrp) { + // Get data based on group type + in.get(Variable("brightness_temperature@" + vargrp[igrp], channels)[0], bt238); + in.get(Variable("brightness_temperature@" + vargrp[igrp], channels)[1], bt314); + // Get bias based on group type + if (options_.addBias.value() == vargrp[igrp]) { + std::vector bias238(nlocs), bias314(nlocs); + if (in.has(Variable("brightness_temperature@" + options_.testBias.value(), channels)[0])) { + in.get(Variable("brightness_temperature@" + options_.testBias.value(), channels)[0], + bias238); + in.get(Variable("brightness_temperature@" + options_.testBias.value(), channels)[1], + bias314); + } else { + bias238.assign(nlocs, 0.0f); + bias314.assign(nlocs, 0.0f); + } + // Add bias correction to the assigned group (only for ObsValue; H(x) already includes bias + // correction + if (options_.addBias.value() == "ObsValue") { + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + bt238[iloc] = bt238[iloc] - bias238[iloc]; + bt314[iloc] = bt314[iloc] - bias314[iloc]; + } + } } - // Add bias correction to the assigned group - if (options_.addBias.value() == "ObsValue") { - for (size_t iloc = 0; iloc < nlocs; ++iloc) { - bt238[iloc] = bt238[iloc] - bias238[iloc]; - bt314[iloc] = bt314[iloc] - bias314[iloc]; + + // Compute the cloud liquid water + cloudLiquidWater(szas, tsavg, water_frac, bt238, bt314, out[igrp]); + } + // -------------------- GMI --------------------------- + } else if (options_.ch37v.value() != boost::none && options_.ch37h.value() != boost::none) { + const std::vector channels = {options_.ch37v.value().get(), options_.ch37h.value().get()}; + // Indices of data at channeles 37v and 37h in the above array "channels" + const int jch37v = 0; + const int jch37h = 1; + std::vector bt_clr_37v(nlocs), bt_clr_37h(nlocs); + in.get(Variable("brightness_temperature_assuming_clear_sky@ObsDiag" , channels) + [jch37v], bt_clr_37v); + in.get(Variable("brightness_temperature_assuming_clear_sky@ObsDiag" , channels) + [jch37h], bt_clr_37h); + // Calculate retrieved cloud liquid water + std::vector bt37v(nlocs), bt37h(nlocs); + for (size_t igrp = 0; igrp < ngrps; ++igrp) { + // Get data based on group type + in.get(Variable("brightness_temperature@" + vargrp[igrp], channels) [jch37v], bt37v); + in.get(Variable("brightness_temperature@" + vargrp[igrp], channels) [jch37h], bt37h); + // Get bias based on group type + if (options_.addBias.value() == vargrp[igrp]) { + std::vector bias37v(nlocs), bias37h(nlocs); + if (in.has(Variable("brightness_temperature@" + options_.testBias.value(), channels) + [jch37v])) { + in.get(Variable("brightness_temperature@" + options_.testBias.value(), channels) + [jch37v], bias37v); + in.get(Variable("brightness_temperature@" + options_.testBias.value(), channels) + [jch37h], bias37h); + } else { + bias37v.assign(nlocs, 0.0f); + bias37h.assign(nlocs, 0.0f); } - } else { - for (size_t iloc = 0; iloc < nlocs; ++iloc) { - bt238[iloc] = bt238[iloc] + bias238[iloc]; - bt314[iloc] = bt314[iloc] + bias314[iloc]; + // Add bias correction to the assigned group (only for ObsValue; H(x) already includes bias + // correction + if (options_.addBias.value() == "ObsValue") { + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + bt37v[iloc] = bt37v[iloc] - bias37v[iloc]; + bt37h[iloc] = bt37h[iloc] - bias37h[iloc]; + } } } + + + // Compute cloud index + CIret_37v37h_diff(bt_clr_37v, bt_clr_37h, water_frac, bt37v, bt37h, out[igrp]); } + // -------------------- amsr2 --------------------------- + } else if (options_.ch18v.value() != boost::none && options_.ch18h.value() != boost::none && + options_.ch36v.value() != boost::none && options_.ch36h.value() != boost::none) { + const std::vector channels = {options_.ch18v.value().get(), options_.ch18h.value().get(), + options_.ch36v.value().get(), options_.ch36h.value().get()}; + const int jch18v = 0; + const int jch18h = 1; + const int jch36v = 2; + const int jch36h = 3; + // systematic bias for channels 1-14. + const std::vector &sys_bias = options_.origbias.value().get(); + // Calculate retrieved cloud liquid water + std::vector bt18v(nlocs), bt18h(nlocs), bt36v(nlocs), bt36h(nlocs); - // Compute the cloud liquid qater - cloudLiquidWater(szas, tsavg, water_frac, bt238, bt314, out[igrp], nlocs); + for (size_t igrp = 0; igrp < ngrps; ++igrp) { + // Get data based on group type + const Variable btVar("brightness_temperature@" + vargrp[igrp], channels); + in.get(btVar[jch18v], bt18v); + in.get(btVar[jch18h], bt18h); + in.get(btVar[jch36v], bt36v); + in.get(btVar[jch36h], bt36h); + // Get bias based on group type + if (options_.addBias.value() == vargrp[igrp]) { + std::vector bias18v(nlocs), bias18h(nlocs); + std::vector bias36v(nlocs), bias36h(nlocs); + if (in.has(Variable("brightness_temperature@" + options_.testBias.value(), channels) + [jch36v])) { + const Variable testBias("brightness_temperature@" + options_.testBias.value(), channels); + in.get(testBias[jch18v], bias18v); + in.get(testBias[jch18h], bias18h); + in.get(testBias[jch36v], bias36v); + in.get(testBias[jch36h], bias36h); + } else { + bias18v.assign(nlocs, 0.0f); + bias18h.assign(nlocs, 0.0f); + bias36v.assign(nlocs, 0.0f); + bias36h.assign(nlocs, 0.0f); + } + // Add bias correction to the assigned group (only for ObsValue; H(x) already includes bias + // correction + if (options_.addBias.value() == "ObsValue") { + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + bt18v[iloc] = bt18v[iloc] - bias18v[iloc]; + bt18h[iloc] = bt18h[iloc] - bias18h[iloc]; + bt36v[iloc] = bt36v[iloc] - bias36v[iloc]; + bt36h[iloc] = bt36h[iloc] - bias36h[iloc]; + } + } + } + // correct systematic bias. + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + bt18v[iloc] = bt18v[iloc] - sys_bias[6]; + bt18h[iloc] = bt18h[iloc] - sys_bias[7]; + bt36v[iloc] = bt36v[iloc] - sys_bias[10]; + bt36h[iloc] = bt36h[iloc] - sys_bias[11]; + } + // Compute cloud index + clw_retr_amsr2(bt18v, bt18h, bt36v, bt36h, out[igrp]); + } } } @@ -124,8 +268,7 @@ void CLWRetMW::cloudLiquidWater(const std::vector & szas, const std::vector & water_frac, const std::vector & bt238, const std::vector & bt314, - std::vector & out, - const std::size_t nlocs) { + std::vector & out) { /// /// \brief Retrieve cloud liquid water from AMSU-A 23.8 GHz and 31.4 GHz channels. /// @@ -136,7 +279,7 @@ void CLWRetMW::cloudLiquidWater(const std::vector & szas, const float t0c = Constants::t0c; const float d1 = 0.754, d2 = -2.265; const float c1 = 8.240, c2 = 2.622, c3 = 1.846; - for (size_t iloc = 0; iloc < nlocs; ++iloc) { + for (size_t iloc = 0; iloc < water_frac.size(); ++iloc) { if (water_frac[iloc] >= 0.99) { float cossza = cos(Constants::deg2rad * szas[iloc]); float d0 = c1 - (c2 - c3 * cossza) * cossza; @@ -154,6 +297,70 @@ void CLWRetMW::cloudLiquidWater(const std::vector & szas, // ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + +void CLWRetMW::CIret_37v37h_diff(const std::vector & bt_clr_37v, + const std::vector & bt_clr_37h, + const std::vector & water_frac, + const std::vector & bt37v, + const std::vector & bt37h, + std::vector & out) { + /// + /// \brief Retrieve cloud index from GMI 37V and 37H channels. + /// + /// GMI cloud index: 1.0 - (Tb_37v - Tb_37h)/(Tb_37v_clr - Tb_37h_clr), in which + /// Tb_37v_clr and Tb_37h_clr for calculated Tb at 37 V and 37H GHz from module values + /// assuming in clear-sky condition. Tb_37v and Tb_37h are Tb observations at 37 V and 37H GHz. + /// + for (size_t iloc = 0; iloc < water_frac.size(); ++iloc) { + if (water_frac[iloc] >= 0.99) { + if (bt37h[iloc] <= bt37v[iloc]) { + out[iloc] = 1.0 - (bt37v[iloc] - bt37h[iloc])/(bt_clr_37v[iloc] - bt_clr_37h[iloc]); + out[iloc] = std::max(0.f, out[iloc]); + } else { + out[iloc] = getBadValue(); + } + } else { + out[iloc] = getBadValue(); + } + } +} + +// ----------------------------------------------------------------------------- +/// \brief Retrieve AMSR2_GCOM-W1 cloud liquid water. +/// This retrieval function is taken from the subroutine "retrieval_amsr2()" in GSI. +void CLWRetMW::clw_retr_amsr2(const std::vector & bt18v, + const std::vector & bt18h, + const std::vector & bt36v, + const std::vector & bt36h, + std::vector & out) { + float clw; + std::vector pred_var_clw(2); + // intercepts + const float a0_clw = -0.65929; + // regression coefficients + float regr_coeff_clw[3] = {-0.00013, 1.64692, -1.51916}; + + for (size_t iloc = 0; iloc < bt18v.size(); ++iloc) { + if (bt18v[iloc] <= bt18h[iloc]) { + out[iloc] = getBadValue(); + } else if (bt36v[iloc] <= bt36h[iloc]) { + out[iloc] = getBadValue(); + } else { + // Calculate predictors + pred_var_clw[0] = log(bt18v[iloc] - bt18h[iloc]); + pred_var_clw[1] = log(bt36v[iloc] - bt36h[iloc]); + clw = a0_clw + bt36h[iloc]*regr_coeff_clw[0]; + for (size_t nvar_clw=0; nvar_clw < pred_var_clw.size(); ++nvar_clw) { + clw = clw + (pred_var_clw[nvar_clw] * regr_coeff_clw[nvar_clw+1]); + } + clw = std::max(0.0f, clw); + clw = std::min(6.0f, clw); + out[iloc] = clw; + } + } +} +// ----------------------------------------------------------------------------- const ufo::Variables & CLWRetMW::requiredVariables() const { return invars_; } diff --git a/src/ufo/filters/obsfunctions/CLWRetMW.h b/src/ufo/filters/obsfunctions/CLWRetMW.h index ec3d1f09f..3428a1f16 100755 --- a/src/ufo/filters/obsfunctions/CLWRetMW.h +++ b/src/ufo/filters/obsfunctions/CLWRetMW.h @@ -11,6 +11,7 @@ #include #include +#include "oops/util/parameters/OptionalParameter.h" #include "oops/util/parameters/Parameter.h" #include "oops/util/parameters/Parameters.h" #include "oops/util/parameters/RequiredParameter.h" @@ -33,13 +34,13 @@ class CLWRetMWParameters : public oops::Parameters { /// of cloud liquid water applies /// Example: AMSU-A channel numbers used in cloud liquid water retrieval /// clwret_channels: 1 - oops::RequiredParameter ch238{"clwret_ch238", this}; + oops::OptionalParameter ch238{"clwret_ch238", this}; /// channel number corresponding to 31.4 GHz to which the retrieval /// of cloud liquid water applies /// Example: AMSU-A channel numbers used in cloud liquid water retrieval /// clwret_channels: 2 - oops::RequiredParameter ch314{"clwret_ch314", this}; + oops::OptionalParameter ch314{"clwret_ch314", this}; /// Names of the data group used to retrieve the cloud liquid water /// Example: get retrieved CLW from observation and simulated observation respectively @@ -60,6 +61,20 @@ class CLWRetMWParameters : public oops::Parameters { /// Example: use observation bias correction values from GSI /// test_bias: GsiObsBias oops::Parameter testBias{"test_bias", "ObsBias", this}; + + /// Cloud index CIret_37v37h_diff: + /// 1.0 - (Tb_37v - Tb_37h)/(Tb_37v_clr - Tb_37h_clr), which is used in + /// all-sky DA. Tb_37v_clr and Tb_37h_clr for calculated Tb at 37V and 37H GHz from model values + /// assuming in clear-sky condition. Tb_37v and Tb_37h are Tb observations at 37 V and 37H GHz. + oops::OptionalParameter ch37h{"clwret_ch37h", this}; + oops::OptionalParameter ch37v{"clwret_ch37v", this}; + + /// For retrieving AMSR2 cloud liquid water. + oops::OptionalParameter ch18h{"clwret_ch18h", this}; + oops::OptionalParameter ch18v{"clwret_ch18v", this}; + oops::OptionalParameter ch36h{"clwret_ch36h", this}; + oops::OptionalParameter ch36v{"clwret_ch36v", this}; + oops::OptionalParameter> origbias{"sys_bias", this}; }; /// @@ -87,9 +102,20 @@ class CLWRetMW : public ObsFunctionBase { const std::vector &, const std::vector &, const std::vector &, - std::vector &, - const std::size_t); + std::vector &); + static void CIret_37v37h_diff(const std::vector &, + const std::vector &, + const std::vector &, + const std::vector &, + const std::vector &, + std::vector &); + static void clw_retr_amsr2(const std::vector &, + const std::vector &, + const std::vector &, + const std::vector &, + std::vector &); inline static float getBadValue() {return bad_clwret_value_;} + private: ufo::Variables invars_; CLWRetMWParameters options_; diff --git a/src/ufo/filters/obsfunctions/CLWRetMW_SSMIS.cc b/src/ufo/filters/obsfunctions/CLWRetMW_SSMIS.cc new file mode 100644 index 000000000..fe159b49d --- /dev/null +++ b/src/ufo/filters/obsfunctions/CLWRetMW_SSMIS.cc @@ -0,0 +1,210 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/CLWRetMW_SSMIS.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/Variable.h" + +namespace ufo { + +static ObsFunctionMaker makerCLWRetMW_SSMIS_("CLWRetMW_SSMIS"); + +CLWRetMW_SSMIS::CLWRetMW_SSMIS(const eckit::LocalConfiguration & conf) + : invars_() { + // Initialize options + options_.deserialize(conf); + + channels_ = {options_.ch19h.value(), options_.ch19v.value(), + options_.ch22v.value(), options_.ch37h.value(), + options_.ch37v.value(), options_.ch91v.value(), + options_.ch91h.value()}; + + // Include list of required data from ObsSpace + invars_ += Variable("brightness_temperature@" + options_.varGroup.value(), channels_); + + // Include list of required data from GeoVaLs + invars_ += Variable("water_area_fraction@GeoVaLs"); +} + +// ----------------------------------------------------------------------------- + +CLWRetMW_SSMIS::~CLWRetMW_SSMIS() {} + +// ----------------------------------------------------------------------------- + +void CLWRetMW_SSMIS::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + // Get required parameters + const std::string &vargrp = options_.varGroup.value(); + + // Get dimension + const size_t nlocs = in.nlocs(); + + // Get area fraction of each surface type + std::vector water_frac(nlocs); + in.get(Variable("water_area_fraction@GeoVaLs"), water_frac); + + std::vector bt19h(nlocs), bt19v(nlocs), bt22v(nlocs), bt37h(nlocs), + bt37v(nlocs), bt91v(nlocs), bt91h(nlocs); + + in.get(Variable("brightness_temperature@" + vargrp, channels_)[0], bt19h); + in.get(Variable("brightness_temperature@" + vargrp, channels_)[1], bt19v); + in.get(Variable("brightness_temperature@" + vargrp, channels_)[2], bt22v); + in.get(Variable("brightness_temperature@" + vargrp, channels_)[3], bt37h); + in.get(Variable("brightness_temperature@" + vargrp, channels_)[4], bt37v); + in.get(Variable("brightness_temperature@" + vargrp, channels_)[5], bt91v); + in.get(Variable("brightness_temperature@" + vargrp, channels_)[6], bt91h); + + // Compute cloud liquid water amount + cloudLiquidWater(bt19h, bt19v, bt22v, bt37h, bt37v, bt91v, bt91h, water_frac, out[0]); +} + +// ----------------------------------------------------------------------------- + +void CLWRetMW_SSMIS::cloudLiquidWater(const std::vector & bt19h, + const std::vector & bt19v, + const std::vector & bt22v, + const std::vector & bt37h, + const std::vector & bt37v, + const std::vector & bt91v, + const std::vector & bt91h, + std::vector & water_frac, + std::vector & clw) { + /// + /// \brief Retrieve cloud liquid water from channels 12-18 of SSMIS. Output is in + /// kg/m2 (mm) and bound between zero and 6.0. + /// + /// Reference: + /// Weng, F., R. R. Ferraro, and N. C. Grody,2000: "Effects of AMSU cross-scan Symmetry of + /// brightness temperatures on retrieval of atmospheric and surface parameters", + /// Ed. P. Pampaloni and S. Paloscia, VSP, Netherlands, 255-262, 2000. + /// Yan B. and F. Weng, 'Intercalibration between Special Sensor Microwave Imager and + /// Sounder (SSMIS) and Special Sensor Microwave Imager (SSM/I)', TGARS Special + /// Issue on the DMSP SSMIS, 46, 984-995. + + const float missing = util::missingValue(missing); + const size_t nchannels = 7; + const size_t nlocs = bt19h.size(); + + // Initialize output to missing. + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + clw[iloc] = missing; + } + + // SSMIS information about channels and frequecies came from + // http://rain.atmos.colostate.edu/FCDR/ssmis.html + // Channels 12-18: 19.35h, 19.35v, 22.235, 37h, 37v, 91.655h, 91.655v GHz. + // Various coefficients and equations from GSI subroutine ret_ssmis. + + const std::vector ap = {0.00424, -2.03627, -2.52875, 0.80170, -3.86053, -7.43913, 1.53650}; + const std::vector bp = {1.00027, 1.00623, 0.99642, 0.99139, 1.00550, 1.03121, 0.99317}; + const std::vector cp0 = {0.969, 0.969, 0.974, 0.986, 0.986, 0.988, 0.988}; + const std::vector dp0 = {0.00415, 0.00473, 0.0107, 0.02612, 0.0217, 0.01383, 0.01947}; + + std::vector cp(nchannels), dp(nchannels); + for (size_t i = 0; i < cp0.size(); ++i) { + cp[i] = 1.0 / (cp0[i]*(1.0-dp0[i])); + dp[i] = cp[i]*dp0[i]; + } + + // Setting the tax array to a relatively large number in the event that any missing data + // will fail the IF-tests of brightness temperatures less than 285K, ensures the output + // CLW is missing also. + + std::vector bt_test(nchannels); + std::vector > tax(nlocs, std::vector (nchannels)); + std::vector > tay(nlocs, std::vector (nchannels)); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + const std::vector btest = {bt19h[iloc], bt19v[iloc], bt22v[iloc], bt37h[iloc], + bt37v[iloc], bt91v[iloc], bt91h[iloc]}; + if (std::any_of(btest.begin(), btest.end(), [](float x){return (x <= 0.0f || x >= 300.0f);})) { + for (size_t ich = 0; ich < nchannels; ++ich) { + tax[iloc][ich] = 999.0f; + } + } else { + tax[iloc][0] = (bt19h[iloc]*cp[1] + bt19v[iloc]*dp[0])/(cp[0]*cp[1] - dp[0]*dp[1]); + tax[iloc][1] = (bt19h[iloc]*dp[1] + bt19v[iloc]*cp[0])/(cp[0]*cp[1] - dp[0]*dp[1]); + tax[iloc][2] = 1.0/cp[2]*(bt22v[iloc] + dp[2]*(0.653*tax[iloc][1] + 96.6)); + tax[iloc][3] = (bt37h[iloc]*cp[4] + bt37v[iloc]*dp[3])/(cp[3]*cp[4] - dp[3]*dp[4]); + tax[iloc][4] = (bt37h[iloc]*dp[4] + bt37v[iloc]*cp[3])/(cp[3]*cp[4] - dp[3]*dp[4]); + tax[iloc][5] = (bt91v[iloc]*cp[6] + bt91h[iloc]*dp[5])/(cp[5]*cp[6] - dp[5]*dp[6]); + tax[iloc][6] = (bt91v[iloc]*dp[6] + bt91h[iloc]*cp[5])/(cp[5]*cp[6] - dp[5]*dp[6]); + } + + for (size_t ich = 0; ich < nchannels; ++ich) { + tay[iloc][ich] = ap[ich] + bp[ich]*tax[iloc][ich]; + } + } + + float alg1, alg2, alg3; + float tby1, tby3, tby4, tpwc; + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + if (water_frac[iloc] >= 0.99) { + clw[iloc] = 0.0; + alg1 = 0.0, alg2 = 0.0, alg3 = 0.0; + tby1 = 0.0, tby3 = 0.0, tby4 = 0.0, tpwc = 0.0; + // Try the quickest answer related to channels 2 and 3. + alg1 = 0.0; + if (tay[iloc][1] < 285.0 && tay[iloc][2] < 285.0) { + alg1 = -3.20*(std::log(290.0f-tay[iloc][1]) - 2.80 - 0.42*std::log(290.0f-tay[iloc][2])); + } + if (alg1 > 0.70) { + clw[iloc] = alg1; + clw[iloc] = std::max(0.0f, std::min(alg1, 6.0f)); + } else { + // Try the next quickest answer related to channels 3 and 5. + alg2 = 0.0; + if (tay[iloc][4] < 285.0 && tay[iloc][2] < 285.0) { + alg2 = -1.66*(std::log(290.0f-tay[iloc][4]) - 2.90 - 0.349*std::log(290.0f-tay[iloc][2])); + } + if (alg2 > 0.28) { + clw[iloc] = alg2; + clw[iloc] = std::max(0.0f, std::min(alg2, 6.0f)); + } else { + // Final test using channels 3 and 7, but we first need total precipitable water. + tby1 = cp[0]*tay[iloc][0] - dp[0]*tay[iloc][1]; + tby3 = cp[2]*tay[iloc][2] - dp[2]*(0.653*tay[iloc][1] + 96.6); + tby4 = cp[3]*tay[iloc][3] - dp[3]*tay[iloc][4]; + tpwc = 232.89 - 0.1486*tby1 - 0.3695*tby4 - (1.8291 - 0.006193*tby3)*tby3; + + if (tpwc < 30.0) { + alg3 = 0; + if (tay[iloc][6] < 285.0 && tay[iloc][2] < 285.0) { + alg3 = -0.44*(std::log(290.f-tay[iloc][6]) + 1.60 + - 1.354*std::log(290.f-tay[iloc][2])); + clw[iloc] = alg3; + clw[iloc] = std::max(0.0f, std::min(alg3, 6.0f)); + } + } else if (alg2 > 0.0) { + clw[iloc] = alg2; + clw[iloc] = std::max(0.0f, std::min(alg2, 6.0f)); + } + } + } + } + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & CLWRetMW_SSMIS::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/CLWRetMW_SSMIS.h b/src/ufo/filters/obsfunctions/CLWRetMW_SSMIS.h new file mode 100644 index 000000000..9630c726b --- /dev/null +++ b/src/ufo/filters/obsfunctions/CLWRetMW_SSMIS.h @@ -0,0 +1,83 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_CLWRETMW_SSMIS_H_ +#define UFO_FILTERS_OBSFUNCTIONS_CLWRETMW_SSMIS_H_ + +#include +#include + +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" + +namespace ufo { + +/// +/// \brief Option to override varGroup default of ObsValue for all channels with ObsBias +/// +class CLWRetMW_SSMISParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(CLWRetMW_SSMISParameters, Parameters) + + public: + /// Names of the data group used to retrieve the cloud liquid water + /// Example: get retrieved channels from observation, ObsValue (default) or bias-corrected obs. + oops::Parameter varGroup{"varGroup", "ObsValue", this}; + oops::RequiredParameter ch19h{"ch19h", this}; + oops::RequiredParameter ch19v{"ch19v", this}; + oops::RequiredParameter ch22v{"ch22v", this}; + oops::RequiredParameter ch37h{"ch37h", this}; + oops::RequiredParameter ch37v{"ch37v", this}; + oops::RequiredParameter ch91v{"ch91v", this}; + oops::RequiredParameter ch91h{"ch91h", this}; +}; + +/// +/// \brief Retrieve cloud liquid water from channels 12-18 of SSMIS data. +/// +/// Reference: +/// Weng, F., R. R. Ferraro, and N. C. Grody,2000: "Effects of AMSU cross-scan Symmetry of +/// brightness temperatures on retrieval of atmospheric and surface parameters", +/// Ed. P. Pampaloni and S. Paloscia, VSP, Netherlands, 255-262, 2000. +/// Yan B. and F. Weng, 'Intercalibration between Special Sensor Microwave Imager and +/// Sounder (SSMIS) and Special Sensor Microwave Imager (SSM/I)', TGARS Special +/// Issue on the DMSP SSMIS, 46, 984-995. +/// +class CLWRetMW_SSMIS : public ObsFunctionBase { + public: + explicit CLWRetMW_SSMIS(const eckit::LocalConfiguration & + = eckit::LocalConfiguration()); + ~CLWRetMW_SSMIS(); + + void compute(const ObsFilterData &, + ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + static void cloudLiquidWater(const std::vector &, + const std::vector &, + const std::vector &, + const std::vector &, + const std::vector &, + const std::vector &, + const std::vector &, + std::vector &, + std::vector &); + + private: + ufo::Variables invars_; + CLWRetMW_SSMISParameters options_; + std::vector channels_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_CLWRETMW_SSMIS_H_ diff --git a/src/ufo/filters/obsfunctions/CloudCostFunction.cc b/src/ufo/filters/obsfunctions/CloudCostFunction.cc new file mode 100755 index 000000000..ab11430aa --- /dev/null +++ b/src/ufo/filters/obsfunctions/CloudCostFunction.cc @@ -0,0 +1,251 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "oops/util/IntSetParser.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/obsfunctions/CloudCostFunction.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/ufo_utils.interface.h" + +namespace ufo { + +static ObsFunctionMaker makerCloudCostFunction_("CloudCostFunction"); + +CloudCostFunction::CloudCostFunction(const eckit::LocalConfiguration & conf) + : invars_() { + // Initialize options + options_.deserialize(conf); + + // List of field names for B matrix + fields_ = options_.field_names.value(); + + // Get channels for computing scattering index from options + std::set chanset = oops::parseIntSet(options_.chanlist.value()); + channels_.assign(chanset.begin(), chanset.end()); + + // List of required data + for (size_t i = 0; i < fields_.size(); ++i) { + invars_ += Variable("brightness_temperature_jacobian_"+fields_[i]+"@ObsDiag", channels_); + } + invars_ += Variable("brightness_temperature@ObsValue", channels_); + invars_ += Variable("brightness_temperature@"+options_.HofXGroup.value(), channels_); + invars_ += Variable("latitude@MetaData"); + if (options_.qtotal_lnq_gkg.value()) { + invars_ += Variable("specific_humidity@GeoVaLs"); + invars_ += Variable("mass_content_of_cloud_liquid_water_in_atmosphere_layer@GeoVaLs"); + invars_ += Variable("mass_content_of_cloud_ice_in_atmosphere_layer@GeoVaLs"); + invars_ += Variable("air_pressure@GeoVaLs"); + invars_ += Variable("air_temperature@GeoVaLs"); + invars_ += Variable("surface_pressure@GeoVaLs"); + invars_ += Variable("surface_temperature@GeoVaLs"); + invars_ += Variable("specific_humidity_at_two_meters_above_surface@GeoVaLs"); + } +} + +// ----------------------------------------------------------------------------- + +void CloudCostFunction::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + // Get dimensions + const size_t nlocs = in.nlocs(); + const size_t nchans = channels_.size(); + ASSERT(nchans > 0); + ASSERT(out.nvars() == 1); + + // B, R error covariance objects + eckit::LocalConfiguration bMatrixConf; + bMatrixConf.set("BMatrix", options_.bmatrix_filepath.value()); + bMatrixConf.set("background fields", options_.field_names.value()); + bMatrixConf.set("qtotal", options_.qtotal_lnq_gkg.value()); + MetOfficeBMatrixStatic staticB(bMatrixConf); + + eckit::LocalConfiguration rMatrixConf; + rMatrixConf.set("RMatrix", options_.rmatrix_filepath.value()); + MetOfficeRMatrixRadiance staticR(rMatrixConf); + + bool split_rain = options_.qtotal_split_rain.value(); + + const std::string clw_name = "mass_content_of_cloud_liquid_water_in_atmosphere_layer"; + const std::string ciw_name = "mass_content_of_cloud_ice_in_atmosphere_layer"; + std::vector gv_pres(nlocs), gv_temp(nlocs), gv_qgas(nlocs), gv_clw(nlocs), gv_ciw(nlocs), + humidity_total(nlocs); + + // Determine if pressure is ascending or descending (B-matrix assumption) + size_t np = in.nlevs(Variable("air_pressure@GeoVaLs")); + std::vector gv_pres_1(nlocs), gv_pres_N(nlocs); + in.get(Variable("air_pressure@GeoVaLs"), 1, gv_pres_1); + in.get(Variable("air_pressure@GeoVaLs"), np, gv_pres_N); + const float missing = util::missingValue(missing); + ASSERT(gv_pres_1[0] != missing); + ASSERT(gv_pres_N[0] != missing); + bool p_ascending = (gv_pres_N[0] > gv_pres_1[0]); + + // Assemble combined Jacobian from component fields + std::vector>> + jac_vec(nlocs, std::vector>(nchans, std::vector())); + for (size_t ifield = 0; ifield < fields_.size(); ++ifield) { + if (options_.qtotal_lnq_gkg.value() && + (fields_[ifield] == clw_name || fields_[ifield] == ciw_name)) { + // qtotal ln(g/kg) Jacobian calculated when "specific_humidity" is reached in field list + continue; + } + std::string jac_name = "brightness_temperature_jacobian_"+fields_[ifield]+"@ObsDiag"; + size_t nlevs = in.nlevs(Variable(jac_name, channels_)[0]); + std::vector jac_store(nlocs); + for (size_t ilev = 0; ilev < nlevs; ++ilev) { + int level_gv = (p_ascending ? ilev+1 : nlevs-ilev); + int level_jac = (options_.reverse_Jacobian.value() ? nlevs-level_gv+1 : level_gv); + if (fields_[ifield] == "specific_humidity" && options_.qtotal_lnq_gkg.value()) { + in.get(Variable("air_pressure@GeoVaLs"), level_gv, gv_pres); + in.get(Variable("air_temperature@GeoVaLs"), level_gv, gv_temp); + in.get(Variable("specific_humidity@GeoVaLs"), level_gv, gv_qgas); + in.get(Variable(clw_name+"@GeoVaLs"), level_gv, gv_clw); + in.get(Variable(ciw_name+"@GeoVaLs"), level_gv, gv_ciw); + std::vector qsaturated(nlocs); + ufo_ops_satrad_qsatwat_f90(qsaturated.data(), gv_temp.data(), gv_pres.data(), + static_cast(nlocs)); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + // Ensure specific humidity is within limits + gv_qgas[iloc] = std::max(gv_qgas[iloc], options_.min_q.value()); + gv_qgas[iloc] = std::min(gv_qgas[iloc], qsaturated[iloc]); + humidity_total[iloc] = gv_qgas[iloc] + gv_clw[iloc] + gv_ciw[iloc]; + } + } + + for (size_t ichan = 0; ichan < nchans; ++ichan) { + in.get(Variable(jac_name, channels_)[ichan], level_jac, jac_store); + + if (fields_[ifield] == "specific_humidity" && options_.qtotal_lnq_gkg.value()) { + std::vector jac_clw(nlocs), jac_ciw(nlocs); + in.get(Variable("brightness_temperature_jacobian_"+clw_name+"@ObsDiag", channels_)[ichan], + level_jac, jac_clw); + in.get(Variable("brightness_temperature_jacobian_"+ciw_name+"@ObsDiag", channels_)[ichan], + level_jac, jac_ciw); + std::vector dq_dqtotal(nlocs), dql_dqtotal(nlocs), dqi_dqtotal(nlocs); + int qsplit_mode = 2; // compute derivatives + ufo_ops_satrad_qsplit_f90(qsplit_mode, static_cast(nlocs), gv_pres.data(), + gv_temp.data(), humidity_total.data(), dq_dqtotal.data(), + dql_dqtotal.data(), dqi_dqtotal.data(), split_rain); + // Jacobian dy/dx for observation y, humdity x in units kg/kg + // For alternative units of B-matrix humidity z in ln(g/kg) + // chain rule gives dy/dz = x.(dy/dx) + // Gradient due to ice is ignored unless we are using scattering radiative transfer + // dTb/dln(qt) = qt*(dTb/dq*dq/dqt + dTb/dql*dql/dqt [+ dTb/dqi*dqi/dqt]) + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + jac_store[iloc] *= dq_dqtotal[iloc]; + jac_store[iloc] += jac_clw[iloc]*dql_dqtotal[iloc]; + if (options_.scattering_switch.value()) { + jac_store[iloc] += jac_ciw[iloc]*dqi_dqtotal[iloc]; + } + jac_store[iloc] *= humidity_total[iloc]; + } + } + + if (fields_[ifield] == "specific_humidity_at_two_meters_above_surface" + && options_.qtotal_lnq_gkg.value()) { + in.get(Variable("surface_pressure@GeoVaLs"), level_gv, gv_pres); + in.get(Variable("surface_temperature@GeoVaLs"), level_gv, gv_temp); + in.get(Variable("specific_humidity_at_two_meters_above_surface@GeoVaLs"), + level_gv, gv_qgas); + std::vector qsaturated(nlocs); + ufo_ops_satrad_qsatwat_f90(qsaturated.data(), gv_temp.data(), gv_pres.data(), + static_cast(nlocs)); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + gv_qgas[iloc] = std::max(gv_qgas[iloc], options_.min_q.value()); + gv_qgas[iloc] = std::min(gv_qgas[iloc], qsaturated[iloc]); + // dTb/dln(q2m) = q2m*(dTb/dq2m) + jac_store[iloc] *= gv_qgas[iloc]; + } + } + + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + jac_vec[iloc][ichan].push_back(jac_store[iloc]); + } + } + } + } + + const size_t sizeB = staticB.getsize(); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + for (size_t ichan = 0; ichan < nchans; ++ichan) { + ASSERT(jac_vec[iloc][ichan].size() == sizeB); + } + } + + // Get departures = ObsValue - HofX (where HofX is bias corrected) + std::vector> departures(nlocs, std::vector(nchans)); + std::vector obsvalues(nlocs); + std::vector bgvalues(nlocs); + std::vector is_out_of_bounds(nlocs, false); + for (size_t ichan = 0; ichan < nchans; ++ichan) { + in.get(Variable("brightness_temperature@ObsValue", channels_)[ichan], obsvalues); + in.get(Variable("brightness_temperature@"+options_.HofXGroup.value(), channels_)[ichan], + bgvalues); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + departures[iloc][ichan] = obsvalues[iloc] - bgvalues[iloc]; + // Flag observations outside expected bounds + if (obsvalues[iloc] < options_.minTb.value() || obsvalues[iloc] > options_.maxTb.value()) { + is_out_of_bounds[iloc] = true; + } + } + } + + std::vector latitude(nlocs); + in.get(Variable("latitude@MetaData"), latitude); + Eigen::MatrixXf Hmatrix(nchans, sizeB); + + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + if (is_out_of_bounds[iloc]) { + out[0][iloc] = options_.maxCost.value(); + continue; + } + + for (size_t ichan = 0; ichan < nchans; ++ichan) { + Hmatrix.row(ichan) = Eigen::Map(jac_vec[iloc][ichan].data(), sizeB); + } + + // Matrix of departures dy + Eigen::Map dy(departures[iloc].data(), nchans); + + // Calculate Scratch_matrix = H.B.H^T + R + Eigen::MatrixXf BHT; + staticB.multiply(latitude[iloc], Hmatrix.transpose(), BHT); + Eigen::MatrixXf HBHT = Hmatrix*BHT; + Eigen::MatrixXf Scratch_matrix; + staticR.add(channels_, HBHT, Scratch_matrix); + + // Calculate Scratch_matrix2 = Scratch_matrix^-1.dy using Cholesky decomposition + Eigen::LLT decomposition(Scratch_matrix); + if (decomposition.info() == Eigen::NumericalIssue) { + oops::Log::warning() << + "CloudCostFunction Scratch_matrix appears not to be positive definite" << std::endl; + out[0][iloc] = options_.maxCost.value(); + continue; + } + Eigen::VectorXf Scratch_matrix2 = decomposition.solve(dy); + + // Final cost + float Cost_final = 0.5*dy.transpose()*Scratch_matrix2; + Cost_final /= static_cast(nchans); // normalise by number of channels + out[0][iloc] = std::min(Cost_final, options_.maxCost.value()); + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & CloudCostFunction::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/CloudCostFunction.h b/src/ufo/filters/obsfunctions/CloudCostFunction.h new file mode 100755 index 000000000..1dd061ebf --- /dev/null +++ b/src/ufo/filters/obsfunctions/CloudCostFunction.h @@ -0,0 +1,132 @@ +/* + * (C) Crown Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_CLOUDCOSTFUNCTION_H_ +#define UFO_FILTERS_OBSFUNCTIONS_CLOUDCOSTFUNCTION_H_ + +#include +#include + +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/metoffice/MetOfficeBMatrixStatic.h" +#include "ufo/utils/metoffice/MetOfficeRMatrixRadiance.h" + +namespace ufo { + +/// +/// \brief Options for calculating Bayesian cost function. +/// +class CloudCostFunctionParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(CloudCostFunctionParameters, Parameters) + + public: + /// Set of channels used in the calculation of the cost function + oops::RequiredParameter chanlist{"cost channels list", this}; + + /// Path to location of file describing the R-matrix + oops::RequiredParameter rmatrix_filepath{"RMatrix", this}; + + /// Path to location of file describing the B-matrix + oops::RequiredParameter bmatrix_filepath{"BMatrix", this}; + + /// List of geovals describing fields required from the B-matrix + oops::RequiredParameter> + field_names{"background fields", this}; + + /// \brief B-matrix file contains error covariances for ln(qtotal in units g/kg) + /// + /// Setting this flag for qtotal requires that the following are all present + /// in the parameter list "background fields": + /// - specific_humidity + /// - mass_content_of_cloud_liquid_water_in_atmosphere_layer + /// - mass_content_of_cloud_ice_in_atmosphere_layer + oops::Parameter qtotal_lnq_gkg{"qtotal", false, this}; + + /// Include treatment of rain when splitting total humidity into constituent phases + oops::Parameter qtotal_split_rain{"qtotal split rain", false, this}; + + /// Include gradient due to ice in brightness temperature total humidity Jacobian + oops::Parameter scattering_switch{"scattering radiative transfer", false, this}; + + /// Limit specific humidity to minimum value + oops::Parameter min_q{"minimum specific humidity", 3.0e-6f, this}; + + /// Jacobian vertical ordering is reverse of geovals + oops::Parameter reverse_Jacobian{"reverse Jacobian order", false, this}; + + /// Minimum bound for ObsValue brightness temperature + oops::Parameter minTb{"minimum ObsValue", 70.0, this}; + + /// Maximum bound for ObsValue brightness temperature + oops::Parameter maxTb{"maximum ObsValue", 340.0, this}; + + /// Maximum value for final cost returned by the ObsFunction + oops::Parameter maxCost{"maximum final cost", 1600.0, this}; + + /// \brief Name of the H(x) group used in the cost function calculation. + /// + /// H(x) is assumed to be already bias corrected, the default is "HofX" + /// + /// Example: use + /// + /// HofX group: MetOfficeBiasCorrHofX + oops::Parameter HofXGroup{"HofX group", "HofX", this}; +}; + +/// +/// \brief Bayesian cost function for detecting cloud-affected radiances. +/// +/// The cloud cost, Jc, is calculated from observation-H(x) departures, y, via +/// +/// Jc = (0.5/Nchan) * y.W.y^T +/// +/// where Nchan is the number of channels in the calculation and +/// W is the inverse of (H.B.H^T + R): +/// +/// H is the Jacobian matrix; +/// B is a background error covariance matrix; +/// R is an observation error covariance matrix. +/// +/// The heritage of this code is the Met Office routine Ops_SatRad_CloudCost. +/// +/// Implementation here follows Met Office usage, with a static (latitude-varying) +/// B-matrix and a fixed, diagonal R-matrix. +/// +/// Reference: S.J. English, J.R. Eyre and J.A. Smith. +/// A cloud‐detection scheme for use with satellite sounding radiances in the +/// context of data assimilation for numerical weather prediction support of +/// nowcasting applications. +/// Quart. J. Royal Meterol. Soc., Vol. 125, pp. 2359-2378 (1999). +/// https://doi.org/10.1002/qj.49712555902 + +class CloudCostFunction : public ObsFunctionBase { + public: + explicit CloudCostFunction(const eckit::LocalConfiguration & + = eckit::LocalConfiguration()); + void compute(const ObsFilterData &, + ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + + private: + ufo::Variables invars_; + std::vector channels_; + std::vector fields_; + CloudCostFunctionParameters options_; +}; + + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_CLOUDCOSTFUNCTION_H_ diff --git a/src/ufo/filters/obsfunctions/CloudDetectMinResidualAVHRR.cc b/src/ufo/filters/obsfunctions/CloudDetectMinResidualAVHRR.cc new file mode 100755 index 000000000..f0a4b9674 --- /dev/null +++ b/src/ufo/filters/obsfunctions/CloudDetectMinResidualAVHRR.cc @@ -0,0 +1,348 @@ +/* + * (C) Copyright 2019 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/CloudDetectMinResidualAVHRR.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "oops/util/IntSetParser.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/Constants.h" + +namespace ufo { + +static ObsFunctionMaker + makerCloudDetectMinResidualAVHRR_("CloudDetectMinResidualAVHRR"); + +// ----------------------------------------------------------------------------- + +CloudDetectMinResidualAVHRR::CloudDetectMinResidualAVHRR(const eckit::LocalConfiguration & conf) + : invars_() { + // Check options + options_.deserialize(conf); + + // Get channels from options + std::set channelset = oops::parseIntSet(options_.channelList); + std::copy(channelset.begin(), channelset.end(), std::back_inserter(channels_)); + ASSERT(channels_.size() > 0); + + // Get test groups from options + const std::string &flaggrp = options_.testQCflag.value(); + const std::string &errgrp = options_.testObserr.value(); + const std::string &biasgrp = options_.testBias.value(); + const std::string &hofxgrp = options_.testHofX.value(); + + // Include required variables from ObsDiag + invars_ += Variable("brightness_temperature_jacobian_surface_temperature@ObsDiag", channels_); + invars_ += Variable("brightness_temperature_jacobian_air_temperature@ObsDiag", channels_); + invars_ += Variable("transmittances_of_atmosphere_layer@ObsDiag", channels_); + invars_ += Variable("pressure_level_at_peak_of_weightingfunction@ObsDiag", channels_); + + // Include list of required data from ObsSpace + invars_ += Variable("brightness_temperature@"+flaggrp, channels_); + invars_ += Variable("brightness_temperature@"+errgrp, channels_); + invars_ += Variable("brightness_temperature@"+biasgrp, channels_); + invars_ += Variable("brightness_temperature@"+hofxgrp, channels_); + invars_ += Variable("brightness_temperature@ObsValue", channels_); + invars_ += Variable("brightness_temperature@ObsError", channels_); + + // Include list of required data from GeoVaLs + invars_ += Variable("water_area_fraction@GeoVaLs"); + invars_ += Variable("land_area_fraction@GeoVaLs"); + invars_ += Variable("ice_area_fraction@GeoVaLs"); + invars_ += Variable("surface_snow_area_fraction@GeoVaLs"); + invars_ += Variable("average_surface_temperature_within_field_of_view@GeoVaLs"); + invars_ += Variable("air_pressure@GeoVaLs"); + invars_ += Variable("air_temperature@GeoVaLs"); + invars_ += Variable("tropopause_pressure@GeoVaLs"); +} + +// ----------------------------------------------------------------------------- + +CloudDetectMinResidualAVHRR::~CloudDetectMinResidualAVHRR() {} + +// ----------------------------------------------------------------------------- + +void CloudDetectMinResidualAVHRR::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + // Get channel use flags from options + std::vector use_flag = options_.useflagChannel.value(); + std::vector use_flag_clddet = options_.useflagCloudDetect.value(); + + // Get tuning parameters for surface sensitivity over sea/land/oce/snow/mixed from options + std::vector dtempf_in = options_.obserrScaleFactorTsfc.value(); + + // Get dimensions + size_t nlocs = in.nlocs(); + size_t nchans = channels_.size(); + size_t nlevs = in.nlevs(Variable("air_pressure@GeoVaLs")); + + // Get test groups from options + const std::string &flaggrp = options_.testQCflag.value(); + const std::string &errgrp = options_.testObserr.value(); + const std::string &biasgrp = options_.testBias.value(); + const std::string &hofxgrp = options_.testHofX.value(); + + // Get variables from ObsDiag + // Load surface temperature jacobian + std::vector> dbtdts(nchans, std::vector(nlocs)); + for (size_t ichan = 0; ichan < nchans; ++ichan) { + in.get(Variable("brightness_temperature_jacobian_surface_temperature@ObsDiag", + channels_)[ichan], dbtdts[ichan]); + } + + // Get temperature jacobian + std::vector>> + dbtdt(nchans, std::vector>(nlevs, std::vector(nlocs))); + for (size_t ichan = 0; ichan < nchans; ++ichan) { + for (size_t ilev = 0; ilev < nlevs; ++ilev) { + int level = nlevs - ilev; + in.get(Variable("brightness_temperature_jacobian_air_temperature@ObsDiag", + channels_)[ichan], level, dbtdt[ichan][ilev]); + } + } + + // Get layer-to-space transmittance + std::vector>> + tao(nchans, std::vector>(nlevs, std::vector(nlocs))); + for (size_t ichan = 0; ichan < nchans; ++ichan) { + for (size_t ilev = 0; ilev < nlevs; ++ilev) { + int level = nlevs - ilev; + in.get(Variable("transmittances_of_atmosphere_layer@ObsDiag", + channels_)[ichan], level, tao[ichan][ilev]); + } + } + + // Get pressure level at the peak of the weighting function + std::vector values(nlocs, 0.0); + std::vector> wfunc_pmaxlev(nchans, std::vector(nlocs)); + for (size_t ichan = 0; ichan < nchans; ++ichan) { + in.get(Variable("pressure_level_at_peak_of_weightingfunction@ObsDiag", + channels_)[ichan], values); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + wfunc_pmaxlev[ichan][iloc] = nlevs - values[iloc] + 1; + } + } + + // Get variables from ObsSpace + // Get effective observation error and convert it to inverse of the error variance + const float missing = util::missingValue(missing); + std::vector qcflag(nlocs, 0); + std::vector> varinv_use(nchans, std::vector(nlocs, 0.0)); + for (size_t ichan = 0; ichan < nchans; ++ichan) { + in.get(Variable("brightness_temperature@"+errgrp, channels_)[ichan], values); + in.get(Variable("brightness_temperature@"+flaggrp, channels_)[ichan], qcflag); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + if (flaggrp == "PreQC") values[iloc] == missing ? qcflag[iloc] = 100 : qcflag[iloc] = 0; + (qcflag[iloc] == 0) ? (values[iloc] = 1.0 / pow(values[iloc], 2)) : (values[iloc] = 0.0); + if (use_flag_clddet[ichan] > 0) varinv_use[ichan][iloc] = values[iloc]; + } + } + + // Get bias corrected innovation (tbobs - hofx - bias) + std::vector> innovation(nchans, std::vector(nlocs)); + for (size_t ichan = 0; ichan < nchans; ++ichan) { + in.get(Variable("brightness_temperature@ObsValue", channels_)[ichan], innovation[ichan]); + in.get(Variable("brightness_temperature@"+hofxgrp, channels_)[ichan], values); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + innovation[ichan][iloc] = innovation[ichan][iloc] - values[iloc]; + } + in.get(Variable("brightness_temperature@"+biasgrp, channels_)[ichan], values); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + innovation[ichan][iloc] = innovation[ichan][iloc] - values[iloc]; + } + } + + // Get original observation error (uninflated) from ObsSpace + std::vector> obserr(nchans, std::vector(nlocs)); + for (size_t ichan = 0; ichan < nchans; ++ichan) { + in.get(Variable("brightness_temperature@ObsError", channels_)[ichan], obserr[ichan]); + } + + // Get tropopause pressure [Pa] + std::vector tropprs(nlocs); + in.get(Variable("tropopause_pressure@GeoVaLs"), tropprs); + + // Get average surface temperature within FOV + std::vector tsavg(nlocs); + in.get(Variable("average_surface_temperature_within_field_of_view@GeoVaLs"), tsavg); + + // Get area fraction of each surface type + std::vector water_frac(nlocs); + std::vector land_frac(nlocs); + std::vector ice_frac(nlocs); + std::vector snow_frac(nlocs); + in.get(Variable("water_area_fraction@GeoVaLs"), water_frac); + in.get(Variable("land_area_fraction@GeoVaLs"), land_frac); + in.get(Variable("ice_area_fraction@GeoVaLs"), ice_frac); + in.get(Variable("surface_snow_area_fraction@GeoVaLs"), snow_frac); + + // Determine dominant surface type in each FOV + std::vector land(nlocs, false); + std::vector sea(nlocs, false); + std::vector ice(nlocs, false); + std::vector snow(nlocs, false); + std::vector mixed(nlocs, false); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + sea[iloc] = water_frac[iloc] >= 0.99; + land[iloc] = land_frac[iloc] >= 0.99; + ice[iloc] = ice_frac[iloc] >= 0.99; + snow[iloc] = snow_frac[iloc] >= 0.99; + mixed[iloc] = (!sea[iloc] && !land[iloc] && !ice[iloc] && !snow[iloc]); + } + + // Setup weight given to each surface type + std::vector dtempf(nlocs); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + if (sea[iloc]) { + dtempf[iloc] = dtempf_in[0]; + } else if (land[iloc]) { + dtempf[iloc] = dtempf_in[1]; + } else if (ice[iloc]) { + dtempf[iloc] = dtempf_in[2]; + } else if (snow[iloc]) { + dtempf[iloc] = dtempf_in[3]; + } else { + dtempf[iloc] = dtempf_in[4]; + } + } + + // Get air pressure [Pa] + std::vector> prsl(nlevs, std::vector(nlocs)); + for (size_t ilev = 0; ilev < nlevs; ++ilev) { + size_t level = nlevs - ilev; + in.get(Variable("air_pressure@GeoVaLs"), level, prsl[ilev]); + } + + // Get air temperature + std::vector> tair(nlevs, std::vector(nlocs)); + for (size_t ilev = 0; ilev < nlevs; ++ilev) { + size_t level = nlevs - ilev; + in.get(Variable("air_temperature@GeoVaLs"), level, tair[ilev]); + } + + // Minimum Residual Method (MRM) for Cloud Detection: + // Determine model level index of the cloud top (lcloud) + // Find pressure of the cloud top (cldprs) + // Estimate cloud fraction (cldfrac) + // output: out = 0 clear channel + // out = 1 cloudy channel + // out = 2 clear channel but too sensitive to surface condition + + // Loop through locations + const float btmax = 1000.f, btmin = 0.f; + for (size_t iloc=0; iloc < nlocs; ++iloc) { + float sum3 = 0.0; + float tmp = 0.0; + float cloudp = 0.0; + std::vector> dbt(nchans, std::vector(nlevs)); + for (size_t ichan=0; ichan < nchans; ++ichan) { + if (varinv_use[ichan][iloc] > 0) { + sum3 = sum3 + innovation[ichan][iloc] * innovation[ichan][iloc] * varinv_use[ichan][iloc]; + } + } + sum3 = 0.75 * sum3; + + // Set initial cloud condition + int lcloud = 0; + float cldfrac = 0.0; + float cldprs = prsl[0][iloc] * 0.01; // convert from [Pa] to [hPa] + + // Loop through vertical layer from surface to model top + for (size_t k = 0 ; k < nlevs ; ++k) { + for (size_t ichan = 0; ichan < nchans; ++ichan) dbt[ichan][k] = 0.0; + // Perform cloud detection within troposphere + if (prsl[k][iloc] * 0.01 > tropprs[iloc] * 0.01) { + float sum = 0.0, sum2 = 0.0; + for (size_t ichan = 0; ichan < nchans; ++ichan) { + if (varinv_use[ichan][iloc] > 0.0) { + dbt[ichan][k] = (tair[k][iloc] - tsavg[iloc]) * dbtdts[ichan][iloc]; + for (size_t kk = 0; kk < k; ++kk) { + dbt[ichan][k] = dbt[ichan][k] + (tair[k][iloc] - tair[kk][iloc]) * + dbtdt[ichan][kk][iloc]; + } + sum = sum + innovation[ichan][iloc] * dbt[ichan][k] * varinv_use[ichan][iloc]; + sum2 = sum2 + dbt[ichan][k] * dbt[ichan][k] * varinv_use[ichan][iloc]; + } + } + if (fabs(sum2) < FLT_MIN) sum2 = copysign(1.0e-12, sum2); + cloudp = std::min(std::max((sum/sum2), 0.f), 1.f); + sum = 0.0; + for (size_t ichan = 0; ichan < nchans; ++ichan) { + if (varinv_use[ichan][iloc] > 0.0) { + tmp = innovation[ichan][iloc] - cloudp * dbt[ichan][k]; + sum = sum + tmp * tmp * varinv_use[ichan][iloc]; + } + } + if (sum < sum3) { + sum3 = sum; + lcloud = k + 1; // array index + 1 -> model coordinate index + cldfrac = cloudp; + cldprs = prsl[k][iloc] * 0.01; + } + } + // end of vertical loop + } + // Cloud check + for (size_t ichan = 0; ichan < nchans; ++ichan) { + size_t ilev; + out[ichan][iloc] = 0; + for (ilev = 0; ilev < lcloud; ++ilev) { + if (fabs(cldfrac * dbt[ichan][ilev]) > obserr[ichan][iloc]) { + out[ichan][iloc]= 1; + varinv_use[ichan][iloc]= 0.0; + break; + } + } + } + // If no clouds is detected, do sensivity to surface temperature check + // Initialize at each location + float sumx = 0.0, sumx2 = 0.0; + float dts = 0.0; + float delta = 0.0; + const float dts_threshold = 3.0; + for (size_t ichan=0; ichan < nchans; ++ichan) { + sumx = sumx + innovation[ichan][iloc] * dbtdts[ichan][iloc] * varinv_use[ichan][iloc]; + sumx2 = sumx2 + dbtdts[ichan][iloc] * dbtdts[ichan][iloc] * varinv_use[ichan][iloc]; + } + if (fabs(sumx2) < FLT_MIN) sumx2 = copysign(1.0e-12, sumx2); + dts = std::fabs(sumx / sumx2); + float dts_save = dts; + if (std::abs(dts) > 1.0) { + if (sea[iloc] == false) { + dts = std::min(dtempf[iloc], dts); + } else { + dts = std::min(dts_threshold, dts); + } + for (size_t ichan=0; ichan < nchans; ++ichan) { + delta = obserr[ichan][iloc]; + if (std::abs(dts * dbtdts[ichan][iloc]) > delta) out[ichan][iloc] = 2; + } + } + // end of location loop + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & CloudDetectMinResidualAVHRR::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/CloudDetectMinResidualAVHRR.h b/src/ufo/filters/obsfunctions/CloudDetectMinResidualAVHRR.h new file mode 100755 index 000000000..42ae13471 --- /dev/null +++ b/src/ufo/filters/obsfunctions/CloudDetectMinResidualAVHRR.h @@ -0,0 +1,84 @@ +/* + * (C) Copyright 2019 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_CLOUDDETECTMINRESIDUALAVHRR_H_ +#define UFO_FILTERS_OBSFUNCTIONS_CLOUDDETECTMINRESIDUALAVHRR_H_ + +#include +#include + +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" + +namespace ufo { + +/// +/// \brief Options applying to Cloud Detection Algorithm (Minimum Residual Method) +/// for Infrared sensors +/// +class CloudDetectMinResidualAVHRRParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(CloudDetectMinResidualAVHRRParameters, Parameters) + + public: + /// List of channels available for assimilation + oops::RequiredParameter channelList{"channels", this}; + + /// Useflag (-1: not used; 0: monitoring; 1: used) for each channel in channelList + oops::RequiredParameter> useflagChannel{"use_flag", this}; + + /// Useflag (-1: not used; 1: used) indicating if the channel is used for cloud detection + oops::RequiredParameter> useflagCloudDetect{"use_flag_clddet", this}; + + /// Observation error scale factors applied to surface temperature jacobians + /// over 5 surface types: [sea, land, ice, snow and mixed] + oops::RequiredParameter> obserrScaleFactorTsfc{"obserr_dtempf", this}; + + /// Name of the data group to which the observation error is applied (default: ObsErrorData) + oops::Parameter testObserr{"test_obserr", "ObsErrorData", this}; + + /// Name of the HofX group used to replace the default group (default is HofX) + oops::Parameter testHofX{"test_hofx", "HofX", this}; + + /// Name of the bias correction group used to replace the default group (default is ObsBias) + oops::Parameter testBias{"test_bias", "ObsBias", this}; + + /// Name of the data group to which the QC flag is applied (default is QCflagsData) + oops::Parameter testQCflag{"test_qcflag", "QCflagsData", this}; +}; + +/// +/// \brief Cloud Detection Algorithm (Minimum Residual Method) for Infrared sensors +/// using selected channels from 15 microns CO2 absorption band +/// Output of this function: +/// 0 = channel is not affected by clouds (clear channel) +/// 1 = channel is affected by clouds (cloudy channel) +/// 2 = channel is not affected by clouds but too sensitive to surface condition +/// +class CloudDetectMinResidualAVHRR : public ObsFunctionBase { + public: + explicit CloudDetectMinResidualAVHRR(const eckit::LocalConfiguration &); + ~CloudDetectMinResidualAVHRR(); + + void compute(const ObsFilterData &, + ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + CloudDetectMinResidualAVHRRParameters options_; + ufo::Variables invars_; + std::vector channels_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_CLOUDDETECTMINRESIDUALAVHRR_H_ diff --git a/src/ufo/filters/obsfunctions/CloudDetectMinResidualIR.cc b/src/ufo/filters/obsfunctions/CloudDetectMinResidualIR.cc index f0757ed91..9fb13974a 100755 --- a/src/ufo/filters/obsfunctions/CloudDetectMinResidualIR.cc +++ b/src/ufo/filters/obsfunctions/CloudDetectMinResidualIR.cc @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -42,7 +43,6 @@ CloudDetectMinResidualIR::CloudDetectMinResidualIR(const eckit::LocalConfigurati // Get test groups from options const std::string &flaggrp = options_.testQCflag.value(); const std::string &errgrp = options_.testObserr.value(); - const std::string &biasgrp = options_.testBias.value(); const std::string &hofxgrp = options_.testHofX.value(); // Include required variables from ObsDiag @@ -54,7 +54,6 @@ CloudDetectMinResidualIR::CloudDetectMinResidualIR(const eckit::LocalConfigurati // Include list of required data from ObsSpace invars_ += Variable("brightness_temperature@"+flaggrp, channels_); invars_ += Variable("brightness_temperature@"+errgrp, channels_); - invars_ += Variable("brightness_temperature@"+biasgrp, channels_); invars_ += Variable("brightness_temperature@"+hofxgrp, channels_); invars_ += Variable("brightness_temperature@ObsValue", channels_); invars_ += Variable("brightness_temperature@ObsError", channels_); @@ -93,7 +92,6 @@ void CloudDetectMinResidualIR::compute(const ObsFilterData & in, // Get test groups from options const std::string &flaggrp = options_.testQCflag.value(); const std::string &errgrp = options_.testObserr.value(); - const std::string &biasgrp = options_.testBias.value(); const std::string &hofxgrp = options_.testHofX.value(); // Get variables from ObsDiag @@ -152,7 +150,7 @@ void CloudDetectMinResidualIR::compute(const ObsFilterData & in, } } - // Get bias corrected innovation (tbobs - hofx - bias) + // Get bias corrected innovation (tbobs - hofx) (hofx includes bias correction) std::vector> innovation(nchans, std::vector(nlocs)); for (size_t ichan = 0; ichan < nchans; ++ichan) { in.get(Variable("brightness_temperature@ObsValue", channels_)[ichan], innovation[ichan]); @@ -160,10 +158,6 @@ void CloudDetectMinResidualIR::compute(const ObsFilterData & in, for (size_t iloc = 0; iloc < nlocs; ++iloc) { innovation[ichan][iloc] = innovation[ichan][iloc] - values[iloc]; } - in.get(Variable("brightness_temperature@"+biasgrp, channels_)[ichan], values); - for (size_t iloc = 0; iloc < nlocs; ++iloc) { - innovation[ichan][iloc] = innovation[ichan][iloc] - values[iloc]; - } } // Get original observation error (uninflated) from ObsSpaec @@ -291,6 +285,7 @@ void CloudDetectMinResidualIR::compute(const ObsFilterData & in, sum2 = sum2 + dbt[ichan] * dbt[ichan] * varinv_use[ichan][iloc]; } } + if (fabs(sum2) < FLT_MIN) sum2 = copysign(1.0e-12, sum2); cloudp = std::min(std::max((sum/sum2), 0.f), 1.f); sum = 0.0; for (size_t ichan = 0; ichan < nchans; ++ichan) { @@ -337,6 +332,7 @@ void CloudDetectMinResidualIR::compute(const ObsFilterData & in, sum = sum + innovation[ichan][iloc] * dbtdts[ichan][iloc] * varinv_use[ichan][iloc]; sum2 = sum2 + dbtdts[ichan][iloc] * dbtdts[ichan][iloc] * varinv_use[ichan][iloc]; } + if (fabs(sum2) < FLT_MIN) sum2 = copysign(1.0e-12, sum2); dts = std::fabs(sum / sum2); if (std::abs(dts) > 1.0) { if (sea[iloc] == false) { diff --git a/src/ufo/filters/obsfunctions/CloudDetectMinResidualIR.h b/src/ufo/filters/obsfunctions/CloudDetectMinResidualIR.h index 5261fbffd..4b85b4270 100755 --- a/src/ufo/filters/obsfunctions/CloudDetectMinResidualIR.h +++ b/src/ufo/filters/obsfunctions/CloudDetectMinResidualIR.h @@ -48,9 +48,6 @@ class CloudDetectMinResidualIRParameters : public oops::Parameters { /// Name of the HofX group used to replace the default group (default is HofX) oops::Parameter testHofX{"test_hofx", "HofX", this}; - /// Name of the bias correction group used to replace the default group (default is ObsBias) - oops::Parameter testBias{"test_bias", "ObsBias", this}; - /// Name of the data group to which the QC flag is applied (default is QCflagsData) oops::Parameter testQCflag{"test_qcflag", "QCflagsData", this}; }; diff --git a/src/ufo/filters/obsfunctions/Conditional.cc b/src/ufo/filters/obsfunctions/Conditional.cc new file mode 100755 index 000000000..2b0fb519e --- /dev/null +++ b/src/ufo/filters/obsfunctions/Conditional.cc @@ -0,0 +1,60 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ioda/ObsDataVector.h" +#include "oops/util/missingValues.h" +#include "oops/util/parameters/Parameter.h" +#include "ufo/filters/obsfunctions/Conditional.h" +#include "ufo/filters/Variable.h" + +namespace ufo { + +static ObsFunctionMaker makerConditional_("Conditional"); + +Conditional::Conditional(const eckit::LocalConfiguration & conf) + : invars_() { + // Initialize options + options_.validateAndDeserialize(conf); + + // Populate invars_ + for (const LocalConditionalParameters &lcp : options_.cases.value()) + invars_ += getAllWhereVariables(lcp.where); +} + +// ----------------------------------------------------------------------------- +void Conditional::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + // Assign default value to array + const float missing = util::missingValue(float()); + for (size_t ivar = 0; ivar < out.nvars(); ++ivar) { + std::fill(out[ivar].begin(), out[ivar].end(), options_.defaultvalue.value().value_or(missing)); + } // ivar + + // Assign values based on the where clauses from the configuration. + // if firstmatchingcase is true, the first case that is true assigns the value. + // if firstmatchingcase is false, the last matching case will assign the value. + std::vector applied(out.nlocs(), false); + for (const LocalConditionalParameters &lcp : options_.cases.value()) { + std::vector apply = processWhere(lcp.where, in); + for (size_t iloc = 0; iloc < out.nlocs(); ++iloc) { + if (apply[iloc] && applied[iloc] == false) { + for (size_t ivar = 0; ivar < out.nvars(); ++ivar) + out[ivar][iloc] = lcp.value.value(); + if (options_.firstmatchingcase.value()) applied[iloc] = true; + } // if apply + } // iloc + } // lcp +} // compute + +// ----------------------------------------------------------------------------- +const ufo::Variables & Conditional::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/Conditional.h b/src/ufo/filters/obsfunctions/Conditional.h new file mode 100755 index 000000000..0d89e3a33 --- /dev/null +++ b/src/ufo/filters/obsfunctions/Conditional.h @@ -0,0 +1,117 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_CONDITIONAL_H_ +#define UFO_FILTERS_OBSFUNCTIONS_CONDITIONAL_H_ + +#include +#include + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/processWhere.h" +#include "ufo/filters/Variables.h" + +namespace ufo { + +/// Parameters for one where configuration from the cases section +/// of the yaml file. +/// +/// Example: +/// +/// - where: +/// - variable: +/// name: float_variable_2@MetaData +/// minvalue: 0 +/// value: 0.5 +/// +class LocalConditionalParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(LocalConditionalParameters, Parameters) + + public: + /// Where clause needed for assignment. This is passed to create a ProcessWhere + /// object and requires the expected parameters for ufo::WhereParameters + oops::RequiredParameter> where{"where", this}; + + /// \brief Value to be assigned when this particular where clause is true. + oops::RequiredParameter value{"value", this}; +}; + +/// Parameters controlling the Conditional obs function. +class ConditionalParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ConditionalParameters, Parameters) + + public: + /// List of cases for assignment. See LocalConditionalParameters + /// for what each where clause requires. + oops::RequiredParameter> cases{"cases", this}; + + /// Default value for the array to be assigned. Missing value is used + /// if this is not present in the yaml. + oops::OptionalParameter defaultvalue{"defaultvalue", this}; + + /// When this flag is true a value is assigned for the first matching where case + /// for a location. This matches with the python case logic. + /// When this is false the last matching case will take precedence which + /// replicates a series of separate variable assignment filter calls. + oops::Parameter firstmatchingcase{"firstmatchingcase", true, this}; +}; + +/// \brief Creates an array with values for specified variables selected by a series of where +/// statements. +/// +/// The obs function has been designed primarily to work with the Variable assignment filter +/// to simplify the assignment of more complicated variables. Any functionality in the +/// processWhere class can be used with this obs function. This can only be used to assign +/// float and int variables because the ObsFunction only computes floating point arrays. +/// +/// Example : Create a new integer variable `emissivity@ObsDerived` and assign values based +/// on the surface type. +/// +/// - filter: Variable Assignment +/// assignments: +/// - name: emissivity@ObsDerived +/// type: int +/// function: +/// name: Conditional@ObsFunction +/// options: +/// defaultvalue: 0.0 # default value - rttov to calculate. +/// cases: +/// - where: +/// - variable: +/// name: surface_type@MetaData +/// is_in: 1 +/// # if necessary, further conditions could be specified in extra items +/// # in the 'where' list +/// value: 0.3 +/// - where: +/// - variable: +/// name: surface_type@MetaData +/// is_in: 2 +/// value: 0.5 +/// +class Conditional : public ObsFunctionBase { + public: + explicit Conditional(const eckit::LocalConfiguration & = eckit::LocalConfiguration()); + void compute(const ObsFilterData &, + ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + ConditionalParameters options_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_CONDITIONAL_H_ diff --git a/src/ufo/filters/obsfunctions/DrawObsErrorFromFile.cc b/src/ufo/filters/obsfunctions/DrawObsErrorFromFile.cc new file mode 100644 index 000000000..8d1361bbc --- /dev/null +++ b/src/ufo/filters/obsfunctions/DrawObsErrorFromFile.cc @@ -0,0 +1,47 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/DrawObsErrorFromFile.h" + +namespace ufo { + +namespace { + +eckit::LocalConfiguration makeConfigForDrawValueFromFile(const eckit::LocalConfiguration &config) { + eckit::LocalConfiguration newConfig(config); + newConfig.set("group", "ErrorVariance"); + return newConfig; +} + +} // namespace + +static ObsFunctionMaker maker("DrawObsErrorFromFile"); + + +DrawObsErrorFromFile::DrawObsErrorFromFile(const eckit::LocalConfiguration &config) + : drawValueFromFile_(makeConfigForDrawValueFromFile(config)) { +} + +void DrawObsErrorFromFile::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + const float missing = util::missingValue(missing); + + // Interpolate variances + drawValueFromFile_.compute(in, out); + + // Transform them into standard deviations + for (size_t ivar = 0; ivar < out.nvars(); ++ivar) + for (float &value : out[ivar]) + if (value != missing) + value = std::sqrt(value); +} + +const ufo::Variables & DrawObsErrorFromFile::requiredVariables() const { + return drawValueFromFile_.requiredVariables(); +} + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/DrawObsErrorFromFile.h b/src/ufo/filters/obsfunctions/DrawObsErrorFromFile.h new file mode 100644 index 000000000..cd5b29d18 --- /dev/null +++ b/src/ufo/filters/obsfunctions/DrawObsErrorFromFile.h @@ -0,0 +1,63 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_DRAWOBSERRORFROMFILE_H_ +#define UFO_FILTERS_OBSFUNCTIONS_DRAWOBSERRORFROMFILE_H_ + +#include + +#include "ufo/filters/obsfunctions/DrawValueFromFile.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" + +namespace ufo { + +/// \brief Derive observation error from a file representing the variance or the covariance +/// matrix. +/// \details See DataExtractor for details on the format of this file. +/// +/// ### example configurations: ### +/// +/// \code{.yaml} +/// - Filter: Perform Action +/// filter variables: +/// - name: air_temperature +/// channels: &all_channels 1-3 +/// action: +/// name: assign error +/// error function: +/// name: DrawObsErrorFromFile@ObsFunction +/// channels: *all_channels +/// options: +/// file: +/// channels: *all_channels +/// interpolation: +/// - name: satellite_id@MetaData +/// method: exact +/// - name: processing_center@MetaData +/// method: exact +/// - name: air_pressure@MetaData +/// method: linear +/// \endcode +/// +/// Note that channel number extraction is implicit, using the channels selected and performed as +/// an exact match before any user defined interpolation takes place. +class DrawObsErrorFromFile : public ObsFunctionBase { + public: + static const std::string classname() {return "DrawObsErrorFromFile";} + + explicit DrawObsErrorFromFile(const eckit::LocalConfiguration &); + + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + + private: + DrawValueFromFile drawValueFromFile_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_DRAWOBSERRORFROMFILE_H_ diff --git a/src/ufo/filters/obsfunctions/DrawValueFromFile.cc b/src/ufo/filters/obsfunctions/DrawValueFromFile.cc new file mode 100644 index 000000000..c93e33928 --- /dev/null +++ b/src/ufo/filters/obsfunctions/DrawValueFromFile.cc @@ -0,0 +1,135 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ +#include "eckit/exception/Exceptions.h" +#include "oops/util/IntSetParser.h" +#include "ufo/filters/obsfunctions/DrawValueFromFile.h" + + +namespace ufo { + +constexpr char InterpMethodParameterTraitsHelper::enumTypeName[]; +constexpr util::NamedEnumerator + InterpMethodParameterTraitsHelper::namedValues[]; + +static ObsFunctionMaker + makerNetCDF_("DrawValueFromFile"); + + +// ----------------------------------------------------------------------------- +DrawValueFromFile::DrawValueFromFile(const eckit::LocalConfiguration &config) + : allvars_() { + // Initialize options + options_.deserialize(config); + + std::vector interpSubConfs; + const std::vector &interpolationParameters = + options_.interpolation.value(); + for (auto intParam = interpolationParameters.begin(); + intParam != interpolationParameters.end(); ++intParam) { + const ufo::InterpMethod & method = intParam->method.value(); + if ((method == InterpMethod::LINEAR) && (intParam + 1 != interpolationParameters.end())) { + throw eckit::UserError("Linear interpolation can only be supplied as the very last " + "argument.", Here()); + } + interpSubConfs.push_back(intParam->toConfiguration()); + interpMethod_[intParam->name.value()] = method; + } + // Get channels from options + if (options_.chlist.value() != boost::none) { + std::set channels = options_.chlist.value().get(); + channels_ = {std::make_move_iterator(std::begin(channels)), + std::make_move_iterator(std::end(channels))}; + } + fpath_ = options_.fpath.value(); + allvars_ = Variables(interpSubConfs); +} + + +// ----------------------------------------------------------------------------- +DrawValueFromFile::~DrawValueFromFile() {} + + +class ExtractVisitor : public boost::static_visitor { + public: + ExtractVisitor(DataExtractor &interpolator, + const size_t &iloc) : + interpolator(interpolator), iloc(iloc) {} + + template + void operator()(const std::vector &obDat) { + auto obVal = obDat[iloc]; + interpolator.extract(obVal); + } + + DataExtractor &interpolator; + const size_t &iloc; +}; + + +// ----------------------------------------------------------------------------- +void DrawValueFromFile::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + const float missing = util::missingValue(missing); + + DataExtractor interpolator{fpath_, options_.group}; + + // Channel number handling + if (options_.chlist.value() != boost::none) + interpolator.scheduleSort("channel_number@MetaData", InterpMethod::EXACT); + + ObData obData; + for (size_t ind=0; ind < allvars_.size(); ind++) { + oops::Log::debug() << "Extracting " << allvars_[ind].variable() << + " from the obsSpace" << std::endl; + + const std::string &varName = get_full_name(allvars_[ind]); + const InterpMethod &interpolationMethod = interpMethod_.at(varName); + interpolator.scheduleSort(varName, interpolationMethod); + switch (in.dtype(allvars_[ind])) { + case ioda::ObsDtype::Integer: + updateObData(in, allvars_[ind], obData); + break; + case ioda::ObsDtype::String: + updateObData(in, allvars_[ind], obData); + break; + case ioda::ObsDtype::Float: + updateObData(in, allvars_[ind], obData); + break; + case ioda::ObsDtype::DateTime: + updateObDataDateTime(in, allvars_[ind], obData); + break; + default: + throw eckit::UserError("Data type not yet handled.", Here()); + } + } + // Finalise (apply) sort by calling with no arguments. + interpolator.sort(); + + for (size_t jvar = 0; jvar < out.nvars(); ++jvar) { + for (size_t iloc = 0; iloc < in.nlocs(); ++iloc) { + if (options_.chlist.value() != boost::none) + interpolator.extract(channels_[jvar]); + + // Perform any extraction methods (exact, nearest and linear interp.) + for (auto &od : obData) { + ExtractVisitor visitor(interpolator, iloc); + boost::apply_visitor(visitor, od.second); + } + out[jvar][iloc] = interpolator.getResult(); + } + } +} + +// ----------------------------------------------------------------------------- +const ufo::Variables & DrawValueFromFile::requiredVariables() const { + return allvars_; +} + + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/DrawValueFromFile.h b/src/ufo/filters/obsfunctions/DrawValueFromFile.h new file mode 100644 index 000000000..f682546c1 --- /dev/null +++ b/src/ufo/filters/obsfunctions/DrawValueFromFile.h @@ -0,0 +1,198 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_DRAWVALUEFROMFILE_H_ +#define UFO_FILTERS_OBSFUNCTIONS_DRAWVALUEFROMFILE_H_ + +#include // transform +#include +#include +#include +#include +#include // pair +#include + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variable.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/dataextractor/DataExtractor.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace eckit { + class Configuration; +} + +namespace ufo { + + +struct InterpMethodParameterTraitsHelper { + typedef InterpMethod EnumType; + static constexpr char enumTypeName[] = "InterpMethod"; + static constexpr util::NamedEnumerator namedValues[] = { + { InterpMethod::EXACT, "exact" }, + { InterpMethod::NEAREST, "nearest" }, + { InterpMethod::LEAST_UPPER_BOUND, "least upper bound" }, + { InterpMethod::GREATEST_LOWER_BOUND, "greatest lower bound" }, + { InterpMethod::LINEAR, "linear" } + }; +}; + +} // namespace ufo + + +namespace oops { + +template <> +struct ParameterTraits : + public EnumParameterTraits +{}; + +} // namespace oops + + +namespace ufo { + +/// \brief How to identify the relevant elements of the interpolated array along a dimension +/// indexed by a particular variable. +class InterpolationParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(InterpolationParameters, Parameters) + + public: + /// Name of the indexing variable (e.g. `latitude@MetaData`). + oops::RequiredParameter name{"name", this}; + + /// Method used to map the value of a variable to a range of slices of the interpolated array + /// along the dimension indexed by that variable. + /// + /// \see InterpMethod for the list of supported methods. + oops::RequiredParameter method{"method", this}; +}; + + +/// \brief Options controlling the DrawValueFromFile ObsFunction (excluding the `group` option). +class DrawValueFromFileParametersWithoutGroup : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(DrawValueFromFileParametersWithoutGroup, Parameters) + + public: + /// Path to the file containing the data to interpolate. + oops::RequiredParameter fpath{"file", this}; + + /// List of interpolation variables and associated methods. + /// Note that channel numbers is handled implicitly by the "channels" (see below). + oops::RequiredParameter> interpolation{"interpolation", + this}; + /// List of channel numbers (then deriving an observation error per channel) + /// If this option is provided, the channel number is implicitly prepended to the list of + /// interpolation variables and matched exactly. + oops::OptionalParameter> chlist{"channels", this}; +}; + + +/// \brief Options controlling the DrawValueFromFile ObsFunction +class DrawValueFromFileParameters : public DrawValueFromFileParametersWithoutGroup { + OOPS_CONCRETE_PARAMETERS(DrawValueFromFileParameters, + DrawValueFromFileParametersWithoutGroup) + + public: + /// The file should contain exactly one variable from this group. This is the variable that + /// will be interpolated. + oops::RequiredParameter group{"group", this}; +}; + + +/// \brief Produce values by interpolating an array loaded from a file, indexed by +/// coordinates whose names correspond to ObsSpace variables. +/// +/// \details See DataExtractor for details on the format of this file. +/// +/// ### example configurations: ### +/// +/// \code{.yaml} +/// - filter: Variable Assignment +/// assignments: +/// - name: interpolated_value@DerivedValue +/// function: +/// name: DrawValueFromFile@ObsFunction +/// channels: 1-3 +/// options: +/// file: +/// channels: 1-3 +/// group: DerivedValue +/// interpolation: +/// - name: satellite_id@MetaData +/// method: exact +/// - name: processing_center@MetaData +/// method: exact +/// - name: air_pressure@MetaData +/// method: linear +/// \endcode +/// +/// Note that channel number extraction is implicit, using the channels selected and performed as +/// an exact match before any user defined interpolation takes place. +class DrawValueFromFile : public ObsFunctionBase { + public: + static const std::string classname() {return "DrawValueFromFile";} + + explicit DrawValueFromFile(const eckit::LocalConfiguration &); + ~DrawValueFromFile(); + + void compute(const ObsFilterData &, + ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + + private: + typedef std::list, + std::vector, + std::vector> + >> ObData; + + Variables allvars_; + std::unordered_map interpMethod_; + std::string fpath_; + DrawValueFromFileParameters options_; + std::vector channels_; + + static std::string get_full_name(const ufo::Variable &variable) { + std::string name = variable.variable(); + if (variable.group().size() > 0) name += ("@"+variable.group()); + return name; + } + + /// \brief This is a convenience function for updating our container for useful observation data + template + static void updateObData(const ObsFilterData &in, const ufo::Variable &var, ObData &obData) { + std::vector dat; + in.get(var, dat); + std::string name = get_full_name(var); + obData.emplace_back(name, std::move(dat)); + } + + /// \brief Add datetime observation information data to our container. + /// \details We simply convert the datetimes to strings as our implementation is not discriminate + /// between the two types. + static void updateObDataDateTime(const ObsFilterData &in, const ufo::Variable &var, + ObData &obData) { + std::vector dat; + std::vector datConv; + in.get(var, dat); + datConv.resize(dat.size()); + + // Convert the vec. of datetime. to strings + std::transform(dat.begin(), dat.end(), datConv.begin(), + [](util::DateTime dt){return dt.toString();}); + std::string name = get_full_name(var); + obData.emplace_back(name, std::move(datConv)); + } +}; + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_DRAWVALUEFROMFILE_H_ diff --git a/src/ufo/filters/obsfunctions/HydrometeorCheckAMSUA.cc b/src/ufo/filters/obsfunctions/HydrometeorCheckAMSUA.cc index 2f2ee8f22..87e648ef7 100755 --- a/src/ufo/filters/obsfunctions/HydrometeorCheckAMSUA.cc +++ b/src/ufo/filters/obsfunctions/HydrometeorCheckAMSUA.cc @@ -130,8 +130,9 @@ void HydrometeorCheckAMSUA::compute(const ObsFilterData & in, } } - // Calculate bias-corrected innovation: Observation - HofX - bias + // Calculate bias-corrected innovation: Observation - HofX (HofX includes bias correction) std::vector> btobs(nchans, std::vector(nlocs)); + // Read bias since it's used for correcting clear-sky simulated values below std::vector> bias(nchans, std::vector(nlocs)); std::vector> innov(nchans, std::vector(nlocs)); std::vector hofx(nlocs); @@ -140,7 +141,7 @@ void HydrometeorCheckAMSUA::compute(const ObsFilterData & in, in.get(Variable("brightness_temperature@"+biasgrp, channels_)[ichan], bias[ichan]); in.get(Variable("brightness_temperature@"+hofxgrp, channels_)[ichan], hofx); for (size_t iloc = 0; iloc < nlocs; ++iloc) { - innov[ichan][iloc] = btobs[ichan][iloc] - hofx[iloc] - bias[ichan][iloc]; + innov[ichan][iloc] = btobs[ichan][iloc] - hofx[iloc]; } } diff --git a/src/ufo/filters/obsfunctions/HydrometeorCheckATMS.cc b/src/ufo/filters/obsfunctions/HydrometeorCheckATMS.cc index 36e9827af..f442021db 100755 --- a/src/ufo/filters/obsfunctions/HydrometeorCheckATMS.cc +++ b/src/ufo/filters/obsfunctions/HydrometeorCheckATMS.cc @@ -155,8 +155,9 @@ void HydrometeorCheckATMS::compute(const ObsFilterData & in, } } - // Calculate bias-corrected innovation: Observation - HofX - bias + // Calculate bias-corrected innovation: Observation - HofX (HofX includes bias correction) std::vector> btobs(nchans, std::vector(nlocs)); + // Still read bias: it's used for correcting clear-sky simulated radiances below std::vector> bias(nchans, std::vector(nlocs)); std::vector> innov(nchans, std::vector(nlocs)); std::vector hofx(nlocs); @@ -165,7 +166,7 @@ void HydrometeorCheckATMS::compute(const ObsFilterData & in, in.get(Variable("brightness_temperature@"+biasgrp, channels_)[ichan], bias[ichan]); in.get(Variable("brightness_temperature@"+hofxgrp, channels_)[ichan], hofx); for (size_t iloc = 0; iloc < nlocs; ++iloc) { - innov[ichan][iloc] = btobs[ichan][iloc] - hofx[iloc] - bias[ichan][iloc]; + innov[ichan][iloc] = btobs[ichan][iloc] - hofx[iloc]; } } diff --git a/src/ufo/filters/obsfunctions/ImpactHeight.cc b/src/ufo/filters/obsfunctions/ImpactHeight.cc new file mode 100644 index 000000000..2f23b29c9 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ImpactHeight.cc @@ -0,0 +1,72 @@ +/* ----------------------------------------------------------------------------- + * (C) British Crown Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * ----------------------------------------------------------------------------- + */ + +/* ----------------------------------------------------------------------------- + * Function to calculate the impact height for GNSS-RO. This is the difference + * between the impact parameter and the earth's radius of curvature. + * ----------------------------------------------------------------------------- + */ +#include "ufo/filters/obsfunctions/ImpactHeight.h" + +#include +#include + +#include "ioda/ObsDataVector.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/Variable.h" + +namespace ufo { + +static ObsFunctionMaker makerImpactHeight_("ImpactHeight"); + +/* ----------------------------------------------------------------------------- + * Specify that impact_parameter and earth_radius_of_curvature need to be + * provided to this function + * ----------------------------------------------------------------------------- + */ +ImpactHeight::ImpactHeight(const eckit::LocalConfiguration & conf) + : invars_() { + invars_ += Variable("impact_parameter@MetaData"); + invars_ += Variable("earth_radius_of_curvature@MetaData"); +} + +// ----------------------------------------------------------------------------- + +ImpactHeight::~ImpactHeight() {} + +/* ----------------------------------------------------------------------------- + * Perform the computation. Read in the required variables, and calculate + * their difference, storing the difference in the output vector. + * ----------------------------------------------------------------------------- + */ +void ImpactHeight::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + const size_t nlocs = in.nlocs(); + std::vector impact_parameter; + in.get(Variable("impact_parameter@MetaData"), impact_parameter); + std::vector radius_curvature; + in.get(Variable("earth_radius_of_curvature@MetaData"), radius_curvature); + for (size_t jj = 0; jj < nlocs; ++jj) { + if (impact_parameter[jj] == util::missingValue(impact_parameter[jj]) || + radius_curvature[jj] == util::missingValue(radius_curvature[jj])) { + out[0][jj] = util::missingValue(out[0][jj]); + } else { + out[0][jj] = impact_parameter[jj] - radius_curvature[jj]; + } + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & ImpactHeight::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/ImpactHeight.h b/src/ufo/filters/obsfunctions/ImpactHeight.h new file mode 100644 index 000000000..890916521 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ImpactHeight.h @@ -0,0 +1,38 @@ +/* ----------------------------------------------------------------------------- + * (C) British Crown Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * ----------------------------------------------------------------------------- + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_IMPACTHEIGHT_H_ +#define UFO_FILTERS_OBSFUNCTIONS_IMPACTHEIGHT_H_ + +#include + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" + +namespace ufo { +/// +/// \brief Function calculates the GNSS-RO impact height as the difference +/// between the impact parameter and earth's radius of curvature. +/// +class ImpactHeight : public ObsFunctionBase { + public: + explicit ImpactHeight(const eckit::LocalConfiguration &); + ~ImpactHeight(); + + void compute(const ObsFilterData &, + ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; +}; +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_IMPACTHEIGHT_H_ diff --git a/src/ufo/filters/obsfunctions/InterChannelConsistencyCheck.cc b/src/ufo/filters/obsfunctions/InterChannelConsistencyCheck.cc index 76ff0b31b..64a500e39 100755 --- a/src/ufo/filters/obsfunctions/InterChannelConsistencyCheck.cc +++ b/src/ufo/filters/obsfunctions/InterChannelConsistencyCheck.cc @@ -89,14 +89,18 @@ void InterChannelConsistencyCheck::compute(const ObsFilterData & in, } // Inter-channel consistency check + bool passive_bc = true; + bool channel_passive = false; size_t ncheck = 6; if (inst == "atms") ncheck = 7; for (size_t iloc = 0; iloc < nlocs; ++iloc) { for (int ichan = 0; ichan < nchans; ++ichan) out[ichan][iloc] = 0; int kval = 0; for (int ichan = 1; ichan < ncheck; ++ichan) { + channel_passive = use_flag[ichan] == -1 || use_flag[ichan] == 0; int channel = ichan + 1; - if (varinv[ichan][iloc] <= 0.0 && use_flag[ichan] >= 1) { + if (varinv[ichan][iloc] <= 0.0 && + (use_flag[ichan] >= 1 || (passive_bc && channel_passive))) { kval = std::max(channel-1, kval); if ((inst == "amsua" || inst == "atms") && channel <= 3) kval = 0; } diff --git a/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.cc b/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.cc new file mode 100644 index 000000000..a24b7bdd0 --- /dev/null +++ b/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.cc @@ -0,0 +1,138 @@ +/* + * (C) Copyright 2020 NOAA NWS NCEP EMC + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.h" + +#include "eckit/exception/Exceptions.h" + +#include "oops/util/missingValues.h" + +#include "ufo/filters/Variable.h" + +namespace ufo { + +static ObsFunctionMaker makerObsFuncLAMDomainCheck_("LAMDomainCheck"); + +// ----------------------------------------------------------------------------- + +LAMDomainCheck::LAMDomainCheck(const eckit::LocalConfiguration & conf) + : invars_() { + oops::Log::debug() << "LAMDomainCheck: config = " << conf << std::endl; + // Initialize options + options_.deserialize(conf); + + // We must know the latitude of each observation + invars_ += Variable("latitude@MetaData"); + // We must know the longitude of each observation + invars_ += Variable("longitude@MetaData"); +} + +// ----------------------------------------------------------------------------- + +LAMDomainCheck::~LAMDomainCheck() {} + +// ----------------------------------------------------------------------------- +/*! \brief LAMDomainCheck::compute +* +* \details The LAMDomainCheck is an obsfunction to compute if an observation is +* located inside a specified limited area model domain. +* The LAMDomainCheck obsfunction returns a value of 1 if the observation is determined +* to be located inside the specified domain and a value of 0 if it lies outside. +* +* In the UFO obs functions YAML, first one must define the map_projection. +* The following values of map_projection are currently supported: +* * "gnomonic_ed" - the ESG grid used by FV3-LAM +* +* The option 'save: true' will save the computed value to the output IODA file as +* 'LAMDomainCheck@DerivedValue' (default is false). +* +* The additional parameters to be defined in the options section +* of the obs function YAML will depend on the choice of map_projection used. +* For gnomonic_ed: +* * a - ESG alpha parameter (default 0) +* * k - ESG kappa parameter (default 0) +* * plat - ESG center point latitude (degrees; default 0) +* * plon - ESG center point longitude (degrees; default 0) +* * pazi - ESG azimuthal angle (radians; default 0) +* * dx - grid spacing in x (degrees; default 1) +* * dy - grid spacing in y (degrees, default 1) +* * npx - number of gridpoints in x (default 2) +* * npy - number of gridpoints in y (default 2) +* If using FV3-LAM, the above values are provided in the netCDF grid file attributes, +* but this option will work for any regional model that utilizes the +* Extended Schmidt Gnomonic grid developed by R. Jim Purser: +* https://dtcenter.org/sites/default/files/events/2020/2-purser-james.pdf +* "The Extended Schmidt Gnomonic grid for regional applications" +* by R. J. Purser, D. Jovic, G. Ketefian, T. Black, J. Beck, J. Dong, J. Carley. +* UFS Users' Workshop, July 27--29, 2020. +* +*/ + +void LAMDomainCheck::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + const size_t nlocs = in.nlocs(); + const float missing = util::missingValue(missing); + + // Ensure that only one output variable is expected. + ASSERT(out.nvars() == 1); + + std::vector iidx(nlocs); + + // Retrieve the latitude and longitude. + std::vector latitude; + std::vector longitude; + in.get(Variable("latitude@MetaData"), latitude); + in.get(Variable("longitude@MetaData"), longitude); + + // get options based off the name of the map projection + if (options_.mapproj.value() == "gnomonic_ed") { + // ESG used in FV3-LAM + const float a = options_.esg_a.value(); + const float k = options_.esg_k.value(); + const float plat = options_.esg_plat.value(); + const float plon = options_.esg_plon.value(); + const float pazi = options_.esg_pazi.value(); + const float dx = options_.esg_dx.value(); + const float dy = options_.esg_dy.value(); + const int npx = options_.esg_npx.value(); + const int npy = options_.esg_npy.value(); + for (size_t jj = 0; jj < nlocs; ++jj) { + lam_domaincheck_esg_f90(a, k, plat, plon, pazi, npx, npy, + dx, dy, latitude[jj], longitude[jj], iidx[jj]); + out[0][jj] = static_cast(iidx[jj]); + } + } else if (options_.mapproj.value() == "circle") { + const float cenlat = options_.cenlat.value(); + const float cenlon = options_.cenlon.value(); + const float radius = options_.radius.value(); + for (size_t jj = 0; jj < nlocs; ++jj) { + // calculate great-circle distance on sphere + lam_domaincheck_circle_f90(cenlat, cenlon, radius, + latitude[jj], longitude[jj], iidx[jj]); + out[0][jj] = static_cast(iidx[jj]); + } + } else { + // throw exception for unsupported projection + std::string errString = " is not a supported map projection. Fatal error!!!"; + oops::Log::error() << options_.mapproj.value() << errString; + throw eckit::BadValue(errString); + } + + if (options_.save) { + out.save("DerivedValue"); + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & LAMDomainCheck::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.h b/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.h new file mode 100644 index 000000000..46b87ced4 --- /dev/null +++ b/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.h @@ -0,0 +1,70 @@ +/* + * (C) Copyright 2020 NOAA NWS NCEP EMC + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_LAMDOMAINCHECK_LAMDOMAINCHECK_H_ +#define UFO_FILTERS_OBSFUNCTIONS_LAMDOMAINCHECK_LAMDOMAINCHECK_H_ + +#include +#include + +#include "ioda/ObsDataVector.h" + +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.interface.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +class LAMDomainCheckParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(LAMDomainCheckParameters, Parameters) + + public: + oops::RequiredParameter mapproj{"map_projection", this}; + oops::Parameter esg_a{"a", 0.0f, this}; + oops::Parameter esg_k{"k", 0.0f, this}; + oops::Parameter esg_plat{"plat", 0.0f, this}; + oops::Parameter esg_plon{"plon", 0.0f, this}; + oops::Parameter esg_pazi{"pazi", 0.0f, this}; + oops::Parameter esg_dx{"dx", 1.0f, this}; + oops::Parameter esg_dy{"dy", 1.0f, this}; + oops::Parameter esg_npx{"npx", 2, this}; + oops::Parameter esg_npy{"npy", 2, this}; + oops::Parameter save{"save", false, this}; + +// for a circle domain on sphere with central lat/lon in degree +// and radius in km + oops::Parameter cenlat{"cenlat", 0.0f, this}; + oops::Parameter cenlon{"cenlon", 0.0f, this}; + oops::Parameter radius{"radius", 100.0f, this}; +}; + +// ----------------------------------------------------------------------------- + +class LAMDomainCheck : public ObsFunctionBase { + public: + static const std::string classname() {return "LAMDomainCheck";} + + explicit LAMDomainCheck(const eckit::LocalConfiguration & + = eckit::LocalConfiguration()); + ~LAMDomainCheck(); + + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + LAMDomainCheckParameters options_; +}; + +// ----------------------------------------------------------------------------- +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_LAMDOMAINCHECK_LAMDOMAINCHECK_H_ diff --git a/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.interface.F90 b/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.interface.F90 new file mode 100644 index 000000000..0f4d6bed0 --- /dev/null +++ b/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.interface.F90 @@ -0,0 +1,135 @@ +! (C) Copyright 2020 NOAA NWS NCEP EMC +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to handle verifying that an observation is within a limited area model domain + +module ufo_lamdomaincheck_mod_c + + use iso_c_binding + use kinds + use ufo_constants_mod, only: pi, deg2rad, rad2deg, two, half, mean_earth_rad + + implicit none + + private + +contains + +! ----------------------------------------------------------------------------- +!> \brief subroutine lam_domaincheck_esg_c +!! +!! \details **lam_domaincheck_esg_c()** is a subroutine that for a given input defined ESG regional grid +!! and a given input lat/lon point, determines if the point is within or outside the regional domain +!! It takes the following arguments as input: +!! * float c_a - alpha parameter for ESG grid definition +!! * float c_k - kappa paremeter for ESG grid definition +!! * float c_plat - center point latitude of ESG grid (degrees) +!! * float c_plon - center point longitude of ESG grid (degrees) +!! * float c_pazi - azimuth angle for ESG grid definition (radians) +!! * int c_npx - number of grid points in x direction +!! * int c_npy - number of grid points in y direction +!! * float c_dx - grid spacing in degrees +!! * float c_dy - grid spacing in degrees +!! * float c_lat - input latitude (degrees) +!! * float c_lon - input longitude (degrees) +!! +!! and returns c_mask, an integer of 1 (inside the domain) or 0 (outside the domain) +!! The above input arguments, c_lat and c_lon are independent for each observation. +!! The other input arguments are available as global attributes in the FV3 regional grid netCDF file. +!! + +subroutine lam_domaincheck_esg_c(c_a, c_k, c_plat, c_plon, c_pazi, c_npx, c_npy,& + c_dx, c_dy, c_lat, c_lon, c_mask) & + bind(c, name='lam_domaincheck_esg_f90') + use esg_grid_mod, only: gtoxm_ak_rr + implicit none + real(c_float), intent(in ) :: c_a, c_k, c_plat, c_plon, c_pazi, c_dx, c_dy + real(c_float), intent(in ) :: c_lat, c_lon + integer(c_int), intent(in ) :: c_npx, c_npy + integer(c_int), intent(inout) :: c_mask + real(kind_real), dimension(2) :: xm + logical :: failure + + real(kind_real) :: a, k, plat, plon, pazi, dx, dy, lat, lon + integer :: npx, npy + + !! convert integers + npx = int(c_npx) + npy = int(c_npy) + !! convert from C to kind_real + a = real(c_a, kind_real) + k = real(c_k, kind_real) + ! some need converted from degrees to radians + plat = real(c_plat, kind_real)*deg2rad + plon = real(c_plon, kind_real)*deg2rad + pazi = real(c_pazi, kind_real) + ! dx and dy are on the supergrid, for actual grid resolution is half + dx = real(c_dx, kind_real)*deg2rad*two + dy = real(c_dy, kind_real)*deg2rad*two + lat = real(c_lat, kind_real)*deg2rad + lon = real(c_lon, kind_real)*deg2rad + + ! call highest level subroutine from Jim Purser's pesg.f90 code + call gtoxm_ak_rr(a, k, plat, plon, pazi,& + dx, dy, lat, lon, xm, failure) + + ! use xm to determine if mask is 1 (good) or 0 (bad) + c_mask = 0 + if ((abs(xm(1)) < npx/2) .and. (abs(xm(2)) < npy/2) .and. (.not. failure)) then + c_mask = 1 + end if + +end subroutine lam_domaincheck_esg_c + +! ----------------------------------------------------------------------------- +!> \brief subroutine lam_domaincheck_circle_c +!! +!! \details **lam_domaincheck_circle_c()** is a subroutine that for a given input defined circle regional grid +!! and a given input central lat/lon point and radius of circle, determines if the point is within or outside +!! the regional domain. Circle domain could be typical for regional MPAS applications. +!! It takes the following arguments as input: +!! * float c_cenlat - center point latitude of circle domain (degrees) +!! * float c_cenlon - center point longitude of circle domain (degrees) +!! * float c_radius - radius of circle domain (km) +!! * float c_lat - input latitude (degrees) +!! * float c_lon - input longitude (degrees) +!! +!! and returns c_mask, an integer of 1 (inside the domain) or 0 (outside the domain) +!! The above input arguments, c_lat and c_lon are independent for each observation. +!! + +subroutine lam_domaincheck_circle_c(c_cenlat, c_cenlon, c_radius, & + c_lat, c_lon, c_mask) & + bind(c, name='lam_domaincheck_circle_f90') + implicit none + real(c_float), intent(in ) :: c_cenlat, c_cenlon, c_radius + real(c_float), intent(in ) :: c_lat, c_lon + integer(c_int), intent(inout) :: c_mask + + real(kind_real) :: dlat, dlon, rr + real(kind_real) :: radius, cenlat, cenlon, lat, lon + + ! some need converted from degrees to radians + cenlat = real(c_cenlat, kind_real)*deg2rad + cenlon = real(c_cenlon, kind_real)*deg2rad + radius = real(c_radius, kind_real) ! in km + lat = real(c_lat, kind_real)*deg2rad + lon = real(c_lon, kind_real)*deg2rad + + ! calculate great-circle distance using haversine formula + dlat = half*abs(lat - cenlat) + dlon = half*abs(lon - cenlon) + rr = sqrt( sin(dlat)**2 + cos(lat)*cos(cenlat)*sin(dlon)**2 ) + rr = two*asin(rr)*mean_earth_rad + + ! use rr to determine if mask is 1 (good) or 0 (bad) + c_mask = 0 ! outside domain + if (rr < radius) then + c_mask = 1 + end if + +end subroutine lam_domaincheck_circle_c + +end module ufo_lamdomaincheck_mod_c diff --git a/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.interface.h b/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.interface.h new file mode 100644 index 000000000..e934e23a2 --- /dev/null +++ b/src/ufo/filters/obsfunctions/LAMDomainCheck/LAMDomainCheck.interface.h @@ -0,0 +1,31 @@ +/* + * (C) Copyright 2020 NOAA NWS NCEP EMC + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_LAMDOMAINCHECK_LAMDOMAINCHECK_INTERFACE_H_ +#define UFO_FILTERS_OBSFUNCTIONS_LAMDOMAINCHECK_LAMDOMAINCHECK_INTERFACE_H_ + +#include "oops/base/Variables.h" +#include "ufo/Fortran.h" + +namespace ufo { + +extern "C" { + +// call Fortran code to get ESG grid information + + void lam_domaincheck_esg_f90(const float &, const float &, const float &, const float &, + const float &, const int &, const int &, + const float &, const float &, const float &, const float &, + int &); + + void lam_domaincheck_circle_f90(const float &, const float &, const float &, + const float &, const float &, int &); + +} // extern C + +} // namespace ufo +#endif // UFO_FILTERS_OBSFUNCTIONS_LAMDOMAINCHECK_LAMDOMAINCHECK_INTERFACE_H_ diff --git a/src/ufo/filters/obsfunctions/LAMDomainCheck/esg_grid_mod.F90 b/src/ufo/filters/obsfunctions/LAMDomainCheck/esg_grid_mod.F90 new file mode 100644 index 000000000..a9c8ad554 --- /dev/null +++ b/src/ufo/filters/obsfunctions/LAMDomainCheck/esg_grid_mod.F90 @@ -0,0 +1,202 @@ +! (C) Copyright 2020 NOAA NWS NCEP EMC +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module of helper functions for FV3-LAM ESG grid domain configuration +!> These routines are borrowed from regional_esg_grid.fd from the +!> RRFS Regional Workflow / UFS_UTILS repository +!> Credit goes to R. Jim Purser for original source of these subroutines + +module esg_grid_mod + + use kinds + use ufo_constants_mod, only: pi, deg2rad, rad2deg, zero, one, two + implicit none + private + public :: gtoxm_ak_dd, gtoxm_ak_rr + + interface gtoxm_ak_rr + module procedure gtoxm_ak_rr_m,gtoxm_ak_rr_g; end interface + interface gtoxm_ak_dd + module procedure gtoxm_ak_dd_g; end interface + interface grtoc + module procedure dgrtoc + end interface + + logical ,parameter:: T=.true.,F=.false. !<- for pain-relief in logical ops + +contains + +!============================================================================= +subroutine gtoxm_ak_rr_m(A,K,plat,plon,pazi,lat,lon,xm,ff)! [gtoxm_ak_rr] +!============================================================================= +! Given the map specification (angles in radians), the grid spacing (in +! map-space units) and the sample lat-lon (in radian), return the the +! image in map space in a 2-vector in grid units. If the transformation +! is invalid, return a .true. failure flag. +!============================================================================= +implicit none +real(kind_real), intent(in ):: a,k,plat,plon,pazi,lat,lon +real(kind_real),dimension(2),intent(out):: xm +logical, intent(out):: ff +real(kind_real),dimension(3,3):: prot,azirot +real(kind_real) :: clat,slat,clon,slon,cazi,sazi +real(kind_real),dimension(3) :: xc +!============================================================================= +clat=cos(plat); slat=sin(plat) +clon=cos(plon); slon=sin(plon) +cazi=cos(pazi); sazi=sin(pazi) + +azirot(:,1)=(/ cazi, sazi, zero/) +azirot(:,2)=(/-sazi, cazi, zero/) +azirot(:,3)=(/ zero, zero, one/) + +prot(:,1)=(/ -slon, clon, zero/) +prot(:,2)=(/-slat*clon, -slat*slon, clat/) +prot(:,3)=(/ clat*clon, clat*slon, slat/) +prot=matmul(prot,azirot) + +call grtoc(lat,lon,xc) +xc=matmul(transpose(prot),xc) +call xctoxm_ak(a,k,xc,xm,ff) +end subroutine gtoxm_ak_rr_m +!============================================================================= +subroutine gtoxm_ak_rr_g(A,K,plat,plon,pazi,delx,dely,lat,lon,&! [gtoxm_ak_rr] + xm,ff) +!============================================================================= +! Given the map specification (angles in radians), the grid spacing (in +! map-space units) and the sample lat-lon (in radian), return the the +! image in map space in a 2-vector in grid units. If the transformation +! is invalid, return a .true. failure flag. +!============================================================================= +implicit none +real(kind_real), intent(in ):: a,k,plat,plon,pazi,delx,dely,lat,lon +real(kind_real),dimension(2),intent(out):: xm +logical, intent(out):: ff +!============================================================================= +call gtoxm_ak_rr_m(A,K,plat,plon,pazi,lat,lon,xm,ff); if(ff)return +xm(1)=xm(1)/delx; xm(2)=xm(2)/dely +end subroutine gtoxm_ak_rr_g + +!============================================================================= +subroutine gtoxm_ak_dd_g(A,K,pdlat,pdlon,pdazi,delx,dely,&! [gtoxm_ak_dd] +dlat,dlon, xm,ff) +!============================================================================= +! Like gtoxm_ak_rr_g, except input angles are expressed in degrees +!============================================================================= +implicit none +real(kind_real), intent(in ):: a,k,pdlat,pdlon,pdazi,delx,dely,dlat,dlon +real(kind_real),dimension(2),intent(out):: xm +logical, intent(out):: ff +!----------------------------------------------------------------------------- +real(kind_real):: plat,plon,pazi,lat,lon +!============================================================================= +plat=pdlat*deg2rad ! Convert these angles from degrees to radians +plon=pdlon*deg2rad ! +pazi=pdazi*deg2rad ! +lat=dlat*deg2rad +lon=dlon*deg2rad +call gtoxm_ak_rr_g(A,K,plat,plon,pazi,delx,dely,lat,lon,xm,ff) +end subroutine gtoxm_ak_dd_g +!============================================================================= +subroutine xctoxm_ak(a,k,xc,xm,ff)! [xctoxm_ak] +!============================================================================= +! Inverse mapping of xmtoxc_ak. That is, go from given cartesian unit +! 3-vector, xc, to map coordinate 2-vector xm (or return a raised failure +! flag, FF, if the attempt fails). +!============================================================================= +implicit none +real(kind_real), intent(in ):: a,k +real(kind_real),dimension(3),intent(in ):: xc +real(kind_real),dimension(2),intent(out):: xm +logical, intent(out):: ff +!----------------------------------------------------------------------------- +real(kind_real),dimension(2):: xs,xt +!============================================================================= +ff=F +call xctoxs(xc,xs) +call xstoxt(k,xs,xt,ff); if(ff)return +call xttoxm(a,xt,xm,ff) +end subroutine xctoxm_ak +!============================================================================= +subroutine xctoxs(xc,xs)! [xctoxs] +!============================================================================= +! Inverse of xstoxc. I.e., cartesians to stereographic +!============================================================================= +implicit none +real(kind_real),dimension(3),intent(in ):: xc +real(kind_real),dimension(2),intent(out):: xs +!----------------------------------------------------------------------------- +real(kind_real):: zp +!============================================================================= +zp=one+xc(3); xs=xc(1:2)/zp +end subroutine xctoxs +!============================================================================= +subroutine xstoxt(k,xs,xt,ff)! [xstoxt] +!============================================================================= +! Inverse of xttoxs. +!============================================================================= +implicit none +real(kind_real), intent(in ):: k +real(kind_real),dimension(2),intent(in ):: xs +real(kind_real),dimension(2),intent(out):: xt +logical, intent(out):: ff +!----------------------------------------------------------------------------- +real(kind_real):: s,sc +!============================================================================= +s=k*(xs(1)*xs(1)+xs(2)*xs(2)); sc=one-s +ff=abs(s)>=one; if(ff)return +xt=two*xs/sc +end subroutine xstoxt +!============================================================================= +subroutine xttoxm(a,xt,xm,ff)! [xttoxm] +!============================================================================= +! Inverse of xmtoxt +!============================================================================= +implicit none +real(kind_real), intent(in ):: a +real(kind_real),dimension(2),intent(in ):: xt +real(kind_real),dimension(2),intent(out):: xm +logical ,intent(out):: ff +!----------------------------------------------------------------------------- +integer:: i +!============================================================================= +do i=1,2; call zttozm(a,xt(i),xm(i),ff); if(ff)return; enddo +end subroutine xttoxm +!============================================================================= +subroutine zttozm(a,zt,zm,ff)! [zttozm] +!============================================================================= +! Inverse of zmtozt +!============================================================================= +implicit none +real(kind_real),intent(in ):: a,zt +real(kind_real),intent(out):: zm +logical, intent(out):: ff +!----------------------------------------------------------------------------- +real(kind_real):: ra,razt +!============================================================================= +ff=F +if (a>zero)then; ra=sqrt( a); razt=ra*zt; zm=atan (razt)/ra +elseif(a=one; if(ff)return + zm=atanh(razt)/ra +else ; zm=zt +endif +end subroutine zttozm +!============================================================================= +subroutine dgrtoc(rlat,rlon,xe)! [grtoc] +!============================================================================= +implicit none +real(kind_real), intent(IN ):: rlat,rlon +real(kind_real),dimension(3),intent(OUT):: xe +!----------------------------------------------------------------------------- +real(kind_real) :: sla,cla,slo,clo +!============================================================================= +sla=sin(rlat); cla=cos(rlat) +slo=sin(rlon); clo=cos(rlon) +xe(1)=cla*clo; xe(2)=cla*slo; xe(3)=sla +end subroutine dgrtoc +!============================================================================= + + +end module esg_grid_mod diff --git a/src/ufo/filters/obsfunctions/NearSSTRetCheckIR.cc b/src/ufo/filters/obsfunctions/NearSSTRetCheckIR.cc index 2d2b168a2..9b5c629d6 100755 --- a/src/ufo/filters/obsfunctions/NearSSTRetCheckIR.cc +++ b/src/ufo/filters/obsfunctions/NearSSTRetCheckIR.cc @@ -41,7 +41,6 @@ NearSSTRetCheckIR::NearSSTRetCheckIR(const eckit::LocalConfiguration & conf) // Get test groups from options const std::string &flaggrp = options_.testQCflag.value(); const std::string &errgrp = options_.testObserr.value(); - const std::string &biasgrp = options_.testBias.value(); const std::string &hofxgrp = options_.testHofX.value(); // Include required variables from ObsDiag @@ -53,7 +52,6 @@ NearSSTRetCheckIR::NearSSTRetCheckIR(const eckit::LocalConfiguration & conf) invars_ += Variable("brightness_temperature@"+flaggrp, channels_); invars_ += Variable("brightness_temperature@"+errgrp, channels_); invars_ += Variable("brightness_temperature@"+hofxgrp, channels_); - invars_ += Variable("brightness_temperature@"+biasgrp, channels_); invars_ += Variable("brightness_temperature@ObsValue", channels_); invars_ += Variable("brightness_temperature@ObsError", channels_); invars_ += Variable("sensor_band_central_radiation_wavenumber@VarMetaData"); @@ -83,7 +81,6 @@ void NearSSTRetCheckIR::compute(const ObsFilterData & in, // Get test groups from options const std::string &flaggrp = options_.testQCflag.value(); const std::string &errgrp = options_.testObserr.value(); - const std::string &biasgrp = options_.testBias.value(); const std::string &hofxgrp = options_.testHofX.value(); // Setup vectors to get 2D variables @@ -138,7 +135,7 @@ void NearSSTRetCheckIR::compute(const ObsFilterData & in, } } - // Get bias corrected innovation (tbobs - hofx - bias) + // Get bias corrected innovation (tbobs - hofx) (hofx includes bias correction) std::vector> innovation(nchans, std::vector(nlocs)); for (size_t ichan = 0; ichan < nchans; ++ichan) { in.get(Variable("brightness_temperature@ObsValue", channels_)[ichan], innovation[ichan]); @@ -146,10 +143,6 @@ void NearSSTRetCheckIR::compute(const ObsFilterData & in, for (size_t iloc = 0; iloc < nlocs; ++iloc) { innovation[ichan][iloc] = innovation[ichan][iloc] - values[iloc]; } - in.get(Variable("brightness_temperature@"+biasgrp, channels_)[ichan], values); - for (size_t iloc = 0; iloc < nlocs; ++iloc) { - innovation[ichan][iloc] = innovation[ichan][iloc] - values[iloc]; - } } // Get original observation error (uninflated) @@ -181,10 +174,11 @@ void NearSSTRetCheckIR::compute(const ObsFilterData & in, // Loop through locations // TODO(EL): review the use of irday with EMC - std::vector irday(nchans, 1); + std::vector irday(nchans); for (size_t iloc=0; iloc < nlocs; ++iloc) { bool sea = water_frac[iloc] >= 0.99; for (size_t ichan = 0; ichan < nchans; ++ichan) { + irday[ichan] = 1; out[ichan][iloc] = 0.0; if (water_frac[iloc] > 0.0 && solza[iloc] <= 89.0 && wavenumber[ichan] > 2400.0) { irday[ichan] = 0; @@ -254,7 +248,7 @@ void NearSSTRetCheckIR::compute(const ObsFilterData & in, if (dtz != -999.0) { for (size_t ichan = 0; ichan < nchans; ++ichan) { - if (use_flag[ichan] >= 1 && varinv[ichan][iloc] > 0.0 && dbtdts[ichan][iloc] >= tschk) { + if (use_flag[ichan] >= 1 && varinv[ichan][iloc] > 0.0 && dbtdts[ichan][iloc] > tschk) { float xindx = pow((dbtdts[ichan][iloc] - ts_ave) / (1.0 - ts_ave), 3); float tzchks = tzchk * pow(0.5, xindx); if (std::fabs(dtz) > tzchks) out[ichan][iloc] = 1; diff --git a/src/ufo/filters/obsfunctions/NearSSTRetCheckIR.h b/src/ufo/filters/obsfunctions/NearSSTRetCheckIR.h index 0164fb356..85e034f6f 100755 --- a/src/ufo/filters/obsfunctions/NearSSTRetCheckIR.h +++ b/src/ufo/filters/obsfunctions/NearSSTRetCheckIR.h @@ -41,9 +41,6 @@ class NearSSTRetCheckIRParameters : public oops::Parameters { /// Name of the HofX group used to replace the default group (default is HofX) oops::Parameter testHofX{"test_hofx", "HofX", this}; - /// Name of the bias correction group used to replace the default group (default is ObsBias) - oops::Parameter testBias{"test_bias", "ObsBias", this}; - /// Name of the data group to which the QC flag is applied (default is QCflagsData) oops::Parameter testQCflag{"test_qcflag", "QCflagsData", this}; }; diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorConventional.cc b/src/ufo/filters/obsfunctions/ObsErrorFactorConventional.cc new file mode 100755 index 000000000..168cf3623 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorConventional.cc @@ -0,0 +1,221 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/ObsErrorFactorConventional.h" + +#include + +#include +#include +#include +#include +#include + +#include "eckit/exception/Exceptions.h" +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/Variable.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/Constants.h" + +namespace ufo { + +static ObsFunctionMaker makerSteps_("ObsErrorFactorConventional"); + +// ----------------------------------------------------------------------------- + +ObsErrorFactorConventional::ObsErrorFactorConventional(const eckit::Configuration &config) + : invars_() { + oops::Log::debug() << "ObsErrorFactorConventional: config = " << config << std::endl; + + // Initialize options + options_.reset(new ObsErrorFactorConventionalParameters()); + options_->deserialize(config); + + // Variable to be inflated + const std::vector inflatevars = options_->inflatevars.value(); + + // QC flags come from files or by default from filters + const std::string qcgrp = options_->testQCflag.value(); + + // Include list of required qc flags for test variable + for (size_t iv = 0; iv < inflatevars.size(); ++iv) { + invars_ += Variable(inflatevars[iv]+"@"+qcgrp); + } + + // Include list of required data from MetaData + invars_ += Variable("air_pressure@MetaData"); // observed obs pressure + invars_ += Variable("station_id@MetaData"); // obs station ID + invars_ += Variable("datetime@MetaData"); // obs date and time + + // Include list of required data from GeoVaLs + invars_ += Variable("air_pressure@GeoVaLs"); // need model pressure at half + // sigma levels (dimension=nsig in GSI) +} + +// ----------------------------------------------------------------------------- + +ObsErrorFactorConventional::~ObsErrorFactorConventional() { + oops::Log::debug() << "ObsErrorFactorCon: destructing " << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsErrorFactorConventional::compute(const ObsFilterData & data, + ioda::ObsDataVector & obserr) const { + const int missing = util::missingValue(missing); + static constexpr float con_g_rd = 500.0f*Constants::grav/(273.0f*Constants::rd); + const float tiny_float = FLT_MIN; + + // Get dimensions + const size_t nlocs = data.nlocs(); + const size_t nlevs = data.nlevs(Variable("air_pressure@GeoVaLs")); + + // Get obs space + auto & obsdb = data.obsspace(); + + // Get output variable size + int varsize = obserr.nvars(); + + // Get yaml options + const std::vector inflatevars = options_->inflatevars.value(); + const std::string qcgrp = options_->testQCflag.value(); + + // QC flags can be either from PreQC or filter flags + // qcthres is 0 if using flter flags + // qcthres is 3 (default) or defined through yaml if using PreQC + int qcthres = 0; + if (qcgrp == "PreQC") qcthres = options_->qcthreshold.value().value_or(3); + + // Get MetaData of obs + std::vector ob_pressure(nlocs); + data.get(Variable("air_pressure@MetaData"), ob_pressure); + std::vector ob_stationID(nlocs); + data.get(Variable("station_id@MetaData"), ob_stationID); + std::vector ob_datetime(nlocs); + data.get(Variable("datetime@MetaData"), ob_datetime); + + // Due to splitting across CPUs, it is possible we have zero obs, so just return nothing. + if (ob_datetime.empty()) { + return; + } + + // Get GeoVals of air pressure [Pa] in vertical column + std::vector> prsl(nlevs, std::vector(nlocs)); + for (size_t ilev = 0; ilev < nlevs; ++ilev) { + data.get(Variable("air_pressure@GeoVaLs"), ilev+1, prsl[ilev]); + } + + for (size_t iv = 0; iv < varsize; ++iv) { // Variable loop + // Get QC flags of test variable + std::vector ob_variable_QCflag(nlocs); + std::vector ob_QCflag(nlocs); + data.get(Variable(inflatevars[iv]+"@"+qcgrp), ob_variable_QCflag); + + // Only obs with QCflag <= qcthres will be checked in this obsFunc + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + (ob_variable_QCflag[iloc] <= qcthres && ob_variable_QCflag[iloc] != missing) ? + ob_QCflag[iloc] = 0 : ob_QCflag[iloc] = 100; + } + + // Define variables to be used + std::vector dprsl(nlevs-1); + float error_factor; + float pre1, pre2, maxpre, conpre, vmag, pdiffu, pdiffd, pdifftotal; + int ipass = 0; + int icount = 0; + int ireport = 0; + + ioda::ObsSpace::RecIdxIter irec; // Using obs grouping/sorting indices + for ( irec = obsdb.recidx_begin(); irec != obsdb.recidx_end(); ++irec ) { // record loop + std:: size_t rNum = obsdb.recidx_recnum(irec); + std::vector rSort = obsdb.recidx_vector(irec); + + ireport++; + for (size_t iloc = 0; iloc < rSort.size(); ++iloc) { // profile loop + icount++; + + for (size_t ilev = 0; ilev < nlevs-1; ++ilev) { + // Background pressure level intervals [cb] + dprsl[ilev] = (prsl[ilev][rSort[iloc]]-prsl[ilev+1][rSort[iloc]])*0.001f; + } + + int indexlev = 0; + for (size_t ilev = 1; ilev < nlevs-1; ++ilev) { + if (ob_pressure[rSort[iloc]] < prsl[ilev][rSort[iloc]]) { + indexlev = ilev; + } + } + + pre1 = 0.5f*dprsl[indexlev]; + pre2 = 0.02f*0.001f*prsl[indexlev][rSort[iloc]]; + maxpre = std::max(pre1, pre2); + conpre = con_g_rd*0.001f*ob_pressure[rSort[iloc]]; + vmag = std::min(maxpre, conpre); + + pdiffu = vmag; + pdiffd = vmag; + + if (ob_QCflag[rSort[iloc]] == 0) { + ipass++; + // Search obs from upper levels. If there are multiple observations + // inside the same model interval, use the toppest one to compute + // the pressure interval, pdiffu + int lloc = iloc+1; + while (lloc < rSort.size()) { + float tmp = abs(ob_pressure[rSort[iloc]]-ob_pressure[rSort[lloc]])*0.001f; + if (ob_QCflag[rSort[lloc]] == 0 && tmp < vmag) { + pdiffu = tmp; + break; + } + lloc++; + } + + // Search obs from lower levels. If there are multiple observations + // inside the same model interval, use the lowest one to compute + // the pressure interval, pdiffd + lloc = iloc-1; + while (lloc >= 0) { + float tmp = abs(ob_pressure[rSort[lloc]]-ob_pressure[rSort[iloc]])*0.001f; + if (ob_QCflag[rSort[lloc]] == 0 && tmp < vmag) { + pdiffd = tmp; + break; + } + lloc--; + } + } + + // When there are multiple observations inside the same model interval, the error_factor + // will be bigger than 1 based on the spacing of the these observations + pdifftotal = std::max(pdiffd+pdiffu, 5.0f * tiny_float); + error_factor = sqrt(2.0f*vmag/pdifftotal); + + // Output + obserr[iv][rSort[iloc]] = error_factor; + } + } // iloc loop + if (icount != nlocs) { + std::string errString = "The data should be sorted or icount and nlocs are not consistent: "; + oops::Log::error() << errString << icount << ", "<< nlocs << std::endl; + throw eckit::BadValue(errString); + } // irec loop + oops::Log::debug() << "ObsErrorFactorCon: inflate var, # of reports, total obs, filtered obs = " + << inflatevars[iv] << " " << ireport << " "<< nlocs << " " << ipass << std::endl; + } // iv loop +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & ObsErrorFactorConventional::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorConventional.h b/src/ufo/filters/obsfunctions/ObsErrorFactorConventional.h new file mode 100644 index 000000000..92a98fdf4 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorConventional.h @@ -0,0 +1,118 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORCONVENTIONAL_H_ +#define UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORCONVENTIONAL_H_ + +#include +#include +#include + +#include "ioda/ObsDataVector.h" + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +/// \brief Options controlling ObsErrorFactorConventional ObsFunction +class ObsErrorFactorConventionalParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsErrorFactorConventionalParameters, Parameters) + + public: + /// Inflate variables + oops::RequiredParameter> inflatevars{"inflate variables", this}; + /// Name of the data group to which the QC flag is applied (default is QCflagsData) + oops::Parameter testQCflag{"test QCflag", "QCflagsData", this}; + oops::OptionalParameter qcthreshold{"test QCthreshold", this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Inflate the observation error for conventional as done by GSI-Observer. +/// +/// This routine was designed to mimic the GSI observer code (i.e., subroutine errormod in +/// qcmod.f90) to inflate the observation error for conventional/satwinds using the +/// qc flags generated from a filter or from the input files with a group name +/// (test QCflag) defined in the yaml. The inflation factor is determined by the obseravtion +/// vertical spacing (in pressure) relative to the corresponding model pressure interval. +/// This error inflation obsFuction is used in GSI for temperature, moiture, and winds from +/// conventional obs as well as some of satellite retrievels, e.g., radiosonde/other conventional +/// temperature, moisture, and wind, SCAT winds, VAD winds, and potentially aircraft ascent +/// and descent profiles. +/// +/// Notes: +/// (1) If using this obs function in a filter, please +/// make sure the "filter variables" and "inflate variables" prescribed +/// with the same variable name. +/// (2) This obs funciton requires each of the obs profiles are sorted by pressure +/// in descending order +/// +/// ### example configurations for testing this obs function: ### +/// +/// obs function: +/// name: ObsErrorFactorConventional@ObsFunction +/// variables: [eastward_wind] # Variable name for output +/// tolerance: 1.e-6 +/// options: +/// inflate variables: [eastward_wind] # Ok to be multiple dimensions for running +/// # this obsFunction only (not within a filter) +/// test QCflag: PreQC # Optional. If not defined, use QCflags from prior filters +/// test QCthreshold: 2 # Optonal, only when PreQC is used +/// # Default is 3 for PreQC +/// # In GSI(PreQC): if noiqc (no oiqc)=true, QCthreshold=7; +/// if noiqc=false, QCthreshold=3 +/// +/// ### example configurations for using this obs function in a filter: ### +/// - filter: BlackList +/// filter variables: +/// - name: virtual_temperature # Have to be consistent with "inflate +/// # variables". Therefore, only one variable allowed +/// # while running with this obsFunc +/// action: +/// name: inflate error +/// inflation variable: +/// name: ObsErrorFactorConventional@ObsFunction +/// options: +/// inflate variables: [virtual_temperature] # Have to be consistent with "filter +/// # variables". Therefore, only one +/// # variable allowed +/// +/// ### example configurations for using obsgrouping: ### +/// +/// obsgrouping: +/// group variables: ["station_id", "datetime"] # Choose parameteres to identify each of +/// # the obs profiles +/// sort variable: "air_pressure" +/// sort order: "descending" +/// +class ObsErrorFactorConventional : public ObsFunctionBase { + public: + static const std::string classname() {return "ObsErrorFactorConventional";} + + explicit ObsErrorFactorConventional(const eckit::Configuration &config); + ~ObsErrorFactorConventional(); + + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + std::unique_ptr options_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORCONVENTIONAL_H_ diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorQuotient.cc b/src/ufo/filters/obsfunctions/ObsErrorFactorQuotient.cc new file mode 100644 index 000000000..603edc3da --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorQuotient.cc @@ -0,0 +1,96 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/ObsErrorFactorQuotient.h" + +#include "eckit/exception/Exceptions.h" + +#include "ioda/ObsDataVector.h" + +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" + +namespace ufo { + +static ObsFunctionMaker makerSteps_("ObsErrorFactorQuotient"); + +// ----------------------------------------------------------------------------- + +ObsErrorFactorQuotient::ObsErrorFactorQuotient(const eckit::LocalConfiguration config) + : invars_() { + // Initialize options + options_.deserialize(config); + + // Initialize the two desired variables, numerator and denominator + const Variable &numerator = options_.numerator.value(); + const Variable &denominator = options_.denominator.value(); + ASSERT(numerator.size() == 1); + ASSERT(denominator.size() == 1); + + invars_ += numerator; + invars_ += denominator; + + oops::Log::debug() << "ObsErrorFactorQuotient: config (constructor) = " + << config << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsErrorFactorQuotient::~ObsErrorFactorQuotient() {} + +// ----------------------------------------------------------------------------- + +void ObsErrorFactorQuotient::compute(const ObsFilterData & data, + ioda::ObsDataVector & out) const { + const float missing = util::missingValue(missing); + + // Get the numeratory and denominator names + const Variable &numerator = options_.numerator.value(); + const Variable &denominator = options_.denominator.value(); + oops::Log::debug() << " ObsErrorFactorQuotient, numerator name: " << numerator.variable() + << " and group: " << numerator.group() << std::endl + << " denominator name: " << denominator.variable() + << " and group: " << denominator.group() << std::endl; + + // Populate the arrays. + ioda::ObsDataVector numer(data.obsspace(), numerator.toOopsVariables()); + data.get(numerator, numer); + ioda::ObsDataVector denom(data.obsspace(), denominator.toOopsVariables()); + data.get(denominator, denom); + + // The 1st index of data should have size 1 and 2nd index should be size nlocs. + int iv = 0; + if (numer[iv].size() != out[iv].size() || numer[iv].size() != denom[iv].size()) { + std::ostringstream errString; + errString << "Something is wrong, numer size not equal out or denom size." + << " Sizes: " << numer[iv].size() << " and " << out[iv].size() << std::endl; + oops::Log::error() << errString.str(); + throw eckit::BadValue(errString.str()); + } + + for (size_t jobs = 0; jobs < numer[iv].size(); ++jobs) { + out[iv][jobs] = missing; + if (numer[iv][jobs] == missing || denom[iv][jobs] == missing || denom[iv][jobs] == 0) { + continue; + } + out[iv][jobs] = numer[iv][jobs]/denom[iv][jobs]; + } + + if (options_.save) { + out.save("DerivedValue"); + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & ObsErrorFactorQuotient::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorQuotient.h b/src/ufo/filters/obsfunctions/ObsErrorFactorQuotient.h new file mode 100644 index 000000000..28767e8e4 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorQuotient.h @@ -0,0 +1,83 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORQUOTIENT_H_ +#define UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORQUOTIENT_H_ + +#include +#include + +#include "ioda/ObsDataVector.h" + +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +/// \brief Options controlling ObsErrorFactorQuotient ObsFunction +class ObsErrorFactorQuotientParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsErrorFactorQuotientParameters, Parameters) + + public: + /// the name of the numerator and denominator variables (with group names). + oops::RequiredParameter numerator{"numerator", this}; + oops::RequiredParameter denominator{"denominator", this}; + oops::Parameter save{"save", false, this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Calculate the ratio of two variables, typically related to ObsError +/// +/// This routine was designed to mimic the GSI-Observer method of rejecting obs data +/// when the ratio of the final ObsError, after inflation, is more than a threshold +/// amount greater than the starting ObsError. A boolean optional save variable +/// is available, false by default. The ObsFunction is simple division expected to +/// be used with Bounds Check above a maximum threshold. +/// +/// ### example configurations for a FilterBase derived class: ### +/// +/// - Filter: Bounds Check +/// filter variables: +/// - name: air_temperature +/// action: +/// name: reject +/// maxvalue: 3.6 +/// test variables: +/// - name: ObsErrorFactorQuotient@ObsFunction +/// options: +/// numerator: +/// name: air_temperature@ObsErrorData # After inflation step +/// denominator: +/// name: air_temperature@ObsError +/// defer to post: true # Likely necessary for order of filters +/// +class ObsErrorFactorQuotient : public ObsFunctionBase { + public: + static const std::string classname() {return "ObsErrorFactorQuotient";} + + explicit ObsErrorFactorQuotient(const eckit::LocalConfiguration); + ~ObsErrorFactorQuotient(); + + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + ObsErrorFactorQuotientParameters options_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORQUOTIENT_H_ diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorSfcPressure.cc b/src/ufo/filters/obsfunctions/ObsErrorFactorSfcPressure.cc new file mode 100644 index 000000000..1cf186e34 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorSfcPressure.cc @@ -0,0 +1,188 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/ObsErrorFactorSfcPressure.h" + +#include + +#include +#include + +#include "eckit/exception/Exceptions.h" +#include "ioda/ObsDataVector.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" +#include "ufo/utils/Constants.h" + +namespace ufo { + +static ObsFunctionMaker makerSteps_("ObsErrorFactorSfcPressure"); + +// ----------------------------------------------------------------------------- + +ObsErrorFactorSfcPressure::ObsErrorFactorSfcPressure(const eckit::Configuration &config) + : invars_() { + oops::Log::debug() << "ObsErrorFactorSfcPressure: config = " << config << std::endl; + const float tiny_float = FLT_MIN; + const float huge_float = FLT_MAX; + const float missing = util::missingValue(missing); + // Initialize options + options_.reset(new ObsErrorFactorSfcPressureParameters()); + options_->deserialize(config); + + // Initialize error_min, max from options. Make sure they are sane. + const float error_min = options_->error_min.value(); + const float error_max = options_->error_max.value(); + ASSERT(error_min < error_max); + + // The starting (un-inflated) value of obserror. If running in sequence of filters, + // then it is probably found in ObsErrorData, otherwise, it is probably ObsError. + const std::string errgrp = options_->original_obserr.value(); + invars_ += Variable("surface_pressure@"+errgrp); + + // Include list of required data from ObsValue + invars_ += Variable("surface_pressure@ObsValue"); + invars_ += Variable("air_temperature@ObsValue"); + + // Include list of required data from MetaData + invars_ += Variable("station_elevation@MetaData"); + + // Include list of required data from GeoVaLs + invars_ += Variable("surface_pressure@GeoVaLs"); + invars_ += Variable("air_pressure@GeoVaLs"); + const std::string geovar_temp = options_->geovar_temp.value(); + invars_ += Variable(geovar_temp + "@GeoVaLs"); + const std::string geovar_sfc_geomz = options_->geovar_sfc_geomz.value(); + invars_ += Variable(geovar_sfc_geomz + "@GeoVaLs"); +} + +// ----------------------------------------------------------------------------- + +ObsErrorFactorSfcPressure::~ObsErrorFactorSfcPressure() {} + +// ----------------------------------------------------------------------------- + +void ObsErrorFactorSfcPressure::compute(const ObsFilterData & data, + ioda::ObsDataVector & obserr) const { + const float missing = util::missingValue(missing); + static constexpr float g_over_rd = 1.0f*Constants::grav/Constants::rd; + const float lapse_rate = 1.0f*Constants::Lclr; + + // Ensure that only one output variable is expected. + ASSERT(obserr.nvars() == 1); + + // Get dimensions + size_t nlocs = data.nlocs(); + size_t nlevs = data.nlevs(Variable("air_pressure@GeoVaLs")); + + // Get min, max error values + const float error_min = options_->error_min.value(); + const float error_max = options_->error_max.value(); + + // Get MetaData of station elevation + std::vector ob_elevation(nlocs); + data.get(Variable("station_elevation@MetaData"), ob_elevation); + + // Get ObsValue of surface pressure and temperature (possibly missing). + std::vector ob_pressure_sfc(nlocs); + data.get(Variable("surface_pressure@ObsValue"), ob_pressure_sfc); + std::vector ob_temperature_sfc(nlocs); + data.get(Variable("air_temperature@ObsValue"), ob_temperature_sfc); + + // Get original ObsError of surface pressure + std::vector currentObserr(nlocs); + const std::string errgrp = options_->original_obserr.value(); + data.get(Variable("surface_pressure@"+errgrp), currentObserr); + + // Get GeoVals of surface altitude, pressure, and temperature. + std::vector model_elevation(nlocs); + const std::string geovar_sfc_geomz = options_->geovar_sfc_geomz.value(); + data.get(Variable(geovar_sfc_geomz + "@GeoVaLs"), model_elevation); + std::vector model_pres_sfc(nlocs); + data.get(Variable("surface_pressure@GeoVaLs"), model_pres_sfc); + + // Get GeoVals of air pressure [Pa] and temperature in vertical column. + std::vector> prsl(nlevs, std::vector(nlocs)); + for (size_t ilev = 0; ilev < nlevs; ++ilev) { + size_t level = nlevs - ilev; + data.get(Variable("air_pressure@GeoVaLs"), level, prsl[ilev]); + } + std::vector> tair(nlevs, std::vector(nlocs)); + const std::string geovar_temp = options_->geovar_temp.value(); + for (size_t ilev = 0; ilev < nlevs; ++ilev) { + size_t level = nlevs - ilev; + data.get(Variable(geovar_temp + "@GeoVaLs"), level, tair[ilev]); + } + + // TODO(gthompsn): model temp is virtual_temp whereas obs is sensible temp. + // Also, for now, assigning model lowest level temp as surface temp. + // Lastly, need to make positive that assumption of 0th level is nearest sfc. + std::vector model_temp_sfc(nlocs); + model_temp_sfc = tair[0]; + + float obserror, new_error, error_factor; + float rdelz, rdp, drdp, ddiff, tges, pges, tges2, pges2, psges, pgesorig, drbx; + int iv = 0; + + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + rdelz = ob_elevation[iloc]-model_elevation[iloc]; + pgesorig = model_pres_sfc[iloc]*0.001; // Converting Pascals to cb + psges = log(pgesorig); + + // Calculating drbx with observed temperature. + // If ob temperature missing, then check if model ground is above or below actual ground. + if (ob_temperature_sfc[iloc] != missing) { + drbx = 0.5f*std::abs(model_temp_sfc[iloc]-ob_temperature_sfc[iloc]) + +0.2f+0.005f*std::abs(rdelz); + tges = 0.5f*(model_temp_sfc[iloc]+ob_temperature_sfc[iloc]); + } else { + // TODO(gthompsn): If model terrain below real world, grab nearest Temp,Pres from a + // vertical profile. Shortcut for now is assume lapse_rate and hydrostatic assumption + // over rdelz. The 2.5 addition is **arbitrary** and lapse rate assumption is 5C/km. + if (std::abs(rdelz) < 5.0) { + tges = model_temp_sfc[iloc]; + drbx = 0.1; + } else if (rdelz > 0.0) { + tges2 = model_temp_sfc[iloc] - lapse_rate*rdelz; + drbx = 0.5f*std::abs(model_temp_sfc[iloc]-tges2)+2.5f+0.005f*std::abs(rdelz); + tges = 0.5f*(model_temp_sfc[iloc]+tges2); + } else { + tges = model_temp_sfc[iloc] - 0.5f*lapse_rate*rdelz; + tges2 = tges - lapse_rate*rdelz; + drbx = 0.5f*std::abs(model_temp_sfc[iloc]-tges2)+2.5f+0.005f*std::abs(rdelz); + drbx = drbx - 0.5f*lapse_rate*rdelz; + } + } + + rdp = g_over_rd*rdelz/tges; + pges = exp(log(pgesorig) - rdp); + drdp = pges*(g_over_rd*std::abs(rdelz)*drbx/(tges*tges)); + ddiff = ob_pressure_sfc[iloc]*0.001f - pges; // innovation in cb + + // make adjustment to observational error (also convert to cb) + obserror = currentObserr[iloc]*0.001f; + // TODO(gthompsn): Consider reducing obserror by 0.7 for data near sea-level and small delta-Z. + // if (ob_elevation[iloc] < 10.0f && rdelz < 5.0f) { + // obserror = obserror*0.7; + // } + new_error = obserror + drdp; + new_error = std::max(error_min*0.001f, std::min(new_error, error_max*0.001f)); + error_factor = std::max(0.7f, new_error/obserror); + + obserr[iv][iloc] = error_factor; + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & ObsErrorFactorSfcPressure::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorSfcPressure.h b/src/ufo/filters/obsfunctions/ObsErrorFactorSfcPressure.h new file mode 100644 index 000000000..b6ac851db --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorSfcPressure.h @@ -0,0 +1,90 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORSFCPRESSURE_H_ +#define UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORSFCPRESSURE_H_ + +#include +#include +#include + +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +/// \brief Options controlling ObsErrorFactorSfcPressure ObsFunction +class ObsErrorFactorSfcPressureParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsErrorFactorSfcPressureParameters, Parameters) + + public: + /// the existence of min,max error values are required + oops::RequiredParameter error_min{"error_min", this}; + oops::RequiredParameter error_max{"error_max", this}; + oops::Parameter original_obserr{"original_obserr", "ObsErrorData", this}; + oops::Parameter geovar_sfc_geomz{"geovar_sfc_geomz", "surface_altitude", this}; + oops::Parameter geovar_temp{"geovar_temp", "virtual_temperature", this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Inflate the observation error for surface pressure as done by GSI-Observer. +/// +/// This routine was designed to mimic the GSI observer code (i.e., setupps.f90) to inflate +/// the observation error for surface pressure using the following inputs: +/// Observed surface pressure, station height, and temperature (possibly missing). +/// Model first-guess fields interpolated to the observation location. +/// The starting obserror is then altered by this code with the "inflate error" action, +/// constrained by the values given for error_min and error_max (Pa). For testing +/// purposes, the optional parameter of original_obserr group name such as ObsError to +/// override the default ObsErrorData can be used for tolerance check of reference results. +/// Internally, the code expects to find surface_altitude@GeoVaLs, however, some datasets +/// may have surface_geopotential_height@GeoVaLs in its place. Also, the model vertical +/// profile of temperature is assumed to be virtual_temperature@GeoVaLs, but can be overriden +/// using geovar_temp option set to air_temperature for example. +/// +/// ~~~~ +/// +/// ### example configurations for a FilterBase derived class: ### +/// +/// - filter: BlackList +/// filter variables: +/// - name: surface_pressure +/// action: +/// name: inflate error +/// inflation variable: +/// name: ObsErrorFactorSfcPressure@ObsFunction +/// options: +/// error_min: 100 # 1 mb +/// error_max: 300 # 3 mb +/// geovar_sfc_geomz: surface_geopotential_height # default is surface_altitude +/// +class ObsErrorFactorSfcPressure : public ObsFunctionBase { + public: + static const std::string classname() {return "ObsErrorFactorSfcPressure";} + + explicit ObsErrorFactorSfcPressure(const eckit::Configuration &config); + ~ObsErrorFactorSfcPressure(); + + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + std::unique_ptr options_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_OBSERRORFACTORSFCPRESSURE_H_ diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorSituDependMW.cc b/src/ufo/filters/obsfunctions/ObsErrorFactorSituDependMW.cc index 224ec57d6..a6598aa8e 100755 --- a/src/ufo/filters/obsfunctions/ObsErrorFactorSituDependMW.cc +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorSituDependMW.cc @@ -54,13 +54,11 @@ ObsErrorFactorSituDependMW::ObsErrorFactorSituDependMW(const eckit::LocalConfigu // Get test groups from options const std::string &flaggrp = options_.testQCflag.value(); const std::string &errgrp = options_.testObserr.value(); - const std::string &biasgrp = options_.testBias.value(); const std::string &hofxgrp = options_.testHofX.value(); // Include list of required data from ObsSpace invars_ += Variable("brightness_temperature@ObsValue", channels_); invars_ += Variable("brightness_temperature@"+hofxgrp, channels_); - invars_ += Variable("brightness_temperature@"+biasgrp, channels_); invars_ += Variable("brightness_temperature@"+errgrp, channels_); invars_ += Variable("brightness_temperature@"+flaggrp, channels_); @@ -104,7 +102,6 @@ void ObsErrorFactorSituDependMW::compute(const ObsFilterData & in, // Get test groups from options const std::string &errgrp = options_.testObserr.value(); const std::string &flaggrp = options_.testQCflag.value(); - const std::string &biasgrp = options_.testBias.value(); const std::string &hofxgrp = options_.testHofX.value(); // Get clear-sky observation error from options @@ -152,17 +149,15 @@ void ObsErrorFactorSituDependMW::compute(const ObsFilterData & in, } } - // Calculate bias corrected innovation: ObsValue - HofX - ObsBias + // Calculate bias corrected innovation: ObsValue - HofX (HofX includes bias correction) std::vector> btobs(nchans, std::vector(nlocs)); std::vector> hofx(nchans, std::vector(nlocs)); - std::vector> bias(nchans, std::vector(nlocs)); std::vector> innov(nchans, std::vector(nlocs)); for (size_t ichan = 0; ichan < nchans; ++ichan) { in.get(Variable("brightness_temperature@ObsValue", channels_)[ichan], btobs[ichan]); in.get(Variable("brightness_temperature@"+hofxgrp, channels_)[ichan], hofx[ichan]); - in.get(Variable("brightness_temperature@"+biasgrp, channels_)[ichan], bias[ichan]); for (size_t iloc = 0; iloc < nlocs; ++iloc) { - innov[ichan][iloc] = btobs[ichan][iloc] - hofx[ichan][iloc] - bias[ichan][iloc]; + innov[ichan][iloc] = btobs[ichan][iloc] - hofx[ichan][iloc]; } } diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorSituDependMW.h b/src/ufo/filters/obsfunctions/ObsErrorFactorSituDependMW.h index c4402d315..fa721d25d 100755 --- a/src/ufo/filters/obsfunctions/ObsErrorFactorSituDependMW.h +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorSituDependMW.h @@ -60,10 +60,6 @@ class ObsErrorFactorSituDependMWParameters : public oops::Parameters { /// Name of the HofX group used to replace the default group (default is HofX) oops::Parameter testHofX{"test_hofx", "HofX", this}; - - /// Name of the group for bias correction terms used to replace the default group - /// (default is ObsBiasTerm) - oops::Parameter testBias{"test_bias", "ObsBias", this}; }; /// diff --git a/src/ufo/filters/obsfunctions/ObsErrorFactorTopoRad.cc b/src/ufo/filters/obsfunctions/ObsErrorFactorTopoRad.cc index a0901cc0a..736c831a8 100755 --- a/src/ufo/filters/obsfunctions/ObsErrorFactorTopoRad.cc +++ b/src/ufo/filters/obsfunctions/ObsErrorFactorTopoRad.cc @@ -47,7 +47,7 @@ ObsErrorFactorTopoRad::ObsErrorFactorTopoRad(const eckit::LocalConfiguration & c std::string inst, sat; splitInstSat(sensor, inst, sat); ASSERT(inst == "amsua" || inst == "atms" || - inst == "iasi" || inst == "cris-fsr" || inst == "airs"); + inst == "iasi" || inst == "cris-fsr" || inst == "airs" || inst == "avhrr3"); if (inst == "amsua" || inst == "atms") { // Get test groups from options @@ -91,7 +91,7 @@ void ObsErrorFactorTopoRad::compute(const ObsFilterData & in, in.get(Variable("surface_geopotential_height@GeoVaLs"), zsges); // Inflate obs error as a function of terrian height (>2000) and surface-to-space transmittance - if (inst == "iasi" || inst == "cris-fsr" || inst == "airs") { + if (inst == "iasi" || inst == "cris-fsr" || inst == "airs" || inst == "avhrr3") { std::vector tao_sfc(nlocs); for (size_t ich = 0; ich < nchans; ++ich) { in.get(Variable("transmittances_of_atmosphere_layer@ObsDiag", channels_)[ich], @@ -146,7 +146,8 @@ void ObsErrorFactorTopoRad::compute(const ObsFilterData & in, } } else { oops::Log::error() << "ObsErrorFactorTopoRad: Invalid instrument (sensor) specified: " << inst - << " The valid instruments are: iasi, cris-fsr, airs, amsua and atms" + << " The valid instruments are: iasi, cris-fsr, airs, avhrr3, " + << " amsua and atms" << std::endl; } } diff --git a/src/ufo/filters/obsfunctions/ObsErrorModelRamp.cc b/src/ufo/filters/obsfunctions/ObsErrorModelRamp.cc index 76e37feac..8b96de32d 100755 --- a/src/ufo/filters/obsfunctions/ObsErrorModelRamp.cc +++ b/src/ufo/filters/obsfunctions/ObsErrorModelRamp.cc @@ -62,6 +62,16 @@ ObsErrorModelRamp::ObsErrorModelRamp(const eckit::LocalConfiguration config) ASSERT(err0[i] > 0.0); ASSERT(err1[i] > 0.0); } + if (options_.x2.value() != boost::none && options_.err2.value() != boost::none) { + const std::vector &x2 = options_.x2.value().get(); + const std::vector &err2 = options_.err2.value().get(); + ASSERT(x2.size() == yvar.size()); + ASSERT(err2.size() == yvar.size()); + for (size_t i = 0; i < yvar.size(); ++i) { + ASSERT(x2[i] >= x1[i]); + ASSERT(err2[i] > 0.0); + } + } } // ----------------------------------------------------------------------------- @@ -93,6 +103,16 @@ void ObsErrorModelRamp::compute(const ObsFilterData & in, float slope; + // Optional extra ramp function + std::vector x2; + std::vector err2; + bool cal_err2_x2 = false; + float slope2 = missing; + if (options_.x2.value() != boost::none && options_.err2.value() != boost::none) { + x2 = options_.x2.value().get(); + err2 = options_.x2.value().get(); + cal_err2_x2 = true; + } // Loop over selected variables for (size_t jvar = 0; jvar < out.nvars(); ++jvar) { size_t ivar = std::min(jvar, xvar.size() - 1); @@ -103,6 +123,13 @@ void ObsErrorModelRamp::compute(const ObsFilterData & in, } else { slope = missing; } + if (cal_err2_x2) { + if (x2[jvar] > x1[jvar]) { + slope2 = (err2[jvar] - err1[jvar]) / (x2[jvar] - x1[jvar]); + } else { + slope2 = missing; + } + } // Calculate piece-wise function value across locations for (size_t iloc = 0; iloc < in.nlocs(); ++iloc) { @@ -112,6 +139,12 @@ void ObsErrorModelRamp::compute(const ObsFilterData & in, out[jvar][iloc] = err0[jvar]; } else if (xvals[ivar][iloc] < x1[jvar] && slope != missing) { out[jvar][iloc] = err0[jvar] + slope * (xvals[ivar][iloc] - x0[jvar]); + } else if (cal_err2_x2) { + if (xvals[ivar][iloc] < x2[jvar] && slope2 != missing) { + out[jvar][iloc] = err1[jvar] + slope2 * (xvals[ivar][iloc] - x1[jvar]); + } else { + out[jvar][iloc] = err2[jvar]; + } } else { out[jvar][iloc] = err1[jvar]; } diff --git a/src/ufo/filters/obsfunctions/ObsErrorModelRamp.h b/src/ufo/filters/obsfunctions/ObsErrorModelRamp.h index 0e3aa5f60..94e15b177 100755 --- a/src/ufo/filters/obsfunctions/ObsErrorModelRamp.h +++ b/src/ufo/filters/obsfunctions/ObsErrorModelRamp.h @@ -43,10 +43,14 @@ class ObsErrorModelRampParameters : public oops::Parameters { oops::RequiredParameter> x0{"x0", this}; /// x-coordinate of the upper ramp inflection point oops::RequiredParameter> x1{"x1", this}; + /// (optinal) an extra upper ramp. + oops::OptionalParameter> x2{"x2", this}; /// y-coordinate of the lower ramp inflection point oops::RequiredParameter> err0{"err0", this}; /// y-coordinate of the upper ramp inflection point oops::RequiredParameter> err1{"err1", this}; + /// (optional) an extra error value. + oops::OptionalParameter> err2{"err2", this}; /// whether to save xvar values to the ObsSpace oops::Parameter save{"save", false, this}; }; @@ -73,10 +77,27 @@ class ObsErrorModelRampParameters : public oops::Parameters { /// ' ' /// x0 x1 /// ~~~~ +/// In case there are x2 and err2 values: +/// +/// err2 |- - - - - - - *--- +/// | , ' . +/// | , ' . +/// | , ' . +/// err1 |- - - *--- . +/// | ,'. . +/// | ,' . . +/// | ,' . . +/// err0 |--*' . . +/// | . . . +/// '--+-------+--------------- +/// ' ' ' +/// x0 x1 x2 +/// ~~~~ /// /// Notes: -/// - for a decaying ramp, set err1 < err0 -/// - for a step function, set x0 == x1 +/// - for a decaying ramp, set err1 < err0 and/or err2 < err1. +/// - for a step function starting at either the first or second inflection point, set x0 == x1 +/// or x2 == x1, respectively. /// /// ### example configurations for a FilterBase derived class: ### /// diff --git a/src/ufo/filters/obsfunctions/ObsErrorModelStepwiseLinear.cc b/src/ufo/filters/obsfunctions/ObsErrorModelStepwiseLinear.cc new file mode 100644 index 000000000..394564dc1 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsErrorModelStepwiseLinear.cc @@ -0,0 +1,178 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include + +#include "ufo/filters/obsfunctions/ObsErrorModelStepwiseLinear.h" + +#include "eckit/exception/Exceptions.h" + +#include "ioda/ObsDataVector.h" + +#include "oops/util/CompareNVectors.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" +#include "oops/util/PropertiesOfNVectors.h" + +namespace ufo { + +static ObsFunctionMaker makerSteps_("ObsErrorModelStepwiseLinear"); + +// ----------------------------------------------------------------------------- + +ObsErrorModelStepwiseLinear::ObsErrorModelStepwiseLinear(const eckit::LocalConfiguration config) + : invars_() { + // Initialize options + options_.deserialize(config); + + // Get piece-wise parameters from options. + const std::vector &xvals = options_.xvals.value(); + const std::vector &errors = options_.errors.value(); + + // Ensure same size vectors (xvals and errors); Also ensure more than one value in each. + if (!oops::allVectorsSameNonZeroSize(xvals, errors) || + ((xvals.size() <= 1 || errors.size() <= 1))) { + std::ostringstream errString; + errString << "At least one vector is the wrong size - either unequal or less than 2." + << std::endl << "Vector sizes of xvals, errors: " + << oops::listOfVectorSizes(xvals, errors) << std::endl; + oops::Log::error() << errString.str(); + throw eckit::BadValue(errString.str()); + } + + // Initialize x-variable + const Variable &xvar = options_.xvar.value(); + ASSERT(xvar.size() == 1); + invars_ += xvar; + + // In the case of using a multiply operator, load it into invars as well. + if (options_.scale_factor_var.value() != boost::none) { + multiplicative_ = true; + const boost::optional &scale_factor_var = options_.scale_factor_var.value(); + invars_ += *scale_factor_var; + oops::Log::debug() << " StepwiseLinear, scale_factor_var: " << *scale_factor_var << std::endl; + } + + // Check that all errors >= 0 + for (size_t i = 0; i < errors.size(); ++i) { + ASSERT(errors[i] >= 0.0); + } + + // In order to check for beyond range of xvals, determine if ascending or descending. + // Also ensure that the entire vector is consistent throughout and no consecutive elements + // of xval are equal. + if (xvals.back() < xvals.front()) { + isAscending_ = false; + } + for (size_t kv = 1; kv < xvals.size(); ++kv) { + if ((xvals[kv] >= xvals[kv-1] && !isAscending_) || (xvals[kv] <= xvals[kv-1] && isAscending_)) { + std::ostringstream errString; + errString << "The xvals vector of elements is NOT internally consistent." + << " It must be entirely either ascending or descending order," + << " or two consecutive values are the same." << std::endl; + oops::Log::error() << errString.str(); + throw eckit::BadValue(errString.str()); + } + } + oops::Log::debug() << "ObsErrorModelStepwiseLinear: config (constructor) = " + << config << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsErrorModelStepwiseLinear::~ObsErrorModelStepwiseLinear() {} + +// ----------------------------------------------------------------------------- + +void ObsErrorModelStepwiseLinear::compute(const ObsFilterData & data, + ioda::ObsDataVector & obserr) const { + const float missing = util::missingValue(missing); + // Linearly interpolate from y0 to y1 at xstar between x0 and x1 to arrive at error + float x0, x1, y0, y1; + float xstar, error; + + // Get the x-variable name and piece-wise parameters from options + const Variable &xvar = options_.xvar.value(); + const std::vector &xvals = options_.xvals.value(); + const std::vector &errors = options_.errors.value(); + oops::Log::debug() << " ObsErrorModelStepwiseLinear, x-variable name: " << xvar.variable() + << " and group: " << xvar.group() << std::endl; + + // Populate the testdata array. xstar is just the 0..nloc-1 value of testvar[iv] + // At each nloc, find matching (x0,x1) and (y0,y1) pair for linear interp. + ioda::ObsDataVector testdata(data.obsspace(), xvar.toOopsVariables()); + data.get(xvar, testdata); + + // Declare a unique_ptr, but keep it empty for now (don’t assign anything to it). + std::unique_ptr> obvalues; + + // If using the multiply operator, load up the variable (scale_factor_var) to multiply by. + if (multiplicative_) { + const boost::optional &scale_factor_var = options_.scale_factor_var.value(); + oops::Log::debug() << " ObsErrorModelStepwiseLinear, scale_factor_var name: " + << (*scale_factor_var).variable() << " and group: " + << (*scale_factor_var).group() << std::endl; + obvalues.reset(new ioda::ObsDataVector(data.obsspace(), + (*scale_factor_var).toOopsVariables())); + data.get(*scale_factor_var, *obvalues); + } + + // The 1st index of data should have size 1 and 2nd index should be size nlocs. + int iv = 0; + if (testdata[iv].size() != obserr[iv].size()) { + std::ostringstream errString; + errString << "Something is wrong, testdata size not equal obserr size." + << " Sizes: " << testdata[iv].size() << " and " << obserr[iv].size() << std::endl; + oops::Log::error() << errString.str(); + throw eckit::BadValue(errString.str()); + } + + for (size_t jobs = 0; jobs < testdata[iv].size(); ++jobs) { + error = 0.0; + obserr[iv][jobs] = missing; + if (testdata[iv][jobs] == missing) { + continue; + } + xstar = testdata[iv][jobs]; + if ((xstar <= xvals[0] && isAscending_) || (xstar >= xvals[0] && !isAscending_)) { + error = errors[0]; + } else if ((xstar >= xvals.back() && isAscending_) + || (xstar <= xvals.back() && !isAscending_)) { + error = errors[errors.size()-1]; + } else { + for (size_t kv = 1; kv < xvals.size(); ++kv) { + if ((isAscending_ && (xstar > xvals[kv-1]) && (xstar <= xvals[kv])) || + (!isAscending_ && (xstar < xvals[kv-1]) && (xstar >= xvals[kv]))) { + x0 = xvals[kv-1]; + x1 = xvals[kv]; + y0 = errors[kv-1]; + y1 = errors[kv]; + error = y0 + (xstar-x0)*((y1-y0)/(x1-x0)); + break; + } + } + } + // TODO(gthompsn): probably need this next line for when filtervariable is flagged missing + // if (!flagged_[jv][jobs]) obserr[jv][jobs] = error; + if (multiplicative_) { + obserr[iv][jobs] = error*(*obvalues)[iv][jobs]; + } else { + obserr[iv][jobs] = error; + } + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & ObsErrorModelStepwiseLinear::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/ObsErrorModelStepwiseLinear.h b/src/ufo/filters/obsfunctions/ObsErrorModelStepwiseLinear.h new file mode 100644 index 000000000..cfaf0ead3 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsErrorModelStepwiseLinear.h @@ -0,0 +1,114 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_OBSERRORMODELSTEPWISELINEAR_H_ +#define UFO_FILTERS_OBSFUNCTIONS_OBSERRORMODELSTEPWISELINEAR_H_ + +#include +#include + +#include "ioda/ObsDataVector.h" + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +/// \brief Options controlling ObsErrorModelStepwiseLinear ObsFunction +class ObsErrorModelStepwiseLinearParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsErrorModelStepwiseLinearParameters, Parameters) + + public: + /// the name of the xvar + oops::RequiredParameter xvar{"xvar", this}; + /// vector of X-steps + oops::RequiredParameter> xvals{"xvals", this}; + /// vector of error values corresponding to vector of X-steps + oops::RequiredParameter> errors{"errors", this}; + /// When final answer is a multiplication factor, we need to know which variable to multiply + oops::OptionalParameter scale_factor_var{"scale_factor_var", this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Parameterize the observation error as a series of steps with +/// linear interpolation between each step. +/// +/// This routine was designed to mimic the GSI fix-file of prepobs_errtable.txt +/// Input is a vector of x-values (e.g. pressures) and corresponding vector of obserrors. +/// Interpolation in X-coordinate requires the value of X for which the output, Y, is +/// calculated using linear interp of obserrors between the steps. +/// If the optional "scale_factor_var" exists, then the final output obserr is +/// calculated as a result of linear interpolation of errors times the scale_factor_var. +/// An example of such usage is RH obserror values between zero and one multiplied by +/// specific_humidity@ObsValue for final ObsError. +/// ~~~~ +/// +/// + err_n o +/// | / +/// | / +/// + err_n-1 o +/// | / +/// obserr + * +/// | / +/// | / +/// err2 + o +/// | / +/// | / +/// err1 + o +/// | / +/// err0 +-----+------+---------*---+--------+ +/// p_0, p_1, p_2,... p* p_n-1, p_n +/// +/// ~~~~ +/// +/// ### example configurations for a FilterBase derived class: ### +/// +/// - Filter: {Filter Name} +/// +/// #### Example for air temperature assigned obserror by pressure (e.g., sonde data) #### +/// +/// filter variables: +/// - name: air_temperature +/// action: +/// name: assign error +/// error function: +/// name: ObsErrorModelStepwiseLinear@ObsFunction +/// options: +/// xvar: +/// name: air_pressure@ObsValue +/// xvals: [110000, 85000, 50000, 25000, 10000, 1] #Pressure (Pa) +/// errors: [1.1, 1.3, 1.8, 2.4, 4.0, 4.5] +/// +class ObsErrorModelStepwiseLinear : public ObsFunctionBase { + public: + static const std::string classname() {return "ObsErrorModelStepwiseLinear";} + + explicit ObsErrorModelStepwiseLinear(const eckit::LocalConfiguration); + ~ObsErrorModelStepwiseLinear(); + + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + ObsErrorModelStepwiseLinearParameters options_; + bool isAscending_ = true; + bool multiplicative_ = false; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_OBSERRORMODELSTEPWISELINEAR_H_ diff --git a/src/ufo/filters/obsfunctions/ObsFunctionLinearCombination.cc b/src/ufo/filters/obsfunctions/ObsFunctionLinearCombination.cc new file mode 100644 index 000000000..189ce9b12 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsFunctionLinearCombination.cc @@ -0,0 +1,80 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/ObsFunctionLinearCombination.h" + +#include + +#include "ioda/ObsDataVector.h" +#include "oops/util/missingValues.h" + +namespace ufo { + +static ObsFunctionMaker + makerLinearCombination_("LinearCombination"); + +// ----------------------------------------------------------------------------- + +LinearCombination::LinearCombination(const eckit::LocalConfiguration & conf) + : invars_() { + // Check options + options_.deserialize(conf); + + // number of input variables + int nv = options_.variables.value().size(); + + // get variable informations + std::vector variables = options_.variables.value(); + for (size_t ii = 0; ii < nv; ++ii) { + invars_ += variables[ii]; + } +} + +// ----------------------------------------------------------------------------- + +LinearCombination::~LinearCombination() {} + +// ----------------------------------------------------------------------------- + +void LinearCombination::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + // dimension + const size_t nlocs = in.nlocs(); + + // number of input variables + int nv = invars_.size(); + + // get coefs for linear combination + const std::vector coefs = options_.coefs.value(); + + // sanity check + ASSERT(coefs.size() == invars_.size() ); + + // compute linear conbination of input variables + const float missing = util::missingValue(missing); + std::vector varin(nlocs); + for (size_t ii = 0; ii < nv; ++ii) { + in.get(invars_[ii], varin); + for (size_t jj = 0; jj < nlocs; ++jj) { + if ( varin[jj] == missing || out[0][jj] == missing ) { + out[0][jj] = missing; + } else { + out[0][jj] += coefs[ii]*varin[jj]; + } + } + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & LinearCombination::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/ObsFunctionLinearCombination.h b/src/ufo/filters/obsfunctions/ObsFunctionLinearCombination.h new file mode 100644 index 000000000..c68726442 --- /dev/null +++ b/src/ufo/filters/obsfunctions/ObsFunctionLinearCombination.h @@ -0,0 +1,74 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_OBSFUNCTIONLINEARCOMBINATION_H_ +#define UFO_FILTERS_OBSFUNCTIONS_OBSFUNCTIONLINEARCOMBINATION_H_ + +#include +#include + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variable.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +/// \brief Options controlling ObsFunctionLinearCombination ObsFunction +class LinearCombinationParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(LinearCombinationParameters, Parameters) + + public: + /// Input variables of the linear combination + oops::RequiredParameter> variables{"variables", this}; + /// coefficient associated with the above variables + oops::RequiredParameter> coefs{"coefs", this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Outputs a linear combination of variables +/// +/// For example, the following +/// +/// obs function: +/// name: LinearCombination@ObsFunction +/// options: +/// variables: [representation_error@GeoVaLs, +/// sea_water_temperature@ObsError] +/// coefs: [0.1, +/// 1.0] +/// +/// will return 0.1 *representation_error@GeoVaLs + +/// 1.0 * sea_water_temperature@ObsError +/// + + +class LinearCombination : public ObsFunctionBase { + public: + explicit LinearCombination(const eckit::LocalConfiguration &); + ~LinearCombination(); + + void compute(const ObsFilterData &, + ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + LinearCombinationParameters options_; + ufo::Variables invars_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_OBSFUNCTIONLINEARCOMBINATION_H_ diff --git a/src/ufo/filters/obsfunctions/ObsFunctionVelocity.cc b/src/ufo/filters/obsfunctions/ObsFunctionVelocity.cc index ef8594efb..a038f04d1 100644 --- a/src/ufo/filters/obsfunctions/ObsFunctionVelocity.cc +++ b/src/ufo/filters/obsfunctions/ObsFunctionVelocity.cc @@ -44,11 +44,8 @@ void ObsFunctionVelocity::compute(const ObsFilterData & in, for (size_t jj = 0; jj < nlocs; ++jj) { if (u[jj] != missing && v[jj] != missing) { out[0][jj] = sqrt(u[jj]*u[jj] + v[jj]*v[jj]); - oops::Log::debug() << "u, v: " << u[jj] << ", " - << v[jj] << ", speed=" << out[0][jj] << std::endl; } else { out[0][jj] = missing; - oops::Log::debug() << "u and v are missing at index, " << jj << std::endl; } } } diff --git a/src/ufo/filters/obsfunctions/SCATRetMW.cc b/src/ufo/filters/obsfunctions/SCATRetMW.cc index 08b0662c2..8329e89f8 100755 --- a/src/ufo/filters/obsfunctions/SCATRetMW.cc +++ b/src/ufo/filters/obsfunctions/SCATRetMW.cc @@ -17,6 +17,7 @@ #include "ioda/ObsDataVector.h" #include "oops/util/IntSetParser.h" +#include "oops/util/missingValues.h" #include "ufo/filters/Variable.h" #include "ufo/utils/Constants.h" @@ -83,24 +84,21 @@ void SCATRetMW::compute(const ObsFilterData & in, in.get(Variable("brightness_temperature@"+options_.testBias.value(), channels_)[0], bias238); in.get(Variable("brightness_temperature@"+options_.testBias.value(), channels_)[1], bias314); in.get(Variable("brightness_temperature@"+options_.testBias.value(), channels_)[2], bias890); - // Add bias correction to the assigned group + // Add bias correction to the assigned group (only need to do it for ObsValue, since HofX + // includes bias correction if (options_.addBias.value() == "ObsValue") { for (size_t iloc = 0; iloc < nlocs; ++iloc) { bt238[iloc] = bt238[iloc] - bias238[iloc]; bt314[iloc] = bt314[iloc] - bias314[iloc]; bt890[iloc] = bt890[iloc] - bias890[iloc]; } - } else { - for (size_t iloc = 0; iloc < nlocs; ++iloc) { - bt238[iloc] = bt238[iloc] + bias238[iloc]; - bt314[iloc] = bt314[iloc] + bias314[iloc]; - bt890[iloc] = bt890[iloc] + bias890[iloc]; - } } } + const float missing = util::missingValue(missing); // Retrieve scattering index for (size_t iloc = 0; iloc < nlocs; ++iloc) { - if (water_frac[iloc] >= 0.99) { + if (water_frac[iloc] >= 0.99 && + bt238[iloc] != missing && bt314[iloc] != missing && bt890[iloc] != missing) { out[igrp][iloc] = -113.2 + (2.41 - 0.0049 * bt238[iloc]) * bt238[iloc] + 0.454 * bt314[iloc] - bt890[iloc]; out[igrp][iloc] = std::max(0.f, out[igrp][iloc]); diff --git a/src/ufo/filters/obsfunctions/SIRetMW.cc b/src/ufo/filters/obsfunctions/SIRetMW.cc index e9f20b59e..e3024da12 100755 --- a/src/ufo/filters/obsfunctions/SIRetMW.cc +++ b/src/ufo/filters/obsfunctions/SIRetMW.cc @@ -95,7 +95,8 @@ void SIRetMW::compute(const ObsFilterData & in, bias90.assign(nlocs, 0.0f); bias150.assign(nlocs, 0.0f); } - // Add bias correction to the assigned group + // Add bias correction to the assigned group (only need to do it for ObsValue, since + // HofX includes bias correction if (options_.addBias.value() == "ObsValue") { for (size_t iloc = 0; iloc < nlocs; ++iloc) { bt90[iloc] = bt90[iloc] - bias90[iloc]; @@ -103,8 +104,6 @@ void SIRetMW::compute(const ObsFilterData & in, } } else { for (size_t iloc = 0; iloc < nlocs; ++iloc) { - bt90[iloc] = bt90[iloc] + bias90[iloc]; - bt150[iloc] = bt150[iloc] + bias150[iloc]; // Temporarily account for ZERO clear-sky BT output from CRTM if (clr90[iloc] > -1.0f && clr90[iloc] < 1.0f) { clr90[iloc] = bt90[iloc]; diff --git a/src/ufo/filters/obsfunctions/SatWindsLNVDCheck.cc b/src/ufo/filters/obsfunctions/SatWindsLNVDCheck.cc new file mode 100644 index 000000000..f3c3203a0 --- /dev/null +++ b/src/ufo/filters/obsfunctions/SatWindsLNVDCheck.cc @@ -0,0 +1,86 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/SatWindsLNVDCheck.h" + +#include +#include + +#include "ioda/ObsDataVector.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/Variable.h" + +namespace ufo { + +static ObsFunctionMaker makerObsFuncSatWindsLNVDCheck_("SatWindsLNVDCheck"); + +// ----------------------------------------------------------------------------- + +SatWindsLNVDCheck::SatWindsLNVDCheck(const eckit::LocalConfiguration & conf) + : invars_() { + oops::Log::debug() << "SatWindsLNVDCheck: config = " << conf << std::endl; + // Initialize options + options_.deserialize(conf); + + // We need to retrieve the observed wind components. + invars_ += Variable("eastward_wind@ObsValue"); + invars_ += Variable("northward_wind@ObsValue"); + + // Typical use would be HofX group, but during testing, we include option for GsiHofX + std::string test_hofx = options_.test_hofx.value(); + invars_ += Variable("eastward_wind@" + test_hofx); + invars_ += Variable("northward_wind@" + test_hofx); + + // TODO(gthompsn): Need to include a check that whatever HofX group name used actually exists. +} + +// ----------------------------------------------------------------------------- + +SatWindsLNVDCheck::~SatWindsLNVDCheck() {} + +// ----------------------------------------------------------------------------- + +void SatWindsLNVDCheck::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + const size_t nlocs = in.nlocs(); + const float missing = util::missingValue(missing); + + // Ensure that only one output variable is expected. + ASSERT(out.nvars() == 1); + + // Retrieve SatWinds observations of wind components + std::vector u, v; + in.get(Variable("eastward_wind@ObsValue"), u); + in.get(Variable("northward_wind@ObsValue"), v); + // Retrieve Model HofX wind components + std::string test_hofx = options_.test_hofx.value(); + std::vector um, vm; + in.get(Variable("eastward_wind@" + test_hofx), um); + in.get(Variable("northward_wind@" + test_hofx), vm); + + for (size_t jj = 0; jj < nlocs; ++jj) { + if (u[jj] != missing && v[jj] != missing) { + out[0][jj] = sqrt((u[jj]-um[jj])*(u[jj]-um[jj]) + (v[jj]-vm[jj])*(v[jj]-vm[jj])) + / log(sqrt(u[jj]*u[jj] + v[jj]*v[jj])); + oops::Log::debug() << "u, v: " << u[jj] << ", " << v[jj] + << " um, vm: " << um[jj] << ", " << vm[jj] + << " LNVD: " << out[0][jj] << std::endl; + } else { + out[0][jj] = missing; + } + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & SatWindsLNVDCheck::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/SatWindsLNVDCheck.h b/src/ufo/filters/obsfunctions/SatWindsLNVDCheck.h new file mode 100644 index 000000000..6258d24c8 --- /dev/null +++ b/src/ufo/filters/obsfunctions/SatWindsLNVDCheck.h @@ -0,0 +1,71 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_SATWINDSLNVDCHECK_H_ +#define UFO_FILTERS_OBSFUNCTIONS_SATWINDSLNVDCHECK_H_ + +#include + +#include "oops/util/parameters/Parameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" + +namespace ufo { + +/// +/// \brief Only one option to override the source of HofX wind components. +/// +class SatWindsLNVDCheckParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(SatWindsLNVDCheckParameters, Parameters) + + public: + /// Name of the HofX group used to replace the default group (default is HofX) + oops::Parameter test_hofx{"test_hofx", "HofX", this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Compute the log-normal vector difference (LNVD) of observation-model +/// SatWinds. If the LNVD is greater than a threshold (usually 3.0), then +/// flag the location as bad. In regular usage, the test_hofx option would +/// be omitted in order that HofX is used for the model wind components. +/// +/// ~~~ +/// +/// ### Sample YAML configuration +/// - filter: Bounds Check +/// filter variables: +/// - name: eastward_wind +/// - name: northward_wind +/// test variables: +/// - name: SatWindsLNVDCheck@ObsFunction +/// options: +/// test_hofx: GsiHofX +/// maxvalue: 3 +/// +class SatWindsLNVDCheck : public ObsFunctionBase { + public: + static const std::string classname() {return "SatWindsLNVDCheck";} + + explicit SatWindsLNVDCheck(const eckit::LocalConfiguration & + = eckit::LocalConfiguration()); + ~SatWindsLNVDCheck(); + + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + SatWindsLNVDCheckParameters options_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_SATWINDSLNVDCHECK_H_ diff --git a/src/ufo/filters/obsfunctions/SatWindsSPDBCheck.cc b/src/ufo/filters/obsfunctions/SatWindsSPDBCheck.cc new file mode 100644 index 000000000..17223b428 --- /dev/null +++ b/src/ufo/filters/obsfunctions/SatWindsSPDBCheck.cc @@ -0,0 +1,110 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/SatWindsSPDBCheck.h" + +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/Variable.h" + +namespace ufo { + +static ObsFunctionMaker makerObsFuncSatWindsSPDBCheck_("SatWindsSPDBCheck"); + +// ----------------------------------------------------------------------------- + +SatWindsSPDBCheck::SatWindsSPDBCheck(const eckit::LocalConfiguration & conf) + : invars_() { + oops::Log::debug() << "SatWindsSPDBCheck: config = " << conf << std::endl; + // Initialize options + options_.deserialize(conf); + + // Initialize error_min, and max from options. Make sure they are sane. + const float error_min = options_.error_min.value(); + const float error_max = options_.error_max.value(); + ASSERT(error_min < error_max); + + // We need to retrieve the observed wind components. + invars_ += Variable("eastward_wind@ObsValue"); + invars_ += Variable("northward_wind@ObsValue"); + + // Typical use would be HofX group, but during testing, we include option for GsiHofX + std::string test_hofx = options_.test_hofx.value(); + invars_ += Variable("eastward_wind@" + test_hofx); + invars_ += Variable("northward_wind@" + test_hofx); + + // The starting (un-inflated) value of obserror. If running in sequence of filters, + // then it is probably found in ObsErrorData, otherwise, it is probably ObsError. + const std::string errgrp = options_.original_obserr.value(); + invars_ += Variable("eastward_wind@"+errgrp); + + // TODO(gthompsn): Need to include a check that whatever HofX/obserr group name used exists. +} + +// ----------------------------------------------------------------------------- + +SatWindsSPDBCheck::~SatWindsSPDBCheck() {} + +// ----------------------------------------------------------------------------- + +void SatWindsSPDBCheck::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + const size_t nlocs = in.nlocs(); + const float missing = util::missingValue(missing); + float spdb = 0.0f; + float residual = 0.0f; + float obserr = 1.0f; + + // Ensure that only one output variable is expected. + ASSERT(out.nvars() == 1); + + // Get min, max, gross error values + const float error_min = options_.error_min.value(); + const float error_max = options_.error_max.value(); + + // Retrieve SatWinds observations of wind components + std::vector u, v; + in.get(Variable("eastward_wind@ObsValue"), u); + in.get(Variable("northward_wind@ObsValue"), v); + // Retrieve Model HofX wind components + std::string test_hofx = options_.test_hofx.value(); + std::vector um, vm; + in.get(Variable("eastward_wind@" + test_hofx), um); + in.get(Variable("northward_wind@" + test_hofx), vm); + + // Get original ObsError of eastward_wind (would make little sense if diff from northward) + std::vector currentObserr(nlocs); + const std::string errgrp = options_.original_obserr.value(); + in.get(Variable("eastward_wind@"+errgrp), currentObserr); + + for (size_t jj = 0; jj < nlocs; ++jj) { + out[0][jj] = 0.0f; + if (u[jj] != missing && v[jj] != missing) { + spdb = sqrt(u[jj]*u[jj]+v[jj]*v[jj]) - sqrt(um[jj]*um[jj]+vm[jj]*vm[jj]); + if (spdb < 0.0f) { + obserr = currentObserr[jj]; + obserr = std::max(error_min, std::min(obserr, error_max)); + residual = sqrt((u[jj]-um[jj])*(u[jj]-um[jj]) + (v[jj]-vm[jj])*(v[jj]-vm[jj])); + out[0][jj] = residual/obserr; + } + } + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & SatWindsSPDBCheck::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/SatWindsSPDBCheck.h b/src/ufo/filters/obsfunctions/SatWindsSPDBCheck.h new file mode 100644 index 000000000..b058b2579 --- /dev/null +++ b/src/ufo/filters/obsfunctions/SatWindsSPDBCheck.h @@ -0,0 +1,86 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_SATWINDSSPDBCHECK_H_ +#define UFO_FILTERS_OBSFUNCTIONS_SATWINDSSPDBCHECK_H_ + +#include + +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" + +namespace ufo { + +/// +/// \brief Two options are required for this function: error_min and error_max, +/// which are bounding the ObsError variable following a GSI fix file. +/// The specification of group name for ObsError is optional but assigned +/// ObsErrorData by default. The HofX group name is also optional to override +/// using test_hofx for testing purposes (set to GsiHofX, for example). +/// +class SatWindsSPDBCheckParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(SatWindsSPDBCheckParameters, Parameters) + + public: + /// the existence of min,max error values are required + oops::RequiredParameter error_min{"error_min", this}; + oops::RequiredParameter error_max{"error_max", this}; + /// Name of the HofX group used to replace the default (HofX) group + oops::Parameter test_hofx{"test_hofx", "HofX", this}; + /// Name of the ObsError group used to replace the default (ObsErrorData) group + oops::Parameter original_obserr{"original_obserr", "ObsErrorData", this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Compute the speed background difference (SPDB) of observation-model +/// SatWinds. If the SPDB is less than zero, then compute a final value +/// of residual over ObsError. That calculation is then used versus a +/// threshold value in Bounds Check filter to flag obs locations as bad. +/// In regular usage, the test_hofx option would be omitted in order than HofX +/// values are used for model wind components. Also, an optional group +/// name for the ObsError variable can be supplied if different from ObsErrorData, +/// and its value is bounded by parameters error_min and error_max. +/// +/// ~~~ +/// +/// ### Sample YAML configuration +/// - filter: Bounds Check +/// filter variables: +/// - name: eastward_wind +/// - name: northward_wind +/// test variables: +/// - name: SatWindsSPDBCheck@ObsFunction +/// options: +/// error_min: 1.4 +/// error_max: 20.0 +/// maxvalue: 1.75 # gross error * 0.7 +/// +class SatWindsSPDBCheck : public ObsFunctionBase { + public: + static const std::string classname() {return "SatWindsSPDBCheck";} + + explicit SatWindsSPDBCheck(const eckit::LocalConfiguration & + = eckit::LocalConfiguration()); + ~SatWindsSPDBCheck(); + + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + SatWindsSPDBCheckParameters options_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_SATWINDSSPDBCHECK_H_ diff --git a/src/ufo/filters/obsfunctions/SatwindIndivErrors.cc b/src/ufo/filters/obsfunctions/SatwindIndivErrors.cc new file mode 100644 index 000000000..9262b8210 --- /dev/null +++ b/src/ufo/filters/obsfunctions/SatwindIndivErrors.cc @@ -0,0 +1,228 @@ +/* ----------------------------------------------------------------------------- + * (C) British Crown Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * ----------------------------------------------------------------------------- + */ + +#include "ufo/filters/obsfunctions/SatwindIndivErrors.h" + +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/Variable.h" + +#include "eckit/exception/Exceptions.h" +#include "oops/util/Logger.h" + +namespace ufo { + +static ObsFunctionMaker makerSatwindIndivErrors_("SatwindIndivErrors"); + +// ----------------------------------------------------------------------------- + +SatwindIndivErrors::SatwindIndivErrors(const eckit::LocalConfiguration & conf) + : invars_() { + // Check options + options_.deserialize(conf); + + // Get parameters from options. + std::string const profile = options_.profile.value(); + std::string const vcoord = options_.vcoord.value(); + + // Include list of required data from ObsSpace + invars_ += Variable(vcoord+"@MetaData"); + invars_ += Variable("percent_confidence_2@MetaData"); + invars_ += Variable(profile+"@hofx"); + + // Include list of required data from GeoVaLs + invars_ += Variable(vcoord+"@GeoVaLs"); + invars_ += Variable(profile+"@GeoVaLs"); +} + +// ----------------------------------------------------------------------------- + +SatwindIndivErrors::~SatwindIndivErrors() {} + +// ----------------------------------------------------------------------------- +/*! \brief Function to calculate situation dependent observation errors for satwinds + * + * \details The errors are calculated by combining an estimate of the error in + * the vector, with an estimate of the error in vector due to an error in the + * pressure (i.e. height assignment). The latter will depend on the AMV height + * error and the background vertical wind shear. In the future we aim to use + * estimates of the vector error and height error from the data producers. + * + * Currently the vector error estimate \f$E_{vector}\f$ + * is based on the model-independent quality index (QI) and is calculated as: + * \f[ + * E_{vector} = \text{EuMult}\left(QI \times 0.01\right) + \text{EuAdd} + * \f] + * The defaults are EuMult=-5.0 and EuAdd=7.5, which gives errors in the range + * from 2.5 m/s (at QI=100) to 4.5 m/s (at QI=60). + * + * The height error estimate \f$E_{p}\f$ is currently set by a look up table + * (dependent on e.g. satellite, channel, pressure level). + * The values are based on the RMS of model best-fit pressure minus AMV observed + * pressure distributions. These are calculated from several months of data. + * + * The error in vector due to the height error, \f$E_{vpress}\f$, is calculated as: + * \f[ + * E_{vpress} = \sqrt{\frac{\sum{W_{i}\left(v_{i}-v_{n}\right)^{2}}} + * {\sum{W_{i}}} + * } + * \f] + * where + * \f[ + * W_{i} = \exp\left(- \left( \left( p_{i} - p_{n} \right)^2 / 2E_{p}^2 \right)\right) \times dP_{i} + * \f] + * i = model level \n + * N = number of model levels (sum over) \n + * \f$v_i\f$ = wind component on model level \n + * \f$v_n\f$ = wind component at observation location \n + * \f$p_i\f$ = pressure on model level \n + * \f$p_n\f$ = pressure at observation location \n + * \f$E_p\f$ = error in height assignment \n + * \f$dP_i\f$ = layer thickness \n + * + * This is calculated separately for the u and v components giving separate + * u and v component errors. + * \f[ + * E_{total}^2 = E_{vector}^2 + E_{vpress}^2 + * \f] + * + * \author J.Cotton (Met Office) + * + * \date 05/01/2021: Created + */ + +void SatwindIndivErrors::compute(const ObsFilterData & in, + ioda::ObsDataVector & obserr) const { + // Get parameters from options + float const eu_add = options_.eu_add.value(); + float const eu_mult = options_.eu_mult.value(); + float const default_err_p = options_.default_err_p.value(); // Pa + float const min_press = options_.min_press.value().value_or(10000); // Pa + std::string const profile = options_.profile.value(); + std::string const vcoord = options_.vcoord.value(); + oops::Log::debug() << "Wind profile for calculating observation errors is " + << profile << std::endl; + oops::Log::debug() << "Vertical coordinate is " << vcoord << std::endl; + + std::ostringstream errString; + + // check profile name matches one of eastward_wind or northward_wind + if ( profile != "eastward_wind" && profile != "northward_wind" ) { + errString << "Wind component must be one of eastward_wind or northward_wind" << std::endl; + throw eckit::BadValue(errString.str()); + } + + // check vcoord name matches one of air_pressure or air_pressure_levels + if ( vcoord != "air_pressure" && vcoord != "air_pressure_levels" ) { + errString << "Vertical coordinate must be air_pressure or air_pressure_levels" << std::endl; + throw eckit::BadValue(errString.str()); + } + + // Get dimensions + const size_t nlocs = in.nlocs(); + const size_t nlevs = in.nlevs(Variable(profile+"@GeoVaLs")); + + // local variables + float const missing = util::missingValue(missing); + + // Get variables from ObsSpace if present. If not, throw an exception + std::vector ob_p; + std::vector ob_qi; + std::vector bg_windcomponent; + in.get(Variable(vcoord+"@MetaData"), ob_p); + in.get(Variable("percent_confidence_2@MetaData"), ob_qi); + in.get(Variable(profile+"@hofx"), bg_windcomponent); + + // Set pressure error (use default 100 hPa for now) + std::vector err_p(nlocs, default_err_p); + + // Get variables from GeoVals + // Get pressure [Pa] (nlevs, nlocs) + std::vector> cx_p(nlevs, std::vector(nlocs)); + for (size_t ilev = 0; ilev < nlevs; ++ilev) { + in.get(Variable(vcoord+"@GeoVaLs"), ilev + 1, cx_p[ilev]); + } + + // Get wind component (nlevs, nlocs) + std::vector> cx_windcomponent(nlevs, std::vector(nlocs)); + for (size_t ilev = 0; ilev < nlevs; ++ilev) { + in.get(Variable(profile+"@GeoVaLs"), ilev + 1, cx_windcomponent[ilev]); + } + + // Loop through locations + for (size_t iloc=0; iloc < nlocs; ++iloc) { + // Initialize at each location + float error_press = 0.0; // default wind error contribution from error in pressure, ms-1 + float error_vector = 3.5; // default wind error contribution from error in vector, ms-1 + double weight = 0.0; + double sum_top = 0.0; + double sum_weight = 0.0; + obserr[0][iloc] = missing; + + // check for valid pressure error estimate + float const min_err_p = 100; // 1 Pa + if ( err_p[iloc] == missing || err_p[iloc] < min_err_p ) { + errString << "Pressure error estimate invalid: " << err_p[iloc] << " Pa" << std::endl; + throw eckit::BadValue(errString.str()); + } + + // Calculate vector error using QI + if ( (ob_qi[iloc] != missing) && (ob_qi[iloc] > 0.0) ) { + error_vector = eu_mult * (ob_qi[iloc] * 0.01) + eu_add; + } else { + oops::Log::warning() << "No valid QI. Using default vector err" << error_vector << std::endl; + } + + // Calculate the error in vector due to error in pressure + // Start at level number 2 (ilev=1) as need to difference with ilev-1 + for (size_t ilev = 1; ilev < nlevs; ++ilev) { + // ignore contribution above min_press in atmosphere + if (cx_p[ilev][iloc] < min_press) { + continue; + } + // Calculate weight for each background level, avoiding zero divide. + if (err_p[iloc] > 0) { + weight = exp(-0.5 * pow(cx_p[ilev][iloc] - ob_p[iloc], 2) / + pow(err_p[iloc], 2) ) + * std::abs(cx_p[ilev][iloc] - cx_p[ilev - 1][iloc]); + } else { + weight = 0.0; + } + + // ignore level if weight is very small + double const min_weight = 0.00001; + if (weight < min_weight) { + continue; + } + + sum_top = sum_top + weight * pow(cx_windcomponent[ilev][iloc] - bg_windcomponent[iloc], 2); + sum_weight = sum_weight + weight; + } + + if (sum_weight > 0) { + error_press = sqrt(sum_top / sum_weight); + } + + obserr[0][iloc] = sqrt(pow(error_vector, 2) + + pow(error_press, 2) ); + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & SatwindIndivErrors::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/SatwindIndivErrors.h b/src/ufo/filters/obsfunctions/SatwindIndivErrors.h new file mode 100644 index 000000000..b9642ce85 --- /dev/null +++ b/src/ufo/filters/obsfunctions/SatwindIndivErrors.h @@ -0,0 +1,72 @@ +/* ----------------------------------------------------------------------------- + * (C) British Crown Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * ----------------------------------------------------------------------------- + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_SATWINDINDIVERRORS_H_ +#define UFO_FILTERS_OBSFUNCTIONS_SATWINDINDIVERRORS_H_ + +#include +#include + +#include "ioda/ObsDataVector.h" + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +/// \brief Options controlling SatwindIndivErrors ObsFunction +class SatwindIndivErrorsParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(SatwindIndivErrorsParameters, Parameters) + + public: + /// Vector error estimate addition + oops::RequiredParameter eu_add{"verror add", this}; + /// Vector error estimate multiply + oops::RequiredParameter eu_mult{"verror mult", this}; + /// Profile we are calculating error for + oops::RequiredParameter profile{"wind component", this}; + /// vertical coordinate to use + oops::RequiredParameter vcoord{"vertical coordinate", this}; + /// default pressure error (Pa) + oops::RequiredParameter default_err_p{"default pressure error", this}; + /// ignore contribution above height of minimum pressure (Pa) + oops::OptionalParameter min_press{"minimum pressure", this}; +}; + +// ----------------------------------------------------------------------------- + +/// +/// \brief Function calculates individual observation errors for Satwind u and v winds +/// dependent on an input height (pressure) error estimate and the wind shear. +/// + +class SatwindIndivErrors : public ObsFunctionBase { + public: + explicit SatwindIndivErrors(const eckit::LocalConfiguration &); + ~SatwindIndivErrors(); + + void compute(const ObsFilterData &, + ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + SatwindIndivErrorsParameters options_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_SATWINDINDIVERRORS_H_ diff --git a/src/ufo/filters/obsfunctions/SunGlintAngle.cc b/src/ufo/filters/obsfunctions/SunGlintAngle.cc new file mode 100644 index 000000000..035fa47df --- /dev/null +++ b/src/ufo/filters/obsfunctions/SunGlintAngle.cc @@ -0,0 +1,72 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/SunGlintAngle.h" + +#include +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/Constants.h" + +namespace ufo { + +static ObsFunctionMaker makerSunGlintAngle_("SunGlintAngle"); + +SunGlintAngle::SunGlintAngle(const eckit::LocalConfiguration & conf) + : invars_() { + // Include list of required data from ObsSpace + invars_ += Variable("solar_zenith_angle@MetaData"); + invars_ += Variable("solar_azimuth_angle@MetaData"); + invars_ += Variable("sensor_zenith_angle@MetaData"); + invars_ += Variable("sensor_azimuth_angle@MetaData"); +} + +// ----------------------------------------------------------------------------- + +SunGlintAngle::~SunGlintAngle() {} + +// ----------------------------------------------------------------------------- + +void SunGlintAngle::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + // Get dimension + const size_t nlocs = in.nlocs(); + + // This is taken from AMSR2's Sun_glint_angle calculation in GSI's subroutine "setuprad.f90". + std::vector &sun_glint = out[0]; + std::vector sun_zenith(nlocs), sun_azimuth(nlocs); + std::vector sat_zenith(nlocs), sat_azimuth(nlocs); + + in.get(Variable("solar_zenith_angle@MetaData"), sun_zenith); + in.get(Variable("solar_azimuth_angle@MetaData"), sun_azimuth); + in.get(Variable("sensor_zenith_angle@MetaData"), sat_zenith); + in.get(Variable("sensor_azimuth_angle@MetaData"), sat_azimuth); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + sun_zenith[iloc] *= Constants::deg2rad; + sun_azimuth[iloc] *= Constants::deg2rad; + sat_zenith[iloc] *= Constants::deg2rad; + sat_azimuth[iloc] *= Constants::deg2rad; + float cosza = cos(sat_zenith[iloc]); + float bearaz = sun_azimuth[iloc] - sat_azimuth[iloc] + M_PI; + sun_glint[iloc] = acos(cos(sun_zenith[iloc])*cosza + sin(sun_zenith[iloc]) + *sin(sat_zenith[iloc])*cos(bearaz))*Constants::rad2deg; + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & SunGlintAngle::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/SunGlintAngle.h b/src/ufo/filters/obsfunctions/SunGlintAngle.h new file mode 100644 index 000000000..f4c29d357 --- /dev/null +++ b/src/ufo/filters/obsfunctions/SunGlintAngle.h @@ -0,0 +1,40 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_SUNGLINTANGLE_H_ +#define UFO_FILTERS_OBSFUNCTIONS_SUNGLINTANGLE_H_ + +#include +#include + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" + +namespace ufo { + +/// +/// \brief Calculate Sun glint angles at observation locations. +/// +class SunGlintAngle : public ObsFunctionBase { + public: + explicit SunGlintAngle(const eckit::LocalConfiguration & + = eckit::LocalConfiguration()); + ~SunGlintAngle(); + + void compute(const ObsFilterData &, + ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_SUNGLINTANGLE_H_ diff --git a/src/ufo/filters/obsfunctions/SymmCldImpactIR.cc b/src/ufo/filters/obsfunctions/SymmCldImpactIR.cc index 8922cc295..65970b7e3 100755 --- a/src/ufo/filters/obsfunctions/SymmCldImpactIR.cc +++ b/src/ufo/filters/obsfunctions/SymmCldImpactIR.cc @@ -77,8 +77,9 @@ void SymmCldImpactIR::compute(const ObsFilterData & in, // TODO(JJG): change CRTM clear-sky behavior if (clr[iloc] > -1.0f && clr[iloc] < 1.0f) clr[iloc] = bak[iloc]; - Cmod = abs(clr[iloc] - bak[iloc]); - Cobs = abs(clr[iloc] - obs[iloc] + bias[iloc]); + // HofX contains bias correction; subtracting it here + Cmod = std::abs(clr[iloc] - bak[iloc] + bias[iloc]); + Cobs = std::abs(clr[iloc] - obs[iloc] + bias[iloc]); SCI[ich][iloc] = 0.5f * (Cmod + Cobs); } else { SCI[ich][iloc] = missing; diff --git a/src/ufo/filters/obsfunctions/TotalColumnVaporGuess.cc b/src/ufo/filters/obsfunctions/TotalColumnVaporGuess.cc new file mode 100644 index 000000000..035e8a3d7 --- /dev/null +++ b/src/ufo/filters/obsfunctions/TotalColumnVaporGuess.cc @@ -0,0 +1,75 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/TotalColumnVaporGuess.h" + +#include +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/Constants.h" + +namespace ufo { + +static ObsFunctionMaker makerTotalColumnVaporGuess_("TotalColumnVaporGuess"); + +TotalColumnVaporGuess::TotalColumnVaporGuess(const eckit::LocalConfiguration & conf) + : invars_() { + // Include list of required data from GeoVaLs + invars_ += Variable("humidity_mixing_ratio@GeoVaLs"); + invars_ += Variable("air_pressure_levels@GeoVaLs"); +} + +// ----------------------------------------------------------------------------- + +TotalColumnVaporGuess::~TotalColumnVaporGuess() {} + +// ----------------------------------------------------------------------------- + +void TotalColumnVaporGuess::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + // Get dimension + const size_t nlocs = in.nlocs(); + const size_t nlevs = in.nlevs(Variable("air_pressure_levels@GeoVaLs")); + const float GK = 1.0/Constants::grav; + + // column q (kg/m^2) = sum( pressure_thickness * (q_mixrati/(1 + q_mixrati)) / grav) + std::vector q_mixrati(nlocs); + std::vector tcwv(nlocs, 0.0); + std::vector pre_lev0(nlocs), pre_levl(nlocs); + + in.get(Variable("air_pressure_levels@GeoVaLs"), 1, pre_lev0); + for (size_t ilev = 1; ilev < nlevs; ++ilev) { + int ilevp1 = ilev + 1; + in.get(Variable("air_pressure_levels@GeoVaLs"), ilevp1, pre_levl); + in.get(Variable("humidity_mixing_ratio@GeoVaLs"), ilev, q_mixrati); + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + // Change the unit of q_mixing g/kg => kg/kg. + q_mixrati[iloc] *= 0.001; + tcwv[iloc] = tcwv[iloc] + q_mixrati[iloc]/(q_mixrati[iloc]+1) + * fabs(pre_levl[iloc] - pre_lev0[iloc]); + } + pre_lev0 = pre_levl; + } + for (size_t iloc = 0; iloc < nlocs; ++iloc) { + tcwv[iloc] *= GK; + } + out[0] = tcwv; +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & TotalColumnVaporGuess::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/TotalColumnVaporGuess.h b/src/ufo/filters/obsfunctions/TotalColumnVaporGuess.h new file mode 100644 index 000000000..74f6a9927 --- /dev/null +++ b/src/ufo/filters/obsfunctions/TotalColumnVaporGuess.h @@ -0,0 +1,44 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_TOTALCOLUMNVAPORGUESS_H_ +#define UFO_FILTERS_OBSFUNCTIONS_TOTALCOLUMNVAPORGUESS_H_ + +#include +#include + +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" + +namespace ufo { + +/// +/// \brief Calculate column water vapor mass in guess at observation locations. +/// +class TotalColumnVaporGuess : public ObsFunctionBase { + public: + explicit TotalColumnVaporGuess(const eckit::LocalConfiguration & + = eckit::LocalConfiguration()); + ~TotalColumnVaporGuess(); + + void compute(const ObsFilterData &, + ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_TOTALCOLUMNVAPORGUESS_H_ diff --git a/src/ufo/filters/obsfunctions/TropopauseEstimate.cc b/src/ufo/filters/obsfunctions/TropopauseEstimate.cc new file mode 100644 index 000000000..9783f7e73 --- /dev/null +++ b/src/ufo/filters/obsfunctions/TropopauseEstimate.cc @@ -0,0 +1,117 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/TropopauseEstimate.h" + +#include +#include +#include +#include +#include + +#include "eckit/exception/Exceptions.h" + +#include "oops/util/DateTime.h" +#include "oops/util/missingValues.h" + +#include "ufo/filters/Variable.h" + +namespace ufo { + +static ObsFunctionMaker makerObsFuncTropopauseEstimate_("TropopauseEstimate"); + +// ----------------------------------------------------------------------------- + +TropopauseEstimate::TropopauseEstimate(const eckit::LocalConfiguration & conf) + : invars_() { + oops::Log::debug() << "TropopauseEstimate: config = " << conf << std::endl; + // Initialize options + options_.deserialize(conf); + + // We must know the datetime of each observation + invars_ += Variable("datetime@MetaData"); + // We must know the latitude of each observation + invars_ += Variable("latitude@MetaData"); + + if (options_.convert_p2z.value()) + oops::Log::debug() << " TropopauseEstimate: will convert pres to height" << std::endl; +} + +// ----------------------------------------------------------------------------- + +TropopauseEstimate::~TropopauseEstimate() {} + +// ----------------------------------------------------------------------------- + +void TropopauseEstimate::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + const size_t nlocs = in.nlocs(); + const float missing = util::missingValue(missing); + + // Ensure that only one output variable is expected. + ASSERT(out.nvars() == 1); + + // Retrieve the options of tropo_equator, tropo_pole, convert_p2z. + const float tropo_equator = options_.tropo_equator.value(); + const float tropo_pole = options_.tropo_pole.value(); + bool convert_p2z = options_.convert_p2z.value(); + + // Retrieve the datetime and latitude. + std::vector latitude; + in.get(Variable("latitude@MetaData"), latitude); + std::vector datetimes; + in.get(Variable("datetime@MetaData"), datetimes); + + // If datetimes is empty, then we should just exit because there is nothing we can do otherwise. + if (datetimes.empty()) { + return; + } + + int year, month, day, hour, minute, second, day_peak; + float slope, tropo_start, season_factor; + std::vector answer(nlocs); + + // Taking a small short-cut. Rather than loop through all datetimes, we + // get only the first one to compute the day of the year. + datetimes[0].toYYYYMMDDhhmmss(year, month, day, hour, minute, second); + const util::DateTime firstDayOfYear(year, 1, 1, 0, 0, 0); + const int day_of_year = (datetimes[0] - firstDayOfYear).toSeconds() / (24*3600); + + // Iterate over the observation points. + for (size_t jj = 0; jj < nlocs; ++jj) { + if (latitude[jj] > 0.0) { + day_peak = 203; + } else { + day_peak = 21; + } + slope = std::max(0.0, (std::abs(latitude[jj])-15.0)/(90.0-15.0)); + tropo_start = (tropo_pole-tropo_equator)*slope + tropo_equator; + season_factor = std::cos((day_of_year - day_peak)*0.5f*0.0174533f); + answer[jj] = tropo_start + season_factor*5000.0; + + // If needed, convert pressure to height. + if (convert_p2z) answer[jj] = 44307.692*(1.0 - pow(answer[jj]/101325.0f, 0.19f)); + + out[0][jj] = answer[jj]; + } + + // TODO(gthompsn): Need to add units or other attributes in the future. + // varname = "TropopausePressure"; units = "Pa"; or height and meters. + if (options_.save) { + out.save("DerivedValue"); + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & TropopauseEstimate::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/TropopauseEstimate.h b/src/ufo/filters/obsfunctions/TropopauseEstimate.h new file mode 100644 index 000000000..0108d7e27 --- /dev/null +++ b/src/ufo/filters/obsfunctions/TropopauseEstimate.h @@ -0,0 +1,90 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_TROPOPAUSEESTIMATE_H_ +#define UFO_FILTERS_OBSFUNCTIONS_TROPOPAUSEESTIMATE_H_ + +#include +#include + +#include "ioda/ObsDataVector.h" + +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +namespace ufo { + +/// +/// \brief Two optional parameters permit overriding default tropopause pressure at the +/// equator and poles (linear interp between). The optional save (default=false) +/// option can be used to save calculated tropopause estimate to the output file. +/// By default (convert_p2z=false), the output is tropopause pressure, but this +/// optional argument can convert the answer from pressure to height using the ICAO +/// standard atmosphere approximation. +/// +class TropopauseEstimateParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(TropopauseEstimateParameters, Parameters) + + public: + /// Default: The tropopause near the equator is located at a constant pressure level (135 hPa) + /// in a belt from 15 S to 15 N and increases linearly to the poles to 360 hPa. + oops::Parameter tropo_equator{"tropo_equator", 13500.0f, this}; + oops::Parameter tropo_pole{"tropo_pole", 36000.0f, this}; + oops::Parameter convert_p2z{"convert_p2z", false, this}; + oops::Parameter save{"save", false, this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Create a first-guess estimate of the tropopause pressure that is based +/// on latitude with some adjustment for day-of-year. An optional parameter +/// can convert the final answer from pressure to height (convert_p2z: true). +/// The conversion is ultra simple approximation of ICAO std. atmosphere because +/// the code is making a tropopause *estimate* only. Additional options can +/// alter the default tropopause pressure at equator (135 hPa) and poles (360 hPa). +/// The code in this method is crude and purely designed for estimating the tropopause +/// when lacking a model-derived estimate that may otherwise arrive via GeoVaLs. +/// +/// ~~~ +/// +/// ### Sample YAML configuration +/// - filter: Difference Check +/// filter variables: +/// - name: eastward_wind +/// - name: northward_wind +/// reference: TropopauseEstimate@ObsFunction +/// # options: # These options will not work yet with Difference Check +/// # - tropo_equator: 13000 # 130 hPa +/// # - tropo_pole: 37000 # 370 hPa +/// value: air_pressure@MetaData +/// minvalue: -5000 # 50 hPa above tropopause level, negative p-diff +/// +class TropopauseEstimate : public ObsFunctionBase { + public: + static const std::string classname() {return "TropopauseEstimate";} + + explicit TropopauseEstimate(const eckit::LocalConfiguration & + = eckit::LocalConfiguration()); + ~TropopauseEstimate(); + + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + TropopauseEstimateParameters options_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_TROPOPAUSEESTIMATE_H_ diff --git a/src/ufo/filters/obsfunctions/WindDirAngleDiff.cc b/src/ufo/filters/obsfunctions/WindDirAngleDiff.cc new file mode 100644 index 000000000..0372d4963 --- /dev/null +++ b/src/ufo/filters/obsfunctions/WindDirAngleDiff.cc @@ -0,0 +1,99 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/obsfunctions/WindDirAngleDiff.h" + +#include +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/Constants.h" + +namespace ufo { + +static ObsFunctionMaker makerObsFuncWindDirAngleDiff_("WindDirAngleDiff"); + +// ----------------------------------------------------------------------------- + +WindDirAngleDiff::WindDirAngleDiff(const eckit::LocalConfiguration & conf) + : invars_() { + oops::Log::debug() << "WindDirAngleDiff: config = " << conf << std::endl; + // Initialize options + options_.deserialize(conf); + + // We need to retrieve the observed wind components. + invars_ += Variable("eastward_wind@ObsValue"); + invars_ += Variable("northward_wind@ObsValue"); + + // Typical use would be HofX group, but during testing, we include option for GsiHofX + std::string test_hofx = options_.test_hofx.value(); + invars_ += Variable("eastward_wind@" + test_hofx); + invars_ += Variable("northward_wind@" + test_hofx); + + // TODO(gthompsn): Need to include a check that whatever HofX group name used actually exists. +} + +// ----------------------------------------------------------------------------- + +WindDirAngleDiff::~WindDirAngleDiff() {} + +// ----------------------------------------------------------------------------- + +void WindDirAngleDiff::compute(const ObsFilterData & in, + ioda::ObsDataVector & out) const { + const size_t nlocs = in.nlocs(); + const float missing = util::missingValue(missing); + const double deg = Constants::rad2deg; + + // Ensure that only one output variable is expected. + ASSERT(out.nvars() == 1); + + // Retrieve minimum_uv value and assure it is sensible. + const float min_uv = std::max(0.0001f, options_.minimum_uv.value()); + + // Retrieve observations of wind components + std::vector u, v; + in.get(Variable("eastward_wind@ObsValue"), u); + in.get(Variable("northward_wind@ObsValue"), v); + // Retrieve Model HofX wind components + std::string test_hofx = options_.test_hofx.value(); + std::vector um, vm; + in.get(Variable("eastward_wind@" + test_hofx), um); + in.get(Variable("northward_wind@" + test_hofx), vm); + + double wdir_obs, wdir_model; + + for (size_t jj = 0; jj < nlocs; ++jj) { + if (u[jj] != missing && v[jj] != missing) { + if (std::abs(u[jj]) < min_uv && std::abs(v[jj]) < min_uv) { + out[0][jj] = 0.0; + } else { + wdir_obs = std::atan2(-u[jj], -v[jj])*deg; + wdir_model = std::atan2(-um[jj], -vm[jj])*deg; + out[0][jj] = std::min({std::abs(wdir_obs-wdir_model), + std::abs(wdir_obs-wdir_model+360.0), + std::abs(wdir_obs-wdir_model-360.0)}); + } + } else { + out[0][jj] = missing; + } + } +} + +// ----------------------------------------------------------------------------- + +const ufo::Variables & WindDirAngleDiff::requiredVariables() const { + return invars_; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/obsfunctions/WindDirAngleDiff.h b/src/ufo/filters/obsfunctions/WindDirAngleDiff.h new file mode 100644 index 000000000..84780a82a --- /dev/null +++ b/src/ufo/filters/obsfunctions/WindDirAngleDiff.h @@ -0,0 +1,73 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_OBSFUNCTIONS_WINDDIRANGLEDIFF_H_ +#define UFO_FILTERS_OBSFUNCTIONS_WINDDIRANGLEDIFF_H_ + +#include + +#include "oops/util/parameters/Parameter.h" + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/obsfunctions/ObsFunctionBase.h" +#include "ufo/filters/Variables.h" + +namespace ufo { + +/// +/// \brief An optional parameter to override the source of HofX wind components, +/// and an optional parameter for minimum wind components (default=0.5 m/s). +/// +class WindDirAngleDiffParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(WindDirAngleDiffParameters, Parameters) + + public: + /// Name of the HofX group used to replace the default group (default is HofX) + oops::Parameter test_hofx{"test_hofx", "HofX", this}; + oops::Parameter minimum_uv{"minimum_uv", 0.5, this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Compute the wind direction angle difference between observation and model. +/// For application in SatWinds QC, a difference larger than 50 degrees is +/// typically used to reject data. This threshold can be used as the max_value +/// in a Bounds Check filter. +/// +/// ~~~ +/// +/// ### Sample YAML configuration +/// - filter: Bounds Check +/// filter variables: +/// - name: eastward_wind +/// - name: northward_wind +/// test variables: +/// - name: WindDirAngleDiff@ObsFunction +/// options: +/// test_hofx: GsiHofX +/// maxvalue: 50 +/// +class WindDirAngleDiff : public ObsFunctionBase { + public: + static const std::string classname() {return "WindDirAngleDiff";} + + explicit WindDirAngleDiff(const eckit::LocalConfiguration & + = eckit::LocalConfiguration()); + ~WindDirAngleDiff(); + + void compute(const ObsFilterData &, ioda::ObsDataVector &) const; + const ufo::Variables & requiredVariables() const; + private: + ufo::Variables invars_; + WindDirAngleDiffParameters options_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_FILTERS_OBSFUNCTIONS_WINDDIRANGLEDIFF_H_ diff --git a/src/ufo/filters/processWhere.cc b/src/ufo/filters/processWhere.cc index 367c3757f..41014b757 100644 --- a/src/ufo/filters/processWhere.cc +++ b/src/ufo/filters/processWhere.cc @@ -8,15 +8,17 @@ #include "ufo/filters/processWhere.h" #include +#include #include #include #include #include "eckit/config/LocalConfiguration.h" +#include "eckit/types/FloatCompare.h" #include "oops/util/IntSetParser.h" #include "oops/util/Logger.h" #include "oops/util/missingValues.h" -#include "oops/util/PartialDateTime.h" +#include "oops/util/wildcard.h" #include "ufo/filters/ObsFilterData.h" #include "ufo/filters/Variables.h" @@ -24,23 +26,21 @@ namespace ufo { // ----------------------------------------------------------------------------- -ufo::Variables getAllWhereVariables(const eckit::Configuration & config) { - std::vector masks; - config.get("where", masks); - +ufo::Variables getAllWhereVariables(const std::vector & params) { ufo::Variables vars; - for (size_t jm = 0; jm < masks.size(); ++jm) { - eckit::LocalConfiguration varconf(masks[jm], "variable"); - vars += ufo::Variable(varconf); + for (const WhereParameters & currentParams : params) { + vars += currentParams.variable; } return vars; } + // ----------------------------------------------------------------------------- -void processWhereMinMax(const std::vector & data, - const float & vmin, const float & vmax, +template +void processWhereMinMax(const std::vector & data, + const T & vmin, const T & vmax, std::vector & mask) { - const float not_set_value = util::missingValue(not_set_value); + const T not_set_value = util::missingValue(not_set_value); const size_t n = data.size(); if (vmin != not_set_value || vmax != not_set_value) { @@ -54,16 +54,14 @@ void processWhereMinMax(const std::vector & data, // ----------------------------------------------------------------------------- void processWhereMinMax(const std::vector & data, - const std::string & vmin, const std::string & vmax, + const util::PartialDateTime & vmin, const util::PartialDateTime & vmax, std::vector & mask) { - const std::string not_set_value = "0000-00-00T00:00:00Z"; + const util::PartialDateTime not_set_value {}; if (vmin != not_set_value || vmax != not_set_value) { - util::PartialDateTime pdt_vmin(vmin), pdt_vmax(vmax); - for (size_t jj = 0; jj < data.size(); ++jj) { - if (vmin != not_set_value && pdt_vmin > data[jj]) mask[jj] = false; - if (vmax != not_set_value && pdt_vmax < data[jj]) mask[jj] = false; + if (vmin != not_set_value && vmin > data[jj]) mask[jj] = false; + if (vmax != not_set_value && vmax < data[jj]) mask[jj] = false; } } } @@ -100,6 +98,31 @@ void processWhereIsIn(const std::vector & data, } } +// ----------------------------------------------------------------------------- +void processWhereIsClose(const std::vector & data, + const float tolerance, const bool relative, + const std::vector & whitelist, + std::vector & mask) { + for (size_t jj = 0; jj < data.size(); ++jj) { + bool inlist = false; + for (auto testvalue : whitelist) { + if (relative) { + float relativetolerance = testvalue * tolerance; + if (eckit::types::is_approximately_equal(data[jj], testvalue, relativetolerance)) { + inlist = true; + break; + } + } else { + if (eckit::types::is_approximately_equal(data[jj], testvalue, tolerance)) { + inlist = true; + break; + } + } + } // testvalue + if (!inlist) mask[jj] = false; + } // jj +} + // ----------------------------------------------------------------------------- template void processWhereIsNotIn(const std::vector & data, @@ -121,24 +144,63 @@ void processWhereIsNotIn(const std::vector & data, } // ----------------------------------------------------------------------------- -void applyMinMaxFloat(std::vector & where, eckit::LocalConfiguration const & mask, - ObsFilterData const & filterdata, Variable const & varname) { - const float not_set_value = util::missingValue(not_set_value); - const float vmin = mask.getFloat("minvalue", not_set_value); - const float vmax = mask.getFloat("maxvalue", not_set_value); +void processWhereIsNotClose(const std::vector & data, + const float tolerance, const bool relative, + const std::vector & blacklist, + std::vector & mask) { + const float missing = util::missingValue(missing); + for (size_t jj = 0; jj < data.size(); ++jj) { + for (auto testvalue : blacklist) { + if (relative) { + float relativetolerance = testvalue * tolerance; + if (data[jj] == missing || + eckit::types::is_approximately_equal(data[jj], testvalue, relativetolerance)) { + mask[jj] = false; + break; + } + } else { + if (data[jj] == missing || + eckit::types::is_approximately_equal(data[jj], testvalue, tolerance)) { + mask[jj] = false; + break; + } + } + } // testvalue + } // jj +} + +// ----------------------------------------------------------------------------- +template +void applyMinMax(std::vector & where, WhereParameters const & parameters, + ObsFilterData const & filterdata, Variable const & varname) { + const T not_set_value = util::missingValue(not_set_value); + + // Set vmin to the value of the 'minvalue' option if it exists; if not, leave vmin unchanged. + T vmin = not_set_value; + if (parameters.minvalue.value() != boost::none) + vmin = parameters.minvalue.value()->as(); + // Set vmax to the value of the 'maxvalue' option if it exists; if not, leave vmax unchanged. + T vmax = not_set_value; + if (parameters.maxvalue.value() != boost::none) + vmax = parameters.maxvalue.value()->as(); + // Apply mask min/max if (vmin != not_set_value || vmax != not_set_value) { - std::vector data; + std::vector data; filterdata.get(varname, data); processWhereMinMax(data, vmin, vmax, where); } } -void applyMinMaxDatetime(std::vector & where, eckit::LocalConfiguration const & mask, - ObsFilterData const & filterdata, Variable const & varname) { - const std::string not_set_value("0000-00-00T00:00:00Z"); - const std::string vmin = mask.getString("minvalue", not_set_value); - const std::string vmax = mask.getString("maxvalue", not_set_value); +// ----------------------------------------------------------------------------- +template <> +void applyMinMax(std::vector & where, WhereParameters const & parameters, + ObsFilterData const & filterdata, Variable const & varname) { + util::PartialDateTime vmin {}, vmax {}, not_set_value {}; + if (parameters.minvalue.value() != boost::none) + vmin = parameters.minvalue.value()->as(); + if (parameters.maxvalue.value() != boost::none) + vmax = parameters.maxvalue.value()->as(); // Apply mask min/max if (vmin != not_set_value || vmax != not_set_value) { @@ -149,83 +211,193 @@ void applyMinMaxDatetime(std::vector & where, eckit::LocalConfiguration co } // ----------------------------------------------------------------------------- -void processWhereBitSet(const std::vector & data, - const std::set & flags, - std::vector & mask) { - std::bitset<32> flags_bs; - for (const int &elem : flags) { - flags_bs[elem] = 1; +/// \brief Process an `any_bit_set_of` keyword in a `where` clause. +/// +/// This function sets to `false` all elements of `where` corresponding to elements of `data` in +/// which all bits with indices `bitIndices` are zero. Bits are numbered from 0 starting from the +/// least significant bit. +/// +/// The vectors `data` and `where` must be of the same length. +/// +/// Example: Suppose `data` is set to [1, 3, 4, 8] and bitIndices to [0, 2]. Then this function will +/// set only the last element of `where` to false, since 8 is the only integer from `data` in whose +/// binary representation both bits 0 and 2 are zero. +void processWhereAnyBitSetOf(const std::vector & data, + const std::set & bitIndices, + std::vector & where) { + std::bitset<32> mask_bs; + for (const int &bitIndex : bitIndices) { + mask_bs[bitIndex] = 1; + } + const int mask = mask_bs.to_ulong(); + + for (size_t jj = 0; jj < data.size(); ++jj) { + if ((data[jj] & mask) == 0) { + // None of the specified bits is set + where[jj] = false; + } } +} + +// ----------------------------------------------------------------------------- +/// \brief Process an `any_bit_unset_of` keyword in a `where` clause. +/// +/// This function sets to `false` all elements of `where` corresponding to elements of `data` in +/// which all bits with indices `bitIndices` are non-zero. Bits are numbered from 0 starting from +/// the least significant bit. +/// +/// The vectors `data` and `where` must be of the same length. +/// +/// Example: Suppose `data` is set to [1, 3, 4, 5] and bitIndices to [0, 2]. Then this function will +/// set only the last element of `where` to false, since 5 is the only integer from `data` in whose +/// binary representation both bits 0 and 2 are non-zero. +void processWhereAnyBitUnsetOf(const std::vector & data, + const std::set & bitIndices, + std::vector & where) { + std::bitset<32> mask_bs; + for (const int &bitIndex : bitIndices) { + mask_bs[bitIndex] = 1; + } + const int mask = mask_bs.to_ulong(); + + for (size_t jj = 0; jj < data.size(); ++jj) { + if ((data[jj] & mask) == mask) { + // None of the specified bits is unset + where[jj] = false; + } + } +} + +// ----------------------------------------------------------------------------- +/// \brief Process a `matches_regex` keyword in a `where` clause. +/// +/// This function sets to `false` all elements of `where` corresponding to elements of `data` that +/// do not match the regular expression `pattern`. The vectors `data` and `where` must be of the +/// same length. +void processWhereMatchesRegex(const std::vector & data, + const std::string & pattern, + std::vector & where) { + std::regex regex(pattern); + for (size_t jj = 0; jj < data.size(); ++jj) { + if (where[jj] && !std::regex_match(data[jj], regex)) + where[jj] = false; + } +} + +/// \brief Process a `matches_regex` keyword in a `where` clause. +/// +/// This function sets to `false` all elements of `where` corresponding to elements of `data` whose +/// string representations do not match the regular expression `pattern`. The vectors `data` and +/// `where` must be of the same length. +void processWhereMatchesRegex(const std::vector & data, + const std::string & pattern, + std::vector & where) { + std::regex regex(pattern); + for (size_t jj = 0; jj < data.size(); ++jj) { + if (where[jj] && !std::regex_match(std::to_string(data[jj]), regex)) + where[jj] = false; + } +} + +// ----------------------------------------------------------------------------- +/// Returns true if `string` matches any of the patterns from the list `patterns`. +/// +/// The patterns may contain wildcards `*` (matching any sequence of characters) and `?` (matching +/// a single character). +bool stringMatchesAnyWildcardPattern(const std::string &string, + const std::vector & patterns) { + return std::any_of(patterns.begin(), + patterns.end(), + [&string] (const std::string &pattern) + { return util::matchesWildcardPattern(string, pattern); }); +} + +/// \brief Function used to process a `matches_wildcard` or `matches_any_wildcard` keyword in a +/// `where` clause. +/// +/// This function sets to `false` all elements of `where` corresponding to elements of `data` that +/// do not match any of the patterns from the list `patterns`. The patterns may contain wildcards +/// `*` (matching any sequence of characters) and `?` (matching a single character). The vectors +/// `data` and `where` must be of the same length. +void processWhereMatchesAnyWildcardPattern(const std::vector & data, + const std::vector & patterns, + std::vector & where) { for (size_t jj = 0; jj < data.size(); ++jj) { - if ((data[jj] & flags_bs.to_ulong()) != 0) mask[jj] = false; + if (where[jj] && !stringMatchesAnyWildcardPattern(data[jj], patterns)) + where[jj] = false; + } +} + +/// \overload Same as the function above, but taking a vector of integers rather than strings. +/// The integers are converted to strings before pattern matching. +void processWhereMatchesAnyWildcardPattern(const std::vector & data, + const std::vector & patterns, + std::vector & where) { + for (size_t jj = 0; jj < data.size(); ++jj) { + if (where[jj] && !stringMatchesAnyWildcardPattern(std::to_string(data[jj]), patterns)) + where[jj] = false; } } // ----------------------------------------------------------------------------- -void isInString(std::vector & where, eckit::LocalConfiguration const & mask, +void isInString(std::vector & where, std::vector const & allowedValues, ObsFilterData const & filterdata, Variable const & varname) { std::vector data; - std::vector whitelistvec = mask.getStringVector("is_in"); - std::set whitelist(whitelistvec.begin(), whitelistvec.end()); + std::set whitelist(allowedValues.begin(), allowedValues.end()); filterdata.get(varname, data); processWhereIsIn(data, whitelist, where); } // ----------------------------------------------------------------------------- -void isInInteger(std::vector & where, eckit::LocalConfiguration const & mask, +void isInInteger(std::vector & where, std::set const & allowedValues, ObsFilterData const & filterdata, Variable const & varname) { std::vector data; - std::set whitelist = oops::parseIntSet(mask.getString("is_in")); filterdata.get(varname, data); - processWhereIsIn(data, whitelist, where); + processWhereIsIn(data, allowedValues, where); } // ----------------------------------------------------------------------------- -void isNotInString(std::vector & where, eckit::LocalConfiguration const & mask, +void isNotInString(std::vector & where, std::vector const & forbiddenValues, ObsFilterData const & filterdata, Variable const & varname) { std::vector data; - std::vector blacklistvec = mask.getStringVector("is_not_in"); - std::set blacklist(blacklistvec.begin(), blacklistvec.end()); + std::set blacklist(forbiddenValues.begin(), forbiddenValues.end()); filterdata.get(varname, data); processWhereIsNotIn(data, blacklist, where); } // ----------------------------------------------------------------------------- -void isNotInInteger(std::vector & where, eckit::LocalConfiguration const & mask, +void isNotInInteger(std::vector & where, std::set const & forbiddenValues, ObsFilterData const & filterdata, Variable const & varname) { std::vector data; filterdata.get(varname, data); - std::set blacklist = oops::parseIntSet(mask.getString("is_not_in")); - processWhereIsNotIn(data, blacklist, where); + processWhereIsNotIn(data, forbiddenValues, where); } // ----------------------------------------------------------------------------- -std::vector processWhere(const eckit::Configuration & config, +std::vector processWhere(const std::vector & params, const ObsFilterData & filterdata) { const size_t nlocs = filterdata.nlocs(); // Everywhere by default if no mask std::vector where(nlocs, true); - std::vector masks; - config.get("where", masks); - - for (size_t jm = 0; jm < masks.size(); ++jm) { - eckit::LocalConfiguration varconf(masks[jm], "variable"); - Variable var(varconf); + for (const WhereParameters ¤tParams : params) { + const Variable &var = currentParams.variable; for (size_t jvar = 0; jvar < var.size(); ++jvar) { if (var.group() != "VarMetaData") { const Variable varname = var[jvar]; ioda::ObsDtype dtype = filterdata.dtype(varname); if (dtype == ioda::ObsDtype::DateTime) { - applyMinMaxDatetime(where, masks[jm], filterdata, varname); + applyMinMax(where, currentParams, filterdata, varname); + } else if (dtype == ioda::ObsDtype::Integer) { + applyMinMax(where, currentParams, filterdata, varname); } else { - applyMinMaxFloat(where, masks[jm], filterdata, varname); + applyMinMax(where, currentParams, filterdata, varname); } // Apply mask is_defined - if (masks[jm].has("is_defined")) { + if (currentParams.isDefined.value()) { if (filterdata.has(varname)) { std::vector data; filterdata.get(varname, data); @@ -236,18 +408,20 @@ std::vector processWhere(const eckit::Configuration & config, } // Apply mask is_not_defined - if (masks[jm].has("is_not_defined")) { + if (currentParams.isNotDefined.value()) { std::vector data; filterdata.get(varname, data); processWhereIsNotDefined(data, where); } // Apply mask is_in - if (masks[jm].has("is_in")) { + if (currentParams.isIn.value() != boost::none) { if (dtype == ioda::ObsDtype::String) { - isInString(where, masks[jm], filterdata, varname); + isInString(where, currentParams.isIn.value()->as>(), + filterdata, varname); } else if (dtype == ioda::ObsDtype::Integer) { - isInInteger(where, masks[jm], filterdata, varname); + isInInteger(where, currentParams.isIn.value()->as>(), + filterdata, varname); } else { throw eckit::UserError( "Only integer and string variables may be used for processWhere 'is_in'", @@ -255,12 +429,39 @@ std::vector processWhere(const eckit::Configuration & config, } } +// Apply mask is_close + if (currentParams.isClose.value() != boost::none) { + if (dtype == ioda::ObsDtype::Float) { + std::vector data; + filterdata.get(varname, data); + if (currentParams.relativetolerance.value() == boost::none && + currentParams.absolutetolerance.value() != boost::none) { + processWhereIsClose(data, currentParams.absolutetolerance.value().get(), + false, currentParams.isClose.value().get(), where); + } else if (currentParams.relativetolerance.value() != boost::none && + currentParams.absolutetolerance.value() == boost::none) { + processWhereIsClose(data, currentParams.relativetolerance.value().get(), + true, currentParams.isClose.value().get(), where); + } else { + throw eckit::UserError( + "For 'is_close' one (and only one) tolerance is needed.", + Here()); + } + } else { + throw eckit::UserError( + "Only float variables may be used for processWhere 'is_close'", + Here()); + } + } + // Apply mask is_not_in - if (masks[jm].has("is_not_in")) { + if (currentParams.isNotIn.value() != boost::none) { if (dtype == ioda::ObsDtype::String) { - isNotInString(where, masks[jm], filterdata, varname); + isNotInString(where, currentParams.isNotIn.value()->as>(), + filterdata, varname); } else if (dtype == ioda::ObsDtype::Integer) { - isNotInInteger(where, masks[jm], filterdata, varname); + isNotInInteger(where, currentParams.isNotIn.value()->as>(), + filterdata, varname); } else { throw eckit::UserError( "Only integer and string variables may be used for processWhere 'is_not_in'", @@ -268,19 +469,119 @@ std::vector processWhere(const eckit::Configuration & config, } } +// Apply mask is_not_close + if (currentParams.isNotClose.value() != boost::none) { + if (dtype == ioda::ObsDtype::Float) { + std::vector data; + filterdata.get(varname, data); + if (currentParams.relativetolerance.value() == boost::none && + currentParams.absolutetolerance.value() != boost::none) { + processWhereIsNotClose(data, currentParams.absolutetolerance.value().get(), + false, currentParams.isNotClose.value().get(), where); + } else if (currentParams.relativetolerance.value() != boost::none && + currentParams.absolutetolerance.value() == boost::none) { + processWhereIsNotClose(data, currentParams.relativetolerance.value().get(), + true, currentParams.isNotClose.value().get(), where); + } else { + throw eckit::UserError( + "For 'is_close' one (and only one) tolerance is needed.", + Here()); + } + } else { + throw eckit::UserError( + "Only float variables may be used for processWhere 'is_not_close'", + Here()); + } + } + // Apply mask any_bit_set_of - if (masks[jm].has("any_bit_set_of")) { + if (currentParams.anyBitSetOf.value() != boost::none) { if (dtype == ioda::ObsDtype::Integer) { std::vector data; - std::set flags = oops::parseIntSet(masks[jm].getString("any_bit_set_of")); + const std::set &bitIndices = *currentParams.anyBitSetOf.value(); filterdata.get(varname, data); - processWhereBitSet(data, flags, where); + processWhereAnyBitSetOf(data, bitIndices, where); } else { throw eckit::UserError( "Only integer variables may be used for processWhere 'any_bit_set_of'", Here()); } } + +// Apply mask any_bit_unset_of + if (currentParams.anyBitUnsetOf.value() != boost::none) { + if (dtype == ioda::ObsDtype::Integer) { + std::vector data; + const std::set &bitIndices = *currentParams.anyBitUnsetOf.value(); + filterdata.get(varname, data); + processWhereAnyBitUnsetOf(data, bitIndices, where); + } else { + throw eckit::UserError( + "Only integer variables may be used for processWhere 'any_bit_unset_of'", + Here()); + } + } + +// Apply mask matches_regex + if (currentParams.matchesRegex.value() != boost::none) { + const std::string pattern = *currentParams.matchesRegex.value(); + // Select observations for which the variable 'varname' matches the regular expression + // 'pattern'. + if (dtype == ioda::ObsDtype::Integer) { + std::vector data; + filterdata.get(varname, data); + processWhereMatchesRegex(data, pattern, where); + } else if (dtype == ioda::ObsDtype::String) { + std::vector data; + filterdata.get(varname, data); + processWhereMatchesRegex(data, pattern, where); + } else { + throw eckit::UserError( + "Only string and integer variables may be used for processWhere 'matches_regex'", + Here()); + } + } + +// Apply mask matches_wildcard + if (currentParams.matchesWildcard.value() != boost::none) { + const std::string &pattern = *currentParams.matchesWildcard.value(); + // Select observations for which the variable 'varname' matches the pattern + // 'pattern', which may contain the * and ? wildcards. + if (dtype == ioda::ObsDtype::Integer) { + std::vector data; + filterdata.get(varname, data); + processWhereMatchesAnyWildcardPattern(data, {pattern}, where); + } else if (dtype == ioda::ObsDtype::String) { + std::vector data; + filterdata.get(varname, data); + processWhereMatchesAnyWildcardPattern(data, {pattern}, where); + } else { + throw eckit::UserError( + "Only string and integer variables may be used for processWhere 'matches_wildcard'", + Here()); + } + } + +// Apply mask matches_any_wildcard + if (currentParams.matchesAnyWildcard.value() != boost::none) { + const std::vector &patterns = *currentParams.matchesAnyWildcard.value(); + // Select observations for which the variable 'varname' matches any of the patterns + // 'patterns'; these may contain the * and ? wildcards. + if (dtype == ioda::ObsDtype::Integer) { + std::vector data; + filterdata.get(varname, data); + processWhereMatchesAnyWildcardPattern(data, patterns, where); + } else if (dtype == ioda::ObsDtype::String) { + std::vector data; + filterdata.get(varname, data); + processWhereMatchesAnyWildcardPattern(data, patterns, where); + } else { + throw eckit::UserError( + "Only string and integer variables may be used for processWhere " + "'matches_any_wildcard'", + Here()); + } + } } } } diff --git a/src/ufo/filters/processWhere.h b/src/ufo/filters/processWhere.h index b5ce829a3..434c1d422 100644 --- a/src/ufo/filters/processWhere.h +++ b/src/ufo/filters/processWhere.h @@ -12,14 +12,105 @@ #include #include +#include "oops/util/AnyOf.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/ParameterTraitsAnyOf.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "oops/util/PartialDateTime.h" +#include "ufo/filters/Variable.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + namespace eckit {class Configuration;} namespace ufo { class ObsFilterData; class Variables; -ufo::Variables getAllWhereVariables(const eckit::Configuration &); -std::vector processWhere(const eckit::Configuration &, const ObsFilterData &); +/// \brief Contents of a single element of the list taken by the `where` option of each filter, +/// used to select the subset of observation locations that should be processed by the filter. +class WhereParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(WhereParameters, Parameters); + + public: + /// Variable whose value must fulfil the conditions specified in the remaining parameters. + oops::RequiredParameter variable{"variable", this}; + + /// This is a tolerance used for absolute float comparison. Currently used for float + /// comparison with isClose and isNotClose. + oops::OptionalParameter absolutetolerance{"absolute_tolerance", this}; + + /// This is a tolerance used for relative tolerance comparison. Currently used for float + /// comparison with isClose and isNotClose. + oops::OptionalParameter relativetolerance{"relative_tolerance", this}; + + /// Select locations at which the condition variable is greater than or equal to the specified + /// value. Can be set to an int, float or datetime in the ISO 8601 format (if any datetime + /// components are zero, they are ignored). + oops::OptionalParameter> minvalue{ + "minvalue", this}; + + /// Select locations at which the condition variable is less than or equal to the specified + /// value. Can be set to an int, float or datetime in the ISO 8601 format (if any datetime + /// components are zero, they are ignored). + oops::OptionalParameter> maxvalue{ + "maxvalue", this}; + + /// Select locations at which the condition variable takes one of the specified values. For + /// integer variables, this can be an integer, range of integers (e.g. `3-5`) or a + /// comma-separated list of integers and/or ranges (e.g. `3-5, 7-8, 10`). For string variables, + /// this should be a bracketed list of strings (e.g. `[abc, def]`); + oops::OptionalParameter, std::vector>> isIn{ + "is_in", this}; + + /// Select locations at which the condition variable is within tolerance to specified values. + /// For float variables, this should be a bracketed list of floats (e.g. `[0.0, 0.5]`) + /// which are compared within a tolerance. A tolerance must be provided by the user and is a + /// float either called absolute_tolerance or relative_tolerance (see above); + oops::OptionalParameter> isClose{"is_close_to_any_of", this}; + + /// Select locations at which the condition variable does not take any of the specified values. + /// For integer variables, this can be an integer, range of integers (e.g. `3-5`) or a + /// comma-separated list of integers and/or ranges (e.g. `3-5, 7-8, 10`). For string variables, + /// this should be a bracketed list of strings (e.g. `[abc, def]`); + oops::OptionalParameter, std::vector>> isNotIn{ + "is_not_in", this}; + + /// Select locations at which the condition variable is not within tolerance of the + /// specified values. For float variables, this should be a bracketed list of floats + /// (e.g. `[0.0, 0.5]`) which are compared within a tolerance A tolerance must be + /// provided by the user and is a float either called absolute_tolerance or + /// relative_tolerance (see above); + oops::OptionalParameter> isNotClose{"is_not_close_to_any_of", this}; + + /// Select locations at which the condition variable is not set to to the missing value indicator. + oops::OptionalParameter isDefined{"is_defined", this}; + + /// Select locations at which the condition variable is set to to the missing value indicator. + oops::OptionalParameter isNotDefined{"is_not_defined", this}; + + /// Select locations at which any of the specified bits in the condition variable is set. + oops::OptionalParameter> anyBitSetOf{"any_bit_set_of", this}; + + /// Select locations at which any of the specified bits in the condition variable is unset. + oops::OptionalParameter> anyBitUnsetOf{"any_bit_unset_of", this}; + + /// Select locations at which the condition variable matches the specified wildcard (which may + /// contain the wildcard characters `*`, standing for any number of characters, and `?`, standing + /// for any character). + oops::OptionalParameter matchesWildcard{"matches_wildcard", this}; + + /// Select locations at which the condition variable matches any of the specified wildcards. + oops::OptionalParameter> matchesAnyWildcard{ + "matches_any_wildcard", this}; + + /// Select locations at which the condition variable matches the specified regular expression. + oops::OptionalParameter matchesRegex{"matches_regex", this}; +}; + +ufo::Variables getAllWhereVariables(const std::vector &); +std::vector processWhere(const std::vector &, const ObsFilterData &); } // namespace ufo diff --git a/src/ufo/filters/rttovonedvarcheck/CMakeLists.txt b/src/ufo/filters/rttovonedvarcheck/CMakeLists.txt new file mode 100644 index 000000000..a81279237 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/CMakeLists.txt @@ -0,0 +1,31 @@ +# (C) Copyright 2017-2020 Met Office. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( rttovonedvarcheck_files + RTTOVOneDVarCheck.h + RTTOVOneDVarCheck.cc + RTTOVOneDVarCheck.interface.F90 + RTTOVOneDVarCheck.interface.h + ufo_rttovonedvarcheck_constants_mod.f90 + ufo_rttovonedvarcheck_minimize_jacobian_mod.f90 + ufo_rttovonedvarcheck_minimize_ml_mod.f90 + ufo_rttovonedvarcheck_minimize_newton_mod.f90 + ufo_rttovonedvarcheck_minimize_utils_mod.f90 + ufo_rttovonedvarcheck_mod.f90 + ufo_rttovonedvarcheck_pcemis_mod.f90 + ufo_rttovonedvarcheck_profindex_mod.f90 + ufo_rttovonedvarcheck_rsubmatrix_mod.f90 + ufo_rttovonedvarcheck_ob_mod.f90 + ufo_rttovonedvarcheck_obs_mod.f90 + ufo_rttovonedvarcheck_utils_mod.f90 +) + +PREPEND( _p_rttovonedvarcheck_files "rttovonedvarcheck" ${rttovonedvarcheck_files} ) + +set ( rttovonedvarcheck_src_files + ${_p_rttovonedvarcheck_files} + PARENT_SCOPE +) + diff --git a/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.cc b/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.cc new file mode 100644 index 000000000..59fdfd49c --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.cc @@ -0,0 +1,120 @@ +/* + * (C) Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +/* 1-D var qc and retrieval of atmospheric state + * J(x) = (x-xb)T B-1 (x-xb) + (y-H(x))T R-1 (y-H(x)) + * Code adapted from Met Office OPS System + */ + +#include +#include +#include +#include +#include + +#include "ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.h" +#include "ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.interface.h" +#include "ufo/GeoVaLs.h" + +#include "eckit/exception/Exceptions.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- + +RTTOVOneDVarCheck::RTTOVOneDVarCheck(ioda::ObsSpace & obsdb, const Parameters_ & parameters, + std::shared_ptr > flags, + std::shared_ptr > obserr) + : FilterBase(obsdb, parameters, flags, obserr), channels_(), retrieved_vars_(), + hoxdiags_retrieved_vars_(), parameters_(parameters) +{ + oops::Log::trace() << "RTTOVOneDVarCheck contructor starting" << std::endl; + + // Check only one variable has been defined - BT + // Get channels from filter variables + if (filtervars_.size() != 1) { + throw eckit::UserError("RTTOVOneDVarCheck contructor:" + " only one variable allowed, aborting."); + } + channels_ = filtervars_[0].channels(); + + // Check at least one channel has been defined + if (channels_.empty()) { + throw eckit::UserError("RTTOVOneDVarCheck contructor: no channels defined, aborting."); + } + + // Setup Fortran object + ufo_rttovonedvarcheck_create_f90(keyRTTOVOneDVarCheck_, obsdb, parameters_.toConfiguration(), + channels_.size(), channels_[0], retrieved_vars_, QCflags::onedvar, QCflags::pass); + + // Create hofxdiags + for (size_t jvar = 0; jvar < retrieved_vars_.size(); ++jvar) { + for (size_t jch = 0; jch < channels_.size(); ++jch) { + hoxdiags_retrieved_vars_.push_back("brightness_temperature_jacobian_"+ + retrieved_vars_[jvar]+"_"+std::to_string(channels_[jch])); + } + } + + // Populate variables list - which makes sure this is not run as a pre-process filter + // because model data is needed + Variables model_vars(retrieved_vars_); + allvars_ += Variables(model_vars, "GeoVaLs"); + + oops::Log::trace() << "RTTOVOneDVarCheck contructor complete. " << std::endl; +} + +// ----------------------------------------------------------------------------- + +RTTOVOneDVarCheck::~RTTOVOneDVarCheck() { + ufo_rttovonedvarcheck_delete_f90(keyRTTOVOneDVarCheck_); + oops::Log::trace() << "RTTOVOneDVarCheck destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void RTTOVOneDVarCheck::applyFilter(const std::vector & apply, + const Variables & filtervars, + std::vector> &) const { + oops::Log::trace() << "RTTOVOneDVarCheck Filter starting" << std::endl; + +// Get GeoVaLs + const ufo::GeoVaLs * gvals = data_.getGeoVaLs(); + +// Create oops variable with the list of channels + oops::Variables variables = filtervars.toOopsVariables(); + +// Convert apply to char for passing to fortran +// needed for channel selection + std::vector apply_char(apply.size(), 'F'); + for (size_t i = 0; i < apply_char.size(); i++) { + if (apply[i]) {apply_char[i]='T';} + } + +// Save qc flags to database for retrieval in fortran - needed for channel selection + flags_->save("FortranQC"); // temporary measure as per ROobserror qc + +// Pass it all to fortran + ufo_rttovonedvarcheck_apply_f90(keyRTTOVOneDVarCheck_, parameters_.ModOptions.value(), + variables, hoxdiags_retrieved_vars_, + gvals->toFortran(), + apply_char.size(), apply_char[0]); + +// Read qc flags from database + flags_->read("FortranQC"); // temporary measure as per ROobserror qc + + oops::Log::trace() << "RTTOVOneDVarCheck Filter complete" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void RTTOVOneDVarCheck::print(std::ostream & os) const { + os << "RTTOVOneDVarCheck: config = " << parameters_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.h b/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.h new file mode 100644 index 000000000..140941a44 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.h @@ -0,0 +1,76 @@ +/* + * (C) Copyright 2017-2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_RTTOVONEDVARCHECK_RTTOVONEDVARCHECK_H_ +#define UFO_FILTERS_RTTOVONEDVARCHECK_RTTOVONEDVARCHECK_H_ + +#include +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "oops/util/Printable.h" +#include "ufo/filters/FilterBase.h" +#include "ufo/filters/QCflags.h" +#include "ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.interface.h" +#include "ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheckParameters.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} + +namespace ufo { +//! \brief RTTOVOneDVarCheck +//! +//! \details RTTOVOneDVarCheck performs a 1D-Var minimization for satellite using the rttov +//! forward operator. If a profile does not converge then all channels for this observation +//! are removed. Some parameters (e.g. surface emissivity) are retrieved and saved in the +//! obsspace for use in 4D-Var. The code is based on the Met Office 1D-Var scheme and thus +//! is predominently in Fortran. +//! +//! \author Met Office +//! +//! \date 09/06/2020 +//! + +class RTTOVOneDVarCheck : public FilterBase, + private util::ObjectCounter { + public: + /// The type of parameters accepted by the constructor of this filter. + /// This typedef is used by the FilterFactory. + typedef RTTOVOneDVarCheckParameters Parameters_; + + static const std::string classname() {return "ufo::RTTOVOneDVarCheck";} + + RTTOVOneDVarCheck(ioda::ObsSpace &, const Parameters_ &, + std::shared_ptr >, + std::shared_ptr >); + ~RTTOVOneDVarCheck() override; + + private: + void print(std::ostream &) const override; + void applyFilter(const std::vector &, const Variables &, + std::vector> &) const override; + int qcFlag() const override {return QCflags::onedvar;} + + F90obfilter keyRTTOVOneDVarCheck_; + std::vector channels_; + oops::Variables retrieved_vars_; + oops::Variables hoxdiags_retrieved_vars_; + Parameters_ parameters_; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_RTTOVONEDVARCHECK_RTTOVONEDVARCHECK_H_ diff --git a/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.interface.F90 b/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.interface.F90 new file mode 100644 index 000000000..81c057ce4 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.interface.F90 @@ -0,0 +1,143 @@ +! (C) Copyright 2017-2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +module ufo_rttovonedvarcheck_mod_c + +use fckit_configuration_module, only: fckit_configuration +use iso_c_binding +use oops_variables_mod +use ufo_geovals_mod +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_rttovonedvarcheck_mod +use ufo_rttovonedvarcheck_utils_mod, only: ufo_rttovonedvarcheck + +implicit none +private + +#define LISTED_TYPE ufo_rttovonedvarcheck + +!> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + +!> Global registry +type(registry_t) :: ufo_rttovonedvarcheck_registry + +! ------------------------------------------------------------------------------------------------ +contains +! ------------------------------------------------------------------------------------------------ +!> Linked list implementation +#include "oops/util/linkedList_c.f" +! ------------------------------------------------------------------------------------------------ + +subroutine ufo_rttovonedvarcheck_create_c(c_self, c_obspace, c_conf, c_nchan, & + c_channels, c_varlist, c_onedvarflag, & + c_passflag) & + bind(c,name='ufo_rttovonedvarcheck_create_f90') + +!> \brief Interface to the Fortran create method +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +implicit none +integer(c_int), intent(inout) :: c_self !< self - inout +type(c_ptr), value, intent(in) :: c_obspace !< obsspace - in +type(c_ptr), value, intent(in) :: c_conf !< yaml configuration - in +integer(c_int), intent(in) :: c_nchan !< number of channels - in +integer(c_int), intent(in) :: c_channels(c_nchan) !< channel numbers - in +type(c_ptr), intent(in), value :: c_varlist !< retrieved variables pointer - in +integer(c_int), intent(in) :: c_onedvarflag !< flag for qc manager logging - in +integer(c_int), intent(in) :: c_passflag !< flag for good data from qc manager - in + +type(ufo_rttovonedvarcheck), pointer :: self +type(fckit_configuration) :: f_conf +type(oops_variables) :: oops_vars + +call ufo_rttovonedvarcheck_registry%setup(c_self, self) +f_conf = fckit_configuration(c_conf) + +call ufo_rttovonedvarcheck_create(self, c_obspace, f_conf, c_channels, & + c_onedvarflag, c_passflag) + +!> Update C++ ObsFilter with input variable list +oops_vars = oops_variables(c_varlist) +call oops_vars%push_back( self % retrieval_variables ) + +end subroutine ufo_rttovonedvarcheck_create_c + +! ------------------------------------------------------------------------------------------------ + +subroutine ufo_rttovonedvarcheck_delete_c(c_self) & + bind(c,name='ufo_rttovonedvarcheck_delete_f90') + +!> \brief Interface to the Fortran delete method +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! + +implicit none +integer(c_int), intent(inout) :: c_self !< self - input + +type(ufo_rttovonedvarcheck), pointer :: self + +call ufo_rttovonedvarcheck_registry%get(c_self, self) +call ufo_rttovonedvarcheck_delete(self) +call ufo_rttovonedvarcheck_registry%delete(c_self, self) + +end subroutine ufo_rttovonedvarcheck_delete_c + +! ------------------------------------------------------------------------------------------------ + +subroutine ufo_rttovonedvarcheck_apply_c(c_self, c_conf, c_vars, c_retvars, c_geovals, c_nobs, c_apply) & + bind(c,name='ufo_rttovonedvarcheck_apply_f90') + +!> \brief Interface to filter apply method +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! + +implicit none +integer(c_int), intent(in) :: c_self !< self - input +type(c_ptr), value, intent(in) :: c_conf !< yaml configuration - in +type(c_ptr), value, intent(in) :: c_vars !< list of variables - input +type(c_ptr), value, intent(in) :: c_retvars !< list of retrieval variables - input +integer(c_int), intent(in) :: c_geovals !< Geovals - input +integer(c_int), intent(in) :: c_nobs !< number of observations - input +character(c_char), intent(in) :: c_apply(c_nobs) !< apply flag (converted to logical) - input + +type(ufo_rttovonedvarcheck), pointer :: self +type(fckit_configuration) :: f_conf +type(oops_variables) :: vars +type(oops_variables) :: retrieval_vars +type(ufo_geovals), pointer :: geovals +integer :: ii +logical :: apply(c_nobs) + +call ufo_rttovonedvarcheck_registry%get(c_self, self) +call ufo_geovals_registry%get(c_geovals, geovals) +f_conf = fckit_configuration(c_conf) + +vars = oops_variables(c_vars) +retrieval_vars = oops_variables(c_retvars) + +! Convert character to logical for passing to Fortran +apply(:) = .false. +where (c_apply == 'T') + apply = .true. +end where + +call ufo_rttovonedvarcheck_apply(self, f_conf, vars, retrieval_vars, geovals, apply) + +end subroutine ufo_rttovonedvarcheck_apply_c + +! ------------------------------------------------------------------------------------------------ + +end module ufo_rttovonedvarcheck_mod_c + diff --git a/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.interface.h b/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.interface.h new file mode 100644 index 000000000..3647eaebc --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.interface.h @@ -0,0 +1,39 @@ +/* + * (C) Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_RTTOVONEDVARCHECK_RTTOVONEDVARCHECK_INTERFACE_H_ +#define UFO_FILTERS_RTTOVONEDVARCHECK_RTTOVONEDVARCHECK_INTERFACE_H_ + +#include "oops/base/Variables.h" +#include "ufo/Fortran.h" + +// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + +/// Interface to Fortran routines + +extern "C" { + void ufo_rttovonedvarcheck_create_f90(F90obfilter &, const ioda::ObsSpace &, + const eckit::Configuration &, const int &, const int &, + oops::Variables &, const int &, const int &); + void ufo_rttovonedvarcheck_delete_f90(F90obfilter &); + void ufo_rttovonedvarcheck_apply_f90(const F90obfilter &, const eckit::Configuration &, + const oops::Variables &, const oops::Variables &, + const F90goms &, const int &, const char &); +} // extern C + +} // namespace ufo + +#endif // UFO_FILTERS_RTTOVONEDVARCHECK_RTTOVONEDVARCHECK_INTERFACE_H_ diff --git a/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheckParameters.h b/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheckParameters.h new file mode 100644 index 000000000..f9830ba79 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheckParameters.h @@ -0,0 +1,118 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_FILTERS_RTTOVONEDVARCHECK_RTTOVONEDVARCHECKPARAMETERS_H_ +#define UFO_FILTERS_RTTOVONEDVARCHECK_RTTOVONEDVARCHECKPARAMETERS_H_ + +#include +#include + +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "ufo/filters/FilterParametersBase.h" + +namespace ufo { + +/// Parameters controlling the operation of the ObsBoundsCheck filter. +class RTTOVOneDVarCheckParameters : public FilterParametersBase { + OOPS_CONCRETE_PARAMETERS(RTTOVOneDVarCheckParameters, FilterParametersBase) + + public: + /// Path to the b-matrix file + oops::RequiredParameter BMatrix{"BMatrix", this}; + + /// Path to the r-matrix file + oops::RequiredParameter RMatrix{"RMatrix", this}; + + /// Specify the number of levels in the profiles + oops::RequiredParameter NLevels{"nlevels", this}; + + /// List of the retrieval variables these will need to match the b-matrix file + oops::RequiredParameter> + RetrievalVariables{"retrieval variables", this}; + + /// Options required for the forward model - RTTOV + oops::RequiredParameter ModOptions{"ModOptions", this}; + + /// Specify the forward model to use - currently only RTTOV + /// is available + oops::Parameter ModName{"ModName", "RTTOV", this}; + + /// Is qtotal being used instead of separate q, clw, ciw + oops::Parameter QTotal{"qtotal", false, this}; + + /// Choose whether to split rain in qsplit routine + oops::Parameter UseQtSplitRain{"UseQtSplitRain", true, this}; + + /// Make sure profile is setup to use RTTOV-Scatt + oops::Parameter RTTOVMWScattSwitch{"RTTOVMWScattSwitch", false, this}; + + /// Use total ice option for RTTOV-Scatt + oops::Parameter RTTOVUseTotalIce{"RTTOVUseTotalIce", true, this}; + + /// Use the Marquardt-Levenberg minimizer - default is Newton + oops::Parameter UseMLMinimization{"UseMLMinimization", false, this}; + + /// Use cost function to determine when a profile has converged + /// the default convergence options tests the absolute difference in the + /// profile between iterations + oops::Parameter UseJforConvergence{"UseJforConvergence", false, this}; + + /// Use liquid water in the q saturation calculations + oops::Parameter UseRHwaterForQC{"UseRHwaterForQC", true, this}; + + /// Reset low level temperatures over seaice and cold, low land + oops::Parameter UseColdSurfaceCheck{"UseColdSurfaceCheck", false, this}; + + /// Output the LWP if the profile converges + oops::Parameter Store1DVarLWP{"Store1DVarLWP", false, this}; + + /// Turn on extra diagnostics + oops::Parameter FullDiagnostics{"FullDiagnostics", false, this}; + + /// Maximum number of iterations to perform in minimization + oops::Parameter Max1DVarIterations{"Max1DVarIterations", 7, this}; + + /// Integer to select convergence option. 1= percentage change in cost tested between iterations + /// otherwise = absolute change in cost tested between iterations + oops::Parameter JConvergenceOption{"JConvergenceOption", 1, this}; + + /// Choose which iteration to start checking the liquid water path + oops::Parameter IterNumForLWPCheck{"IterNumForLWPCheck", 2, this}; + + /// Maximum number of iterations for internal Marquardt-Levenberg loop + oops::Parameter MaxMLIterations{"MaxMLIterations", 7, this}; + + /// Starting observation to run through 1d-var, subsetting for testing + oops::Parameter StartOb{"StartOb", 0, this}; + + /// Final observation to run through 1d-var, subsetting for testing + oops::Parameter FinishOb{"FinishOb", 0, this}; + + /// Convergence factor used when the absolute difference in the profile is used + /// to determine convergence. + oops::Parameter ConvergenceFactor{"ConvergenceFactor", 0.4, this}; + + /// Cost threshold for convergence check when cost function value is used for convergence + oops::Parameter CostConvergenceFactor{"CostConvergenceFactor", 0.01, this}; + + /// Default emissivity value to use over land + oops::Parameter EmissLandDefault{"EmissLandDefault", 0.95, this}; + + /// Default emissivity value to use over seaice + oops::Parameter EmissSeaIceDefault{"EmissSeaIceDefault", 0.92, this}; + + /// Default eigen value path is blank but needs to be present if using PC emiss + oops::Parameter EmisEigVecPath{"EmisEigVecPath", "", this}; + + /// Default emis atlas path is blank + oops::Parameter EmisAtlas{"EmisAtlas", "", this}; +}; + +} // namespace ufo + +#endif // UFO_FILTERS_RTTOVONEDVARCHECK_RTTOVONEDVARCHECKPARAMETERS_H_ diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_constants_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_constants_mod.f90 new file mode 100644 index 000000000..e1454efd2 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_constants_mod.f90 @@ -0,0 +1,47 @@ +! (C) copyright 2020 Met Office +! +! this software is licensed under the terms of the apache licence version 2.0 +! which can be obtained at http://www.apache.org/licenses/license-2.0. + +!> Fortran module constants used throughout the rttovonedvarcheck filter + +module ufo_rttovonedvarcheck_constants_mod + +use kinds + +implicit none +private + +!----------------------------------------------------------------------------- +! 1. miscellaneous definitions +!----------------------------------------------------------------------------- +integer, parameter, public :: max_string = 200 !< maximum string length + +! RTTOV values for surface land, sea and seaice +integer, parameter, public :: & + RTLand = 0, & !< integer for land surface type + RTSea = 1, & !< integer for sea surface type + RTIce = 2 !< integer for seaice surface type + +!----------------------------------------------------------------------------- +! 2. Physical Constants +!----------------------------------------------------------------------------- + +real(kind_real), parameter, public :: & + MaxTemperature = 340.0_kind_real, & !< Maximum temperature ( K ) + MinTemperature = 70.0_kind_real !< Minimum temperature ( K ) + +!----------------------------------------------------------------------------- +! 3. Information for emissivity retrieval +! This is not currently available but has been left in for future development +!----------------------------------------------------------------------------- + +!< Mapping for each of the 20 ATOVS channels (1-15 AMSU-A; 16-20 AMSU-B) +integer, parameter, public :: EmissElements(20) = & + (/ 1,2,3,3,3,3,3,3,3,3,3,3,3,3,4, & ! AMSU-A mapping + 4,5,5,5,5 /) ! AMSU-B mapping + +!< ATOVS Channel numbers for each of the 5 emissivity values +integer, parameter, public :: EmissMap(5) = (/ 1, 2, 3, 16, 17 /) + +end module ufo_rttovonedvarcheck_constants_mod diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_jacobian_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_jacobian_mod.f90 new file mode 100644 index 000000000..8ee6466c8 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_jacobian_mod.f90 @@ -0,0 +1,536 @@ +! (C) Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to get the jacobian for the 1D-Var + +module ufo_rttovonedvarcheck_minimize_jacobian_mod + +use iso_c_binding +use kinds +use ufo_constants_mod, only: zero +use ufo_geovals_mod +use ufo_radiancerttov_mod +use ufo_rttovonedvarcheck_constants_mod +use ufo_rttovonedvarcheck_ob_mod +use ufo_rttovonedvarcheck_profindex_mod +use ufo_rttovonedvarcheck_utils_mod, only: ufo_rttovonedvarcheck +use ufo_vars_mod +use ufo_utils_mod, only: Ops_SatRad_Qsplit + +implicit none +private + +public ufo_rttovonedvarcheck_get_jacobian + +contains + +!------------------------------------------------------------------------------ +!> Get the jacobian used in the 1D-Var. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_get_jacobian(self, geovals, ob, channels, & + profindex, & + prof_x, hofxdiags, rttov_simobs, & + hofx, H_matrix) + +implicit none + +! subroutine arguments +type(ufo_rttovonedvarcheck), intent(in) :: self !< Main 1D-Var object +type(ufo_geovals), intent(in) :: geovals !< model data at obs location +type(ufo_rttovonedvarcheck_ob), intent(inout) :: ob !< satellite metadata +integer, intent(in) :: channels(:) !< channels used for this calculation +type(ufo_rttovonedvarcheck_profindex), intent(in) :: profindex !< index array for x vector +real(kind_real), intent(in) :: prof_x(:) !< x vector +type(ufo_geovals), intent(inout) :: hofxdiags !< model data to pass the jacobian +type(ufo_radiancerttov), intent(inout) :: rttov_simobs !< rttov simulate obs object +real(kind_real), intent(out) :: hofx(:) !< BT's +real(kind_real), intent(out) :: H_matrix(:,:) !< Jacobian + +select case (trim(ob % forward_mod_name)) + case ("RTTOV") + call ufo_rttovonedvarcheck_GetHmatrixRTTOVsimobs(geovals, ob, self % obsdb, & + rttov_simobs, channels, & + profindex, hofxdiags, & + self % UseQtsplitRain, self % FullDiagnostics, & + hofx(:), H_matrix) ! out + + case default + call abor1_ftn("rttovonedvarcheck get jacobian: no suitable forward model => exiting") +end select + +end subroutine ufo_rttovonedvarcheck_get_jacobian + +!------------------------------------------------------------------------------ +!> Get the jacobian from rttov and if neccessary convert +!! to variables used in the 1D-Var. +!! +!! \details Heritage: Ops_SatRad_GetHmatrix_RTTOV12.f90 +!! +!! \warning mwemiss and emisspc not implemented yet +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_GetHmatrixRTTOVsimobs(geovals, ob, obsdb, & + rttov_data, channels, profindex, & + hofxdiags, UseQtsplitRain, FullDiagnostics, & + hofx, H_matrix) + +implicit none + +! subroutine arguments +type(ufo_geovals), intent(in) :: geovals !< model data at obs location +type(ufo_rttovonedvarcheck_ob), intent(inout) :: ob !< satellite metadata +type(c_ptr), value, intent(in) :: obsdb !< observation database +type(ufo_radiancerttov), intent(inout) :: rttov_data !< structure for running rttov_k +integer, intent(in) :: channels(:) !< channels used for this calculation +type(ufo_rttovonedvarcheck_profindex), intent(in) :: profindex !< index array for x vector +type(ufo_geovals), intent(inout) :: hofxdiags !< model data to pass the jacobian +logical, intent(in) :: UseQtsplitRain !< flag to make qtsplit use rain +logical, intent(in) :: FullDiagnostics +real(kind_real), intent(out) :: hofx(:) !< BT's +real(kind_real), intent(out) :: H_matrix(:,:) !< Jacobian + +! Local arguments +integer :: nchans, nlevels, nq_levels +integer :: i, j +integer :: chan +logical :: RTTOV_GasunitConv = .false. +real(kind_real),allocatable :: q_kgkg(:) +real(kind_real) :: s2m_kgkg +type(ufo_geoval), pointer :: geoval +real(kind_real), allocatable :: pressure(:) +real(kind_real), allocatable :: dq_dqt(:) +real(kind_real), allocatable :: dql_dqt(:) +real(kind_real), allocatable :: dqi_dqt(:) +real(kind_real), allocatable :: dBT_dq(:) +real(kind_real), allocatable :: dBT_dql(:) +character(len=max_string) :: varname +real(c_double) :: BT(size(ob % channels_all)) +real(kind_real) :: u, v, dBT_du, dBT_dv, windsp + +nchans = size(channels) + +call rttov_data % simobs(geovals, obsdb, size(ob % channels_all), 1, BT, hofxdiags, ob_info=ob) + +! -------------------- +!Get hofx for just channels used +!-------------------- +all_chan_loop: do i = 1, size(ob % channels_all) + do j = 1, nchans + if(channels(j) == ob % channels_all(i)) then + hofx(j) = BT(i) + cycle all_chan_loop + end if + end do +end do all_chan_loop + +!---------------------------------------------------------------- +! 1.1) Temperature - invert as RTTOV level 1 as top of atmosphere and +! 1Dvar profile and is from the surface up. +! var_ts - air_temperature +! Note : RTTOV jacobian is TOA -> surface same as prof_x +!---------------------------------------------------------------- +if (profindex % t(1) > 0) then + nlevels = profindex % t(2) - profindex % t(1) + 1 + do i = 1, nchans + write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_ts),"_",channels(i) ! K + call ufo_geovals_get_var(hofxdiags , varname, geoval) + H_matrix(i,profindex % t(1):profindex % t(2)) = geoval % vals(:,1) + end do +end if + +!------ +! 1.2) Water vapour +!------ + +! Water Vapour Jacobians must be converted from +! kg/kg to ln(g/kg) - the unit conversion cancels, then: +! dy/d(ln q) = dy/dq * q(kg/kg) +! var_q = "specific_humidity" ! kg/kg +! Note : RTTOV jacobian is TOA -> surface same as prof_x +if (profindex % q(1) > 0) then + + nq_levels = profindex % q(2)-profindex % q(1)+1 + allocate(q_kgkg(nq_levels)) + + ! Get humidity data from geovals + q_kgkg(:) = zero + call ufo_geovals_get_var(geovals, var_q, geoval) + q_kgkg(:) = geoval%vals(nlevels:1:-1, 1) + + do i = 1, nchans + write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_q),"_",channels(i) ! kg/kg + call ufo_geovals_get_var(hofxdiags, varname, geoval) + H_matrix(i,profindex % q(1):profindex % q(2)) = geoval % vals(:,1) * q_kgkg(:) + end do + + deallocate(q_kgkg) + +end if + +!------ +! 1.3) Total water +!------ + +! For the sake of this first implementation this will not include liquid +! and ice water content just water vapour which is consistent with the +! profile loaded from GeoVaLs. +! var_q = "specific_humidity" ! kg/kg +! var_clw = "mass_content_of_cloud_liquid_water_in_atmosphere_layer" +! var_cli = "mass_content_of_cloud_ice_in_atmosphere_layer" +! Note : RTTOV jacobian is TOA -> surface same as prof_x +if (profindex % qt(1) > 0) then + + allocate(q_kgkg(nlevels)) + allocate(pressure(nlevels)) + allocate(dq_dqt(nlevels)) + allocate(dql_dqt(nlevels)) + allocate(dqi_dqt(nlevels)) + allocate(dBT_dq(nlevels)) + allocate(dBT_dql(nlevels)) + + ! Get humidity data from geovals + q_kgkg(:) = zero + call ufo_geovals_get_var(geovals, var_q, geoval) + q_kgkg(:) = q_kgkg(:) + geoval%vals(nlevels:1:-1, 1) + call ufo_geovals_get_var(geovals, var_clw, geoval) + q_kgkg(:) = q_kgkg(:) + geoval%vals(nlevels:1:-1, 1) + call ufo_geovals_get_var(geovals, var_cli, geoval) + q_kgkg(:) = q_kgkg(:) + geoval%vals(nlevels:1:-1, 1) + + ! var_prs = "air_pressure" Pa + call ufo_geovals_get_var(geovals, trim(var_prs), geoval) + pressure(:) = geoval%vals(nlevels:1:-1, 1) + + ! Calculate the gradients with respect to qtotal + call Ops_SatRad_Qsplit ( 0, & + pressure(:), & + ob % background_T(nlevels:1:-1), & + q_kgkg(:), & ! in + dq_dqt(:), & ! out + dql_dqt(:), & ! out + dqi_dqt(:), & ! out + UseQtsplitRain) + + ! Calculate jacobian wrt humidity and clw + do i = 1, nchans + + write(varname,"(3a,i0)") "brightness_temperature_jacobian_", trim(var_q), "_", channels(i) ! kg/kg + call ufo_geovals_get_var(hofxdiags, varname, geoval) + dBT_dq(:) = zero + dBT_dq(:) = geoval % vals(:,1) + + write(varname,"(3a,i0)") "brightness_temperature_jacobian_", trim(var_clw), "_", channels(i) ! kg/kg + call ufo_geovals_get_var(hofxdiags, varname, geoval) + dBT_dql(:) = zero + dBT_dql(:) = geoval % vals(:,1) + + H_matrix(i,profindex % qt(1):profindex % qt(2)) = & + (dBT_dq(:) * dq_dqt(:) + & + dBT_dql(:) * dql_dqt(:) ) * q_kgkg(:) + + end do + + ! Clean up + deallocate(q_kgkg) + deallocate(pressure) + deallocate(dq_dqt) + deallocate(dql_dqt) + deallocate(dqi_dqt) + deallocate(dBT_dq) + deallocate(dBT_dql) + +end if + +!---- +! 2.) Single-valued variables +!---- + +! 2.1) Surface Temperature - var_sfc_t2m = "surface_temperature" + +if (profindex % t2 > 0) then + do i = 1, nchans + write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_t2m),"_",channels(i) ! K + call ufo_geovals_get_var(hofxdiags, varname, geoval) + H_matrix(i,profindex % t2) = geoval % vals(1,1) + end do +end if + +! 2.2) Water vapour - var_sfc_q2m = "specific_humidity_at_two_meters_above_surface" ! (kg/kg) + +if (profindex % q2 > 0) then + s2m_kgkg = zero + call ufo_geovals_get_var(geovals, var_sfc_q2m, geoval) + s2m_kgkg = geoval%vals(1, 1) + do i = 1, nchans + write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_q2m),"_",channels(i) ! kg/kg + call ufo_geovals_get_var(hofxdiags, varname, geoval) + H_matrix(i,profindex % q2) = geoval % vals(1,1) * s2m_kgkg + end do +end if + +! 2.3) Surface pressure - var_sfc_p2m = "air_pressure_at_two_meters_above_surface" ! (Pa) + +if (profindex % pstar > 0) then + do i = 1, nchans + write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_p2m),"_",channels(i) + call ufo_geovals_get_var(hofxdiags, varname, geoval) + H_matrix(i,profindex % pstar) = geoval % vals(1,1) + end do +end if + +! 2.4) Windspeed - var_u = "eastward_wind" +! - var_v = "northward_wind" +! - windsp = sqrt (u*u + v*v) + +if (profindex % windspeed > 0) then + call ufo_geovals_get_var(geovals, trim(var_u), geoval) + u = geoval % vals(1, 1) + call ufo_geovals_get_var(geovals, trim(var_v), geoval) + v = geoval % vals(1, 1) + windsp = sqrt (u ** 2 + v ** 2) + + do i = 1, nchans + write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_u),"_",channels(i) + call ufo_geovals_get_var(hofxdiags, varname, geoval) + dBT_du = geoval % vals(1,1) + write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_v),"_",channels(i) + call ufo_geovals_get_var(hofxdiags, varname, geoval) + dBT_dv = geoval % vals(1,1) + if (windsp > zero) then + ! directional derivation of the Jacobian + H_matrix(i,profindex % windspeed) = (dBT_du * u + dBT_dv * v) / windsp + else + H_matrix(i,profindex % windspeed) = zero + end if + end do +end if + +! 2.5) Skin temperature - var_sfc_tskin = "skin_temperature" ! (K) + +if (profindex % tstar > 0) then + do i = 1, nchans + write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_tskin),"_",channels(i) + call ufo_geovals_get_var(hofxdiags, varname, geoval) + H_matrix(i,profindex % tstar) = geoval % vals(1,1) + end do +end if + +! This has been left in for future development +! 2.5) Cloud top pressure +! This is not in rttov interface yet +!if (profindex % cloudtopp > 0) then +! do i = 1, nchans +! varname = "cloud_top_pressure" +! write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(varname),"_",channels(i) +! call ufo_geovals_get_var(hofxdiags, varname, geoval) +! H_matrix(i,profindex % cloudtopp) = geoval % vals(1,1) +! end do +!end if + +! This has been left in for future development +! 2.6) Effective cloud fraction +! This is not in rttov interface yet +!if (profindex % cloudfrac > 0) then +! do i = 1, nchans +! varname = "cloud_fraction" +! call ufo_geovals_get_var(hofxdiags, varname, geoval) +! H_matrix(i,profindex % cloudfrac) = geoval % vals(1,1) +! end do +!end if + +!---- +! 3.) Emissivities +! This has been left in for future development +!---- + +! 3.1 Microwave Emissivity - var_sfc_emiss = "surface_emissivity" + +!if (profindex % mwemiss(1) > 0) then +! ! The emissivity matrix needs to be "unpacked" as it is only one +! ! dimensional over channels - implying you want to retrieve a +! ! single emissivity value. It is unpacked here so that each emissivity +! ! has a corresponding entry for the relevant channel. Note that this is +! ! a bit physically dubious as several channels have the same frequency, etc. +! ! This complexity is dealt with in the B Matrix. +! ! Check that we want only the diagonal elements to be non-zero +! do j = 1, size(EmissMap) +! chan = EmissMap(j) +! do i = 1, nchans +! if (channels(i) == chan) then +! write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_emiss),"_",channels(i) +! call ufo_geovals_get_var(hofxdiags, varname, geoval) +! H_matrix(i,profindex % mwemiss(1) + j - 1) = geoval % vals(1,1) +! end if +! end do +! end do +!end if + +!! 3.2. Infrared Emissivity - work in progress +! +!IF (profindex % emisspc(1) > 0) THEN +! +! DO i = 1, nchans +! emissivity_K(i) = rttov_data % profiles_k(i) % emissivity(1) +! END DO +! +! CALL Ops_SatRad_EmisKToPC (nchans, & ! in +! Channels, & ! in +! nemisspc, & ! in +! emiss(:), & ! in +! emissivity_K(:), & ! in +! H_matrix(1:nchans,profindex % emisspc(1):profindex % emisspc(2))) ! out +!END IF +! +! Here for diagnostics + +if (FullDiagnostics) then + call ufo_rttovonedvarcheck_PrintHmatrix( & + nchans, & ! in + profindex % nprofelements, & ! in + ob % channels_used, & ! in + H_matrix, & ! in + profindex ) ! in +end if + +end subroutine ufo_rttovonedvarcheck_GetHmatrixRTTOVsimobs + +!--------------------------------------------------------------------------- +!> Routine to print the contents of the jacobian for testing +!! +!! \details Heritage: Ops_SatRad_PrintHMatrix.f90 +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_PrintHmatrix( & + nchans, & ! in + nprofelements, & ! in + channels, & ! in + H_matrix, & ! in + profindex ) ! in + +implicit none + +!Subroutine arguments: +integer, intent(in) :: nchans +integer, intent(in) :: nprofelements +integer, intent(in) :: channels(nchans) +real(kind_real), intent(in) :: H_matrix(nchans,nprofelements) +type(ufo_rttovonedvarcheck_profindex), intent(in) :: profindex + +! Local variables: +integer :: i +character(len=10) :: int_fmt +character(len=12) :: real_fmt +character(len=3) :: txt_nchans +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_PrintHmatrix" +!------------------------------------------------------------------------------- + +write( unit=txt_nchans,fmt='(i3)' ) nchans +write( unit=int_fmt,fmt='(a)' ) '(' // trim(txt_nchans) // 'I30)' +write( unit=real_fmt,fmt='(a)' ) '(' // trim(txt_nchans) // 'E30.15)' + +write(*,*) + +write(*, int_fmt) channels(:) + + if ( profindex % t(1) > 0 ) THEN + write(*, '(a)') 'Temperature Profile' + do i = profindex%t(1),profindex%t(2) + write(*, real_fmt) H_matrix(:,i) + end do + end if + + if ( profindex % q(1) > 0 ) THEN + write(*, '(a)') 'q Profile' + do i = profindex%q(1),profindex%q(2) + write(*, real_fmt) H_matrix(:,i) + end do + end if + + if ( profindex % qt(1) > 0 ) THEN + write(*, '(a)') 'qt Profile /1000' + do i = profindex%qt(1),profindex%qt(2) + write(*, real_fmt) H_matrix(:,i)/1000 + end do + end if + + if ( profindex % o3profile(1) > 0 ) THEN + write(*, '(a)') 'Ozone Profile' + do i = profindex%o3profile(1),profindex%o3profile(2) + write(*, real_fmt) H_matrix(:,i) + end do + end if + + if ( profindex % o3total > 0 ) THEN + write(*, '(a)') 'Total Column Ozone' + write(*, real_fmt) H_matrix(:,profindex%o3total) + end if + + + if ( profindex % lwp > 0 ) THEN + write(*, '(a)') 'LWP' + write(*, real_fmt) H_matrix(i,profindex % lwp) + end if + + if ( profindex % t2 > 0 ) THEN + write(*, '(a)') '2m T' + write(*, real_fmt) H_matrix(:,profindex % t2) + end if + + if ( profindex % q2 > 0 ) THEN + write(*, '(a)') '2m q' + write(*, real_fmt) H_matrix(:,profindex % q2) + end if + + if ( profindex % pstar > 0 ) THEN + write(*, '(a)') 'P Star' + write(*, real_fmt) H_matrix(:,profindex % pstar) + end if + + if ( profindex % windspeed > 0 ) THEN + write(*, '(a)') 'Windspeed' + write(*, real_fmt) H_matrix(:,profindex % windspeed) + end if + + if ( profindex % tstar > 0 ) THEN + write(*, '(a)') 'Skin Temperature' + write(*, real_fmt) H_matrix(:,profindex % tstar) + end if + + if ( profindex % mwemiss(1) > 0) THEN + write(*, '(a)') 'Microwave emissivity retrieval' + do i = profindex%mwemiss(1),profindex%mwemiss(2) + write(*, real_fmt) H_matrix(:,i) + end do + end if + + if ( profindex % cloudtopp > 0 ) THEN + write(*, '(a)') 'Cloud top pressure' + write(*, real_fmt) H_matrix(:,profindex % cloudtopp) + end if + + if ( profindex % cloudfrac > 0 ) THEN + write(*, '(a)') 'Effective cloud fraction' + write(*, real_fmt) H_matrix(:,profindex % cloudfrac) + end if + +write(*,*) +write(*, '(a)') 'End H-Matrix' +write(*, '(a)') '------------------------' + +end subroutine ufo_rttovonedvarcheck_PrintHmatrix + +! ------------------------------------------------------------ + +end module ufo_rttovonedvarcheck_minimize_jacobian_mod diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_ml_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_ml_mod.f90 new file mode 100644 index 000000000..b09b6d57a --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_ml_mod.f90 @@ -0,0 +1,642 @@ +! (C) Copyright 2020 Met Office +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module containing the routines to perform a Marquardt-Levenberg +!! minimization. + +module ufo_rttovonedvarcheck_minimize_ml_mod + +use kinds +use ufo_constants_mod, only: zero, ten +use fckit_log_module, only : fckit_log +use ufo_geovals_mod +use ufo_radiancerttov_mod +use ufo_rttovonedvarcheck_constants_mod +use ufo_rttovonedvarcheck_minimize_jacobian_mod +use ufo_rttovonedvarcheck_minimize_utils_mod +use ufo_rttovonedvarcheck_profindex_mod +use ufo_rttovonedvarcheck_rsubmatrix_mod +use ufo_rttovonedvarcheck_ob_mod +use ufo_rttovonedvarcheck_utils_mod +use ufo_utils_mod, only: Ops_Cholesky + +implicit none +private + +public ufo_rttovonedvarcheck_minimize_ml + +contains + +!------------------------------------------------------------------------------- +!> Perform a minimization using the Marquardt-Levenberg method. +!! +!! \details Heritage: Ops_SatRad_MinimizeML_RTTOV12.f90 +!! +!! Find the most probable atmospheric state vector by minimizing a cost function +!! through a series of iterations. if a solution exists, the iterations will +!! converge when the iterative increments are acceptably small. A limit on the +!! total number of iterations allowed is imposed. +!! +!! Using the formulation given by Rodgers (1976) : +!! +!! Delta_x = xn + (xb-xn).I' + Wn.(ym-y(xn) - H.(xb-xn))
+!! where:
+!! x is an atmospheric state vector, subscripted b=background,n=nth +!! iteration
+!! I' is a diagonal matrix with I'(J,J) = B_damped(J,J)/B_undamped(J,J) +!! although, however, damping will no longer be used
+!! Wn = B.Hn'.(Hn.B.Hn'+R)^-1
+!! B is the background error covariance matrix
+!! R is the combined forward model and ob error covariance matrix
+!! +!! Delta_x is checked for convergence after each iteration +!! +!! The loop is exited with convergence if either of the following conditions are +!! true, depending on whether UseJforConvergence is true or false +!! -# if UseJforConvergence is true then the change in total cost function from +!! one iteration to the next is sufficiently small to imply convergence +!! -# if UseJforConvergence is false then the increments to the atmospheric +!! state vector are sufficiently small to imply convergence at an acceptable +!! solution +!! +!! Either of the following two conditions will cause the 1dvar to stop and exit +!! with an error. +!! -# The increments are sufficiently large to suppose a solution will not +!! be found. +!! -# The maximum number of allowed iterations has been reached. In most +!! cases, one of the above criteria will have occurred. +!! +!! References: +!! +!! Rodgers, Retrieval of atmospheric temperature and composition from +!! remote measurements of thermal radiation, Rev. Geophys.Sp.Phys. 14, 1976. +!! +!! Eyre, Inversion of cloudy satellite sounding radiances by nonlinear +!! optimal estimation. I: Theory and simulation for TOVS,QJ,July 89. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_minimize_ml(self, & + ob, & + r_matrix, & + b_matrix, & + b_inv, & + b_sigma, & + local_geovals, & + hofxdiags, & + rttov_simobs, & + profile_index, & + onedvar_success) + +implicit none + +type(ufo_rttovonedvarcheck), intent(inout) :: self !< structure containing settings +type(ufo_rttovonedvarcheck_ob), intent(inout) :: ob !< satellite metadata +type(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: r_matrix !< observation error covariance +real(kind_real), intent(in) :: b_matrix(:,:) !< state error covariance +real(kind_real), intent(in) :: b_inv(:,:) !< inverse state error covariance +real(kind_real), intent(in) :: b_sigma(:) !< standard deviations of the state error covariance diagonal +type(ufo_geovals), intent(inout) :: local_geovals !< model data at obs location +type(ufo_geovals), intent(inout) :: hofxdiags !< model data containing the jacobian +type(ufo_radiancerttov), intent(inout) :: rttov_simobs !< rttov simulated obs object +type(ufo_rttovonedvarcheck_profindex), intent(in) :: profile_index !< index array for x vector +logical, intent(out) :: onedvar_success !< convergence flag + +! Local declarations: +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_minimize_ml" +integer :: inversionstatus ! status of minimisation +logical :: outOfRange ! out of range flag for check iteration +logical :: Converged ! converged flag +logical :: Error ! error flag +integer :: iter ! iteration counter +integer :: RTerrorcode ! error code for RTTOV +integer :: nchans ! number of satellite channels used +integer :: ii ! counter +integer :: nprofelements ! number of profile elements +real(kind_real) :: Gamma ! steepness of descent parameter +real(kind_real) :: Jcost ! current cost value +real(kind_real) :: JcostOld ! previous iteration cost value +real(kind_real) :: JcostOrig ! initial cost value +real(kind_real) :: DeltaJ ! change in cost during iteration +real(kind_real) :: DeltaJo ! change in cost from original + +real(kind_real), allocatable :: OldProfile(:) +real(kind_real), allocatable :: GuessProfile(:) +real(kind_real), allocatable :: GuessProfileBefore(:) +real(kind_real), allocatable :: BackProfile(:) +real(kind_real), allocatable :: H_matrix(:,:) +real(kind_real), allocatable :: Diffprofile(:) +real(kind_real), allocatable :: AbsDiffProfile(:) +real(kind_real), allocatable :: Ydiff(:) +real(kind_real), allocatable :: Y(:) +real(kind_real), allocatable :: Y0(:) +real(kind_real), allocatable :: out_H_matrix(:,:) +real(kind_real), allocatable :: out_Y(:) +real(kind_real) :: Jout(3) +type(ufo_geovals) :: geovals + +!------------ +! Setup +!------------ + +Converged = .false. +onedvar_success = .false. +Error = .false. +nchans = size(ob % channels_used) +Gamma = 1.0e-4_kind_real +Jcost = 1.0e4_kind_real +JcostOld = 1.0e4_kind_real +nprofelements = profile_index % nprofelements +allocate(OldProfile(nprofelements)) +allocate(GuessProfile(nprofelements)) +allocate(GuessProfileBefore(nprofelements)) +allocate(BackProfile(nprofelements)) +allocate(H_matrix(nchans,nprofelements)) +allocate(Diffprofile(nprofelements)) +allocate(AbsDiffprofile(nprofelements)) +allocate(Ydiff(nchans)) +allocate(Y(nchans)) +allocate(Y0(nchans)) +geovals = local_geovals + +call fckit_log % debug("Using ML solver") + +! Map GeovaLs to 1D-var profile using B matrix profile structure +call ufo_rttovonedvarcheck_GeoVaLs2ProfVec(geovals, profile_index, ob, GuessProfile(:)) + +Iterations: do iter = 1, self % max1DVarIterations + + !------------------------- + ! 1. Generate new profile + !------------------------- + + ! Save current profile + OldProfile(:) = GuessProfile(:) + + ! Get jacobian and new hofx + call ufo_rttovonedvarcheck_get_jacobian(self, geovals, ob, ob % channels_used, & + profile_index, GuessProfile(:), & + hofxdiags, rttov_simobs, Y(:), H_matrix) + + if (iter == 1) then + RTerrorcode = 0 + Diffprofile(:) = zero + BackProfile(:) = GuessProfile(:) + Y0(:) = Y(:) + end if + + ! exit on error + if (RTerrorcode /= 0) then + write(*,*) "Radiative transfer error" + exit Iterations + end if + + ! Calculate Obs diff and initial cost + Ydiff(:) = ob % yobs(:) - Y(:) + if (iter == 1) then + Diffprofile(:) = GuessProfile(:) - BackProfile(:) + call ufo_rttovonedvarcheck_CostFunction(Diffprofile, b_inv, Ydiff, r_matrix, Jout) + Jcost = Jout(1) + JCostOrig = Jcost + end if + + !----------------------------------------------------- + ! 1a. if UseJForConvergence is true + ! then compute costfunction for latest profile + ! and determine convergence using change in cost fn + !----------------------------------------------------- + + if (self % UseJForConvergence) then + + ! exit on error + !if (inversionStatus /= 0) exit Iterations + + ! check for convergence + if (iter > 1) then + + if (self % JConvergenceOption == 1) then + + ! percentage change tested between iterations + DeltaJ = abs ((Jcost - JcostOld) / max (Jcost, tiny (zero))) + + ! default test for checking that overall cost is getting smaller + DeltaJo = -1.0_kind_real + + else + + ! absolute change tested between iterations + DeltaJ = abs (Jcost - JcostOld) + + ! change between current cost and initial + DeltaJo = Jcost - JCostorig + + end if + + if (DeltaJ < self % cost_convergencefactor .and. & + DeltaJo < zero) then ! overall is cost getting smaller? + converged = .true. + exit iterations + end if + + end if + + end if ! end of specific code for cost test convergence + + ! store cost function from previous cycle + JcostOld = Jcost + + ! Iterate (Guess) profile vector + call ufo_rttovonedvarcheck_ML_RTTOV12 ( self, & + Ydiff, & + nChans, & + ob, & + H_Matrix, & + transpose(H_Matrix), & + nprofelements, & + profile_index, & + DiffProfile, & + GuessProfile, & + BackProfile, & + b_inv, & + r_matrix, & + geovals, & + hofxdiags, & + rttov_simobs, & + gamma, & + Jcost, & + inversionStatus) + + if (inversionStatus /= 0) then + inversionStatus = 1 + write(*,*) "inversion failed" + exit Iterations + end if + + !--------------------------------------------------------- + ! 2. Check new profile and transfer to forward model format + !--------------------------------------------------------- + + ! Check profile and constrain humidity variables + call ufo_rttovonedvarcheck_CheckIteration (self, & ! in + geovals, & ! in + profile_index, & ! in + GuessProfile(:), & ! inout + outOfRange) ! out + + ! Update geovals to be the same as guess profile + call ufo_rttovonedvarcheck_ProfVec2GeoVaLs(geovals, profile_index, ob, & + GuessProfile, self % UseQtSplitRain) + + ! if qtotal in retrieval vector check cloud + ! variables for current iteration + + if ((.not. outofRange) .and. profile_index % qt(1) > 0) then + + if (iter >= self % IterNumForLWPCheck) then + + call ufo_rttovonedvarcheck_CheckCloudyIteration( geovals, & ! in + profile_index, & ! in + self % nlevels, & ! in + OutOfRange ) ! out + + end if + + end if + + !------------------------------------------------- + ! 3. Check for convergence using change in profile + ! This is performed if UseJforConvergence is false + !------------------------------------------------- + + absDiffprofile(:) = zero + + if ((.not. outOfRange) .and. (.not. self % UseJForConvergence)) then + absDiffProfile(:) = abs(GuessProfile(:) - OldProfile(:)) + if (ALL (absDiffProfile(:) <= B_sigma(:) * self % ConvergenceFactor)) then + write(*,*) "Profile used for convergence" + Converged = .true. + end if + end if + + !--------------------- + ! 4. output diagnostics + !--------------------- + + if (self % FullDiagnostics) then + write (*, '(A,I0)') 'Iteration', iter + write (*, '(A)') '------------' + write (*, '(A,L1)') 'Status: converged = ', Converged + if (outOfRange) write (*, '(A)') 'exiting with bad increments' + write (*, '(A)') 'New profile:' + call ufo_geovals_print(geovals, 1) + write (*, '(A)') + end if + + ! exit conditions + + if (Converged .OR. outOfRange) exit iterations + +end do Iterations + +! Pass convergence flag out +onedvar_success = converged + +! Pass output profile, final BTs and final cost out +if (converged) then + ob % output_profile(:) = GuessProfile(:) + + ! Recalculate final cost - to make sure output when using profile convergence + call ufo_rttovonedvarcheck_CostFunction(Diffprofile, b_inv, Ydiff, r_matrix, Jout) + ob % final_cost = Jout(1) + + ! If lwp output required then recalculate + if (self % Store1DVarLWP) then + call ufo_rttovonedvarcheck_CheckCloudyIteration( geovals, & ! in + profile_index, & ! in + self % nlevels, & ! in + OutOfRange, & ! out + OutLWP = ob % LWP ) ! out + end if + + ! Recalculate final BTs for all channels + allocate(out_H_matrix(size(ob % channels_all),nprofelements)) + allocate(out_Y(size(ob % channels_all))) + call ufo_rttovonedvarcheck_get_jacobian(self, geovals, ob, ob % channels_all, & + profile_index, GuessProfile(:), & + hofxdiags, rttov_simobs, out_Y(:), out_H_matrix) + ob % output_BT(:) = out_Y(:) + deallocate(out_Y) + deallocate(out_H_matrix) +end if + +!---------------------- +! 4. output diagnostics +!---------------------- + +if (self % UseJForConvergence .and. self % FullDiagnostics) then + write(*,'(A45,3F10.3,I5,L5)') "ML J initial, final, lowest, iter, converged = ", & + JCostorig, Jcost, Jcost, iter, onedvar_success +end if + +! ---------- +! Tidy up +! ---------- +if (allocated(OldProfile)) deallocate(OldProfile) +if (allocated(GuessProfile)) deallocate(GuessProfile) +if (allocated(GuessProfileBefore)) deallocate(GuessProfileBefore) +if (allocated(BackProfile)) deallocate(BackProfile) +if (allocated(H_matrix)) deallocate(H_matrix) +if (allocated(Diffprofile)) deallocate(Diffprofile) +if (allocated(AbsDiffprofile)) deallocate(AbsDiffprofile) +if (allocated(Ydiff)) deallocate(Ydiff) +if (allocated(Y)) deallocate(Y) +if (allocated(Y0)) deallocate(Y0) + +end subroutine ufo_rttovonedvarcheck_minimize_ml + +!--------------------------------------------------------------------- +!> Updates the profile vector during the Marquardt-Levenberg +!! minimization. +!! +!! \details Heritage: Ops_SatRad_MarquardtLevenberg_RTTOV12.f90 +!! +!! Updates the profile vector DeltaProfile according to Rodgers (1976), Eqn. 100, +!! extended to allow for additional cost function terms and Marquardt-Levenberg +!! descent. +!! +!! x_(n+1) = xb + U^-1.V
+!! where
+!! U=(B^-1 + H^T R^-1 H + J2)
+!! V=H^T R^-1 [(ym-y(x_n))+H(x_n-xb)] - J1
+!! and
+!! J_extra=J0+J1.(x-xb)+(x-xb)^T.J2.(x-xb) is the additional cost +!! function
+!! x is an atmospheric state vector, subscripted b=background,n=nth +!! iteration
+!! ym is the measurement vector (i.e. observed brightness temperatures)
+!! y(xn) is the observation vector calculated for xn
+!! ym and y(xn) are not used individually at all, hence these are input
+!! as a difference vector DeltaBT.
+!! B is the background error covariance matrix
+!! R is the combined forward model and ob error covariance matrix
+!! H is the forward model gradient (w.r.t. xn) matrix
+!! H' is the transpose of H
+!! +!! When J_extra is zero this is simply Rogers (1976), Eqn. 100. +!! +!! U^-1.V is solved using Cholesky decomposition. +!! +!! This routine should be used when: +!! -# The length of the observation vector is greater than the length +!! of the state vector, +!! -# Additional cost function terms are provided and +!! -# where Newtonian minimisation is desired. +!! +!! References: +!! +!! Rodgers, Retrieval of atmospheric temperature and composition from +!! remote measurements of thermal radiation, Rev. Geophys.Sp.Phys. 14, 1976. +!! +!! Rodgers, Inverse Methods for Atmospheres: Theory and Practice. World +!! Scientific Publishing, 2000. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_ML_RTTOV12 ( self, & + DeltaBT, & + nChans, & + ob, & + H_Matrix, & + H_Matrix_T, & + nprofelements, & + profile_index, & + DeltaProfile, & + GuessProfile, & + BackProfile, & + b_inv, & + r_matrix, & + geovals, & + hofxdiags, & + rttov_simobs, & + gamma, & + Jold, & + Status) + +implicit none + +! subroutine arguments: +type(ufo_rttovonedvarcheck), intent(in) :: self !< structure containing settings +real(kind_real), intent(in) :: DeltaBT(:) !< y-y(x) +integer, intent(in) :: nChans !< number of channels +type(ufo_rttovonedvarcheck_ob), intent(inout) :: ob !< satellite metadata +real(kind_real), intent(in) :: H_Matrix(:,:) !< Jacobian +real(kind_real), intent(in) :: H_Matrix_T(:,:) !< transpose of the Jacobian +integer, intent(in) :: nprofelements !< number of profile elements +type(ufo_rttovonedvarcheck_profindex), intent(in) :: profile_index !< index array for x vector +real(kind_real), intent(inout) :: DeltaProfile(:) !< x_(n+1) - xb +real(kind_real), intent(inout) :: GuessProfile(:) !< x_(n+1) +real(kind_real), intent(in) :: BackProfile(:) !< xb +real(kind_real), intent(in) :: b_inv(:,:) !< inverse of the state error covariance +type(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: r_matrix !< observation error covariance +type(ufo_geovals), intent(inout) :: geovals !< model data at obs location +type(ufo_geovals), intent(inout) :: hofxdiags !< model data containing the jacobian +type(ufo_radiancerttov), intent(inout) :: rttov_simobs +real(kind_real), intent(inout) :: gamma !< steepness of descent parameter +real(kind_real), intent(inout) :: Jold !< previous steps cost which gets update by good step +integer, intent(out) :: Status !< code to capture failed Cholesky decomposition + +! Local declarations: +character(len=*), parameter :: RoutineName = 'ufo_rttovonedvarcheck_ML_RTTOV12' +integer :: i +integer :: Iter_ML ! M-L iteration count +real(kind_real) :: JOut(3) ! J, Jb, Jo +real(kind_real) :: JCost ! Cost function, +logical :: OutOfRange +real(kind_real) :: HTR(nprofelements, nChans) ! Scratch vector +real(kind_real) :: U(nprofelements, nprofelements) ! U = H.B.H^T + R +real(kind_real) :: V(nprofelements) ! V = (y-y(x_n))-H^T(xb-x_n) +real(kind_real) :: USave(nprofelements, nprofelements) ! U = H.B.H^T + R +real(kind_real) :: VSave(nprofelements) ! V = (y-y(x_n))-H^T(xb-x_n) +real(kind_real) :: New_DeltaProfile(nprofelements) +real(kind_real) :: Tau_Surf(nchans) ! Transmittance from surface +real(kind_real) :: BriTemp(nchans) ! Forward modelled brightness temperatures +real(kind_real) :: Ydiff(nchans) +real(kind_real) :: Emiss(nchans) +real(kind_real) :: H_matrix_tmp(nchans,nprofelements) +logical :: CalcEmiss(nchans) +integer :: StatusOK = 0 +integer :: RTStatus = 0 + +Status = StatusOK + +!--------------------------------------------------------------------------- +! 1. Calculate HTR-1 for the three allowed forms of R matrix. if required, the +! matrix is tested to determine whether it is stored as an inverse and +! inverted if not. +!--------------------------------------------------------------------------- + +!HTR(-1) = matmul(H_matrix_T, R_inverse) +call r_matrix % multiply_inverse_matrix(H_matrix_T, HTR) + +!--------------------------------------------------------------------------- +! 2. Calculate U and V +!--------------------------------------------------------------------------- + +U = matmul (HTR, H_matrix) +V = matmul (HTR, DeltaBT) +V = V + matmul (U, DeltaProfile) + +!--------------------------------------------------------------------------- +! 3. Add on inverse of B-matrix to U. +!--------------------------------------------------------------------------- + +U = U + b_inv + +!--------------------------------------------------------------------------- +! 5. Start Marquardt-Levenberg loop. +! Search for a value of gamma that reduces the cost function. +!--------------------------------------------------------------------------- + +JCost = 1.0e10_kind_real +Gamma = Gamma / ten +USave = U +VSave = V +Iter_ML = 0 + +DescentLoop : do while (JCost > JOld .and. & + Iter_ML < self % MaxMLIterations .and. & + Gamma <= 1.0e25_kind_real) + + Iter_ML = Iter_ML + 1 + + !------------------------------------------------------------------------ + ! 5.1 Add on Marquardt-Levenberg terms to U and V. + !------------------------------------------------------------------------ + + Gamma = Gamma * ten + + do I = 1, nprofelements + U(I,I) = USave(I,I) + Gamma + end do + V = VSave + Gamma * DeltaProfile + + !------------------------------------------------------------------------ + ! 5.2 Calculate new profile increment. + !------------------------------------------------------------------------ + + call Ops_Cholesky (U, & + V, & + nprofelements, & + New_DeltaProfile, & + Status) + if (Status /= 0) then + write(*,*) 'Error in Cholesky decomposition' + end if + + !------------------------------------------------------------------------ + ! 5.3. Calculate the radiances for the new profile. + !------------------------------------------------------------------------ + + GuessProfile(:) = BackProfile(:) + New_DeltaProfile(:) + + call ufo_rttovonedvarcheck_CheckIteration (self, & ! in + geovals, & ! in + profile_index, & ! in + GuessProfile(:), & ! inout + outOfRange) ! out + + if (.not. OutOfRange) then + call ufo_rttovonedvarcheck_ProfVec2GeoVaLs(geovals, profile_index, ob, & + GuessProfile, self % UseQtSplitRain) + + !Emiss(1:nchans) = RTprof_Guess % Emissivity(Channels(1:nchans)) + !CalcEmiss(1:nchans) = RTprof_Guess % CalcEmiss(Channels(1:nchans)) + + ! Get Jabobian and new hofx + call ufo_rttovonedvarcheck_get_jacobian(self, geovals, ob, ob % channels_used, & + profile_index, GuessProfile(:), & + hofxdiags, rttov_simobs, BriTemp(:), H_matrix_tmp) + + !------------------------------------------------------------------------ + ! 5.6. Calculate the new cost function. + !------------------------------------------------------------------------ + + if (RTStatus > 0) then + + ! In case of RT error, set cost to large value and + ! continue with next iteration + + Jcost = 1.0e10_kind_real + else + DeltaProfile(:) = GuessProfile(:) - BackProfile(:) + Ydiff(:) = ob % yobs(:) - BriTemp(:) + call ufo_rttovonedvarcheck_CostFunction(DeltaProfile, b_inv, Ydiff, r_matrix, Jout) + Jcost = Jout(1) + if (Status /= StatusOK) goto 9999 + end if + + else + + ! if profile out of range, set cost to large value and + ! continue with next iteration + + Jcost = 1.0e10_kind_real + + end if + +end do DescentLoop + +DeltaProfile = New_DeltaProfile +Gamma = Gamma / ten +JOld = JCost + +9999 continue + +end subroutine ufo_rttovonedvarcheck_ML_RTTOV12 + +end module ufo_rttovonedvarcheck_minimize_ml_mod diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_newton_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_newton_mod.f90 new file mode 100644 index 000000000..ebe829e42 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_newton_mod.f90 @@ -0,0 +1,694 @@ +! (C) Copyright 2020 Met Office +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to perform newton steepest descent minimization + +module ufo_rttovonedvarcheck_minimize_newton_mod + +use kinds +use ufo_constants_mod, only: zero +use fckit_log_module, only : fckit_log +use ufo_geovals_mod +use ufo_radiancerttov_mod +use ufo_rttovonedvarcheck_constants_mod +use ufo_rttovonedvarcheck_minimize_jacobian_mod +use ufo_rttovonedvarcheck_minimize_utils_mod +use ufo_rttovonedvarcheck_ob_mod +use ufo_rttovonedvarcheck_profindex_mod +use ufo_rttovonedvarcheck_rsubmatrix_mod +use ufo_rttovonedvarcheck_utils_mod +use ufo_utils_mod, only: Ops_Cholesky + +implicit none +private + +! public subroutines +public ufo_rttovonedvarcheck_minimize_newton + +contains + +!------------------------------------------------------------------------------ +!> Get the jacobian used in the 1D-Var. +!! +!! \details Heritage: Ops_SatRad_MinimizeNewton_RTTOV12.f90 +!! +!! Find the most probable atmospheric state vector by minimizing a cost function +!! through a series of iterations. if a solution exists, the iterations will +!! converge when the iterative increments are acceptably small. A limit on the +!! total number of iterations allowed, is imposed. +!! +!! Using the formulation given by Rodgers (1976) : +!! +!! Delta_x = xn + (xb-xn).I' + Wn.(ym-y(xn) - H.(xb-xn))
+!! where:
x is an atmospheric state vector, subscripted b=background,n=nth +!! iteration
+!! I' is a diagonal matrix with I'(J,J) = B_damped(J,J)/B_undamped(J,J) +!! although, however, damping will no longer be used
+!! Wn = B.Hn'.(Hn.B.Hn'+R)^-1
+!! B is the background error covariance matrix
+!! R is the combined forward model and ob error covariance matrix
+!! +!! Delta_x is checked for convergence after each iteration +!! +!! The loop is exited with convergence if either of the following conditions are +!! true, depending on whether UseJforConvergence is true or false
+!! -# if UseJforConvergence is true then the change in total cost function from +!! one iteration to the next is sufficiently small to imply convergence +!! -# if UseJforConvergence is false then the increments to the atmospheric +!! state vector are sufficiently small to imply convergence at an acceptable +!! solution
+!! Either of the following two conditions will cause the 1dvar to stop and exit +!! with an error.
+!! -# The increments are sufficiently large to suppose a solution will not +!! be found. +!! -# The maximum number of allowed iterations has been reached. in most +!! cases, one of the above criteria will have occurred. +!! +!! References: +!! +!! Rodgers, Retrieval of atmospheric temperature and composition from +!! remote measurements of thermal radiation, Rev. Geophys.Sp.Phys. 14, 1976. +!! +!! Eyre, inversion of cloudy satellite sounding radiances by nonlinear +!! optimal estimation. I: Theory and simulation for TOVS,QJ,July 89. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_minimize_newton(self, & + ob, & + r_matrix, & + b_matrix, & + b_inv, & + b_sigma, & + local_geovals, & + hofxdiags, & + rttov_simobs, & + profile_index, & + onedvar_success) + +implicit none + +type(ufo_rttovonedvarcheck), intent(inout) :: self !< Main 1D-Var object +type(ufo_rttovonedvarcheck_ob), intent(inout) :: ob !< satellite metadata +type(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: r_matrix !< observation error covariance +real(kind_real), intent(in) :: b_matrix(:,:) !< state error covariance +real(kind_real), intent(in) :: b_inv(:,:) !< inverse of the state error covariance +real(kind_real), intent(in) :: b_sigma(:) !< standard deviations of the state error covariance diagonal +type(ufo_geovals), intent(inout) :: local_geovals !< model data at obs location +type(ufo_geovals), intent(inout) :: hofxdiags !< model data containing the jacobian +type(ufo_radiancerttov), intent(inout) :: rttov_simobs +type(ufo_rttovonedvarcheck_profindex), intent(in) :: profile_index !< index array for x vector +logical, intent(out) :: onedvar_success !< convergence flag + +! Local declarations: +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_minimize_newton" +integer :: inversionstatus +logical :: outOfRange +logical :: Converged +logical :: Error +integer :: iter +integer :: RTerrorcode +integer :: nchans +integer :: nprofelements +real(kind_real) :: Jcost ! current value +real(kind_real) :: JcostOld ! previous iteration value +real(kind_real) :: JcostOrig ! initial value +real(kind_real) :: DeltaJ +real(kind_real) :: DeltaJo + +real(kind_real), allocatable :: OldProfile(:) +real(kind_real), allocatable :: GuessProfile(:) +real(kind_real), allocatable :: BackProfile(:) +real(kind_real), allocatable :: H_matrix(:,:) +real(kind_real), allocatable :: Diffprofile(:) +real(kind_real), allocatable :: AbsDiffProfile(:) +real(kind_real), allocatable :: Xdiff(:) +real(kind_real), allocatable :: Ydiff(:) +real(kind_real), allocatable :: Y(:) +real(kind_real), allocatable :: Y0(:) +real(kind_real), allocatable :: out_H_matrix(:,:) +real(kind_real), allocatable :: out_Y(:) +type(ufo_geovals) :: geovals +real(kind_real) :: Jout(3) + +integer :: ii, jj + +! --------- +! Setup +! --------- +Converged = .false. +onedvar_success = .false. +Error = .false. +nchans = size(ob % channels_used) +inversionstatus = 0 +nprofelements = profile_index % nprofelements +allocate(OldProfile(nprofelements)) +allocate(GuessProfile(nprofelements)) +allocate(BackProfile(nprofelements)) +allocate(H_matrix(nchans,nprofelements)) +allocate(Diffprofile(nprofelements)) +allocate(AbsDiffprofile(nprofelements)) +allocate(Xdiff(nprofelements)) +allocate(Ydiff(nchans)) +allocate(Y(nchans)) +allocate(Y0(nchans)) +geovals = local_geovals + +if (self % FullDiagnostics) call ufo_geovals_print(geovals,1) +call fckit_log % debug("Using Newton solver") + +JCost = 1.0e4_kind_real + +Iterations: do iter = 1, self % max1DVarIterations + + !------------------------- + ! 1. Generate new profile + !------------------------- + ! Save cost from previous iteration + if (self % UseJForConvergence) then + JcostOld = Jcost + end if + + ! initialise RTerrorcode and profile increments on first iteration + if (iter == 1) then + + RTerrorcode = 0 + Diffprofile(:) = zero + JcostOld = 1.0e4_kind_real + + ! Map GeovaLs to 1D-var profile using B matrix profile structure + call ufo_rttovonedvarcheck_GeoVaLs2ProfVec(geovals, profile_index, & + ob, GuessProfile(:)) + + if (self % FullDiagnostics) & + write(*,*) "Humidity GuessProfile 1st iteration = ",GuessProfile(profile_index % qt(1):profile_index % qt(2)) + + end if + + ! Save current profile + OldProfile(:) = GuessProfile(:) + + ! Get jacobian and hofx + call ufo_rttovonedvarcheck_get_jacobian(self, geovals, ob, ob % channels_used, & + profile_index, GuessProfile(:), & + hofxdiags, rttov_simobs, Y(:), H_matrix) + + if (iter == 1) then + BackProfile(:) = GuessProfile(:) + Y0(:) = Y(:) + do ii = 1, size(ob % channels_all) + do jj = 1, size(ob % channels_used) + if (ob % channels_all(ii) == ob % channels_used(jj)) then + ob % background_BT(ii) = Y0(jj) + end if + end do + end do + end if + + ! exit on error + if (RTerrorcode /= 0) then + write(*,*) "Radiative transfer error" + exit Iterations + end if + + !----------------------------------------------------- + ! 1a. if UseJForConvergence is true + ! then compute costfunction for latest profile + ! and determine convergence using change in cost fn + !----------------------------------------------------- + + ! Profile differences + Xdiff(:) = GuessProfile(:) - BackProfile(:) + Ydiff(:) = ob % yobs(:) - Y(:) + + if (self % FullDiagnostics) then + write(*,*) "Ob BT = " + write(*,'(10F10.3)') ob % yobs(:) + write(*,*) "HofX BT = " + write(*,'(10F10.3)') Y(:) + end if + + if (self % UseJForConvergence) then + + call ufo_rttovonedvarcheck_CostFunction(Xdiff, b_inv, Ydiff, r_matrix, Jout) + Jcost = Jout(1) + + ! exit on error + if (inversionStatus /= 0) exit Iterations + + ! store initial cost value + if (iter == 1) JCostOrig = jcost + + ! check for convergence + if (iter > 1) then + + if (self % JConvergenceOption == 1) then + + ! percentage change tested between iterations + DeltaJ = abs ((Jcost - JcostOld) / max (Jcost, tiny (zero))) + + ! default test for checking that overall cost is getting smaller + DeltaJo = -1.0_kind_real + + else + + ! absolute change tested between iterations + DeltaJ = abs (Jcost - JcostOld) + + ! change between current cost and initial + DeltaJo = Jcost - JCostorig + + end if + + if (self % FullDiagnostics) THEN + write (*, '(A,F12.5)') 'Cost Function = ', Jcost + write (*, '(A,F12.5)') 'Cost Function old = ', JcostOld + write (*, '(A,F12.5)') 'Cost Function Increment = ', deltaj + end if + + if (DeltaJ < self % cost_convergencefactor .and. & + DeltaJo < zero) then ! overall is cost getting smaller? + converged = .true. + if (self % FullDiagnostics) then + write (*, '(A,I0)') 'Iteration', iter + write (*, '(A)') '------------' + write (*, '(A,L1)') 'Status: converged = ', Converged + write (*, '(A)') 'New profile:' + call ufo_geovals_print(geovals, 1) + write (*, '(A)') + write (*, '(A,3F12.5)') 'Cost Function, increment, cost_convergencefactor = ', & + Jcost, deltaj, self % cost_convergencefactor + end if + exit iterations + end if + + end if + + end if ! end of specific code for cost test convergence + + ! Iterate (Guess) profile vector + if (nchans > nprofelements) then + call fckit_log % debug("Many Chans") + call ufo_rttovonedvarcheck_NewtonManyChans (Ydiff, & + nchans, & + H_matrix(:,:), & ! in + transpose (H_matrix(:,:)), & ! in + nprofelements, & + Diffprofile, & + b_inv, & + r_matrix, & + inversionStatus) + else ! nchans <= nprofelements + call fckit_log % debug("Few Chans") + call ufo_rttovonedvarcheck_NewtonFewChans (Ydiff, & + nchans, & + H_matrix(:,:), & ! in + transpose (H_matrix(:,:)), & ! in + nprofelements, & + Diffprofile, & + b_matrix, & + r_matrix, & + inversionStatus) + + if (self % FullDiagnostics) then + write(*,*) "After newton fewchans start" + call ufo_rttovonedvarcheck_PrintIterInfo(ob % yobs(:), Y(:), & + ob % channels_used, guessprofile, & + backprofile, diffprofile, b_inv, h_matrix ) + write(*,*) "After newton fewchans finish" + end if + + end if + + if (inversionStatus /= 0) then + inversionStatus = 1 + write(*,*) "inversion failed" + exit Iterations + end if + + GuessProfile(:) = BackProfile(:) + Diffprofile(:) + + !--------------------------------------------------------- + ! 2. Check new profile and transfer to forward model format + !--------------------------------------------------------- + + ! Check profile and constrain humidity variables + call ufo_rttovonedvarcheck_CheckIteration (self, & ! in + geovals, & ! in + profile_index, & ! in + GuessProfile(:), & ! inout + outOfRange) ! out + + ! Update geovals with guess profile + call ufo_rttovonedvarcheck_ProfVec2GeoVaLs(geovals, profile_index, & + ob, GuessProfile, self % UseQtSplitRain) + + ! if qtotal in retrieval vector check cloud + ! variables for current iteration + + if ((.NOT. outofRange) .and. profile_index % qt(1) > 0) then + + if (iter >= self % IterNumForLWPCheck) then + + call ufo_rttovonedvarcheck_CheckCloudyIteration( geovals, & ! in + profile_index, & ! in + self % nlevels, & ! in + OutOfRange ) ! out + + end if + + end if + + !------------------------------------------------- + ! 3. Check for convergence using change in profile + ! This is performed if UseJforConvergence is false + !------------------------------------------------- + + absDiffprofile(:) = zero + + if ((.NOT. outOfRange) .and. (.NOT. self % UseJForConvergence))then + absDiffProfile(:) = abs(GuessProfile(:) - OldProfile(:)) + if (ALL (absDiffProfile(:) <= B_sigma(:) * self % ConvergenceFactor)) then + call fckit_log % debug("Profile used for convergence") + Converged = .true. + end if + end if + + !--------------------- + ! 4. output diagnostics + !--------------------- + + if (self % FullDiagnostics) then + write (*, '(A,I0)') 'Iteration', iter + write (*, '(A)') '------------' + write (*, '(A,L1)') 'Status: converged = ', Converged + if (outOfRange) write (*, '(A)') 'exiting with bad increments' + write (*, '(A)') 'New profile:' + call ufo_geovals_print(geovals, 1) + write (*, '(A)') + end if + + ! exit conditions + + if (Converged .OR. outOfRange) exit iterations + +end do Iterations + +! Pass convergence flag out +onedvar_success = converged + +! Recalculate final cost - to make sure output when profile has not converged +call ufo_rttovonedvarcheck_CostFunction(Xdiff, b_inv, Ydiff, r_matrix, Jout) +ob % final_cost = Jout(1) +ob % niter = iter + +! Pass output profile, final BTs and final cost out +if (converged) then + ob % output_profile(:) = GuessProfile(:) + + ! Recalculate final cost - to make sure output when using profile convergence + call ufo_rttovonedvarcheck_CostFunction(Diffprofile, b_inv, Ydiff, r_matrix, Jout) + ob % final_cost = Jout(1) + + ! If lwp output required then recalculate + if (self % Store1DVarLWP) then + call ufo_rttovonedvarcheck_CheckCloudyIteration( geovals, & ! in + profile_index, & ! in + self % nlevels, & ! in + OutOfRange, & ! out + OutLWP = ob % LWP ) ! out + end if + + ! Recalculate final BTs for all channels + allocate(out_H_matrix(size(ob % channels_all),nprofelements)) + allocate(out_Y(size(ob % channels_all))) + call ufo_rttovonedvarcheck_get_jacobian(self, geovals, ob, ob % channels_all, & + profile_index, GuessProfile(:), & + hofxdiags, rttov_simobs, out_Y(:), out_H_matrix) + ob % output_BT(:) = out_Y(:) + deallocate(out_Y) + deallocate(out_H_matrix) +end if + +!--------------------- +! 4. output diagnostics +!--------------------- + +if (self % UseJForConvergence .and. self % FullDiagnostics) then + write(*,'(A70,3F10.3,I5,2L5)') "Newton J initial, final, lowest, iter, converged, outofrange = ", & + JCostorig, Jcost, Jcost, iter, onedvar_success, outOfRange +end if + +! ---------- +! Tidy up +! ---------- +if (allocated(OldProfile)) deallocate(OldProfile) +if (allocated(GuessProfile)) deallocate(GuessProfile) +if (allocated(BackProfile)) deallocate(BackProfile) +if (allocated(H_matrix)) deallocate(H_matrix) +if (allocated(Diffprofile)) deallocate(Diffprofile) +if (allocated(AbsDiffprofile)) deallocate(AbsDiffprofile) +if (allocated(Xdiff)) deallocate(Xdiff) +if (allocated(Ydiff)) deallocate(Ydiff) +if (allocated(Y)) deallocate(Y) +if (allocated(Y0)) deallocate(Y0) + +call fckit_log % debug("finished with ufo_rttovonedvarcheck_minimize_newton") + +end subroutine ufo_rttovonedvarcheck_minimize_newton + +!------------------------------------------------------------------------------ +!> Update the profile if newber of channels is less than number of elements in +!! the profile +!! +!! \details Heritage: Ops_SatRad_NewtonFewChans.f90 +!! +!! Updates the profile vector DeltaProfile according to Rodgers (1976), Eqn. 101: +!! +!! x_(n+1) = xb + B.Hn'.Q
+!! Q = U^-1.V
+!! where:
x is an atmospheric state vector, subscripted b=background,n=nth +!! iteration
+!! U = (Hn.B.Hn'+R)
+!! V = (ym-y(xn) - H.(xb-xn))
+!! ym is the measurement vector (i.e. observed brightness temperatures)
+!! y(xn) is the observation vector calculated for xn
+!! ym and y(xn) are not used individually at all, hence these are input +!! as a difference vector DeltaBT.
+!! B is the background error covariance matrix
+!! R is the combined forward model and ob error covariance matrix
+!! H is the forward model gradient (w.r.t. xn) matrix
+!! H' is the transpose of H
+!! +!! Q = U^-1.V is solved by Cholesky decomposition. +!! +!! This routine should be used when:
+!! -# The length of the observation vector is less than the length +!! of the state vector. +!! +!! Note on input/output variable DeltaProfile: +!! +!! On input, DeltaProfile is x(n-1)-xb.
+!! in construction of variable v, the sign is reversed:
+!! V = (ym-y(xn) + H.(xn-xb)) -- see equation in description above.
+!! On output, DeltaProfile is xn-xb and should be ADDED to the background +!! profile +!! +!! References: +!! +!! Rodgers, Retrieval of atmospheric temperature and composition from +!! remote measurements of thermal radiation, Rev. Geophys.Sp.Phys. 14, 1976. +!! +!! Rodgers, inverse Methods for Atmospheres: Theory and Practice. World +!! Scientific Publishing, 2000. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_NewtonFewChans (DeltaBT, & + nChans, & + H_Matrix, & + H_Matrix_T, & + nprofelements, & + DeltaProfile, & + B_matrix, & + r_matrix, & + Status) + +implicit none + +! subroutine arguments: +real(kind_real), intent(in) :: DeltaBT(:) !< y-y(x) +integer, intent(in) :: nChans !< number of channels +real(kind_real), intent(in) :: H_Matrix(:,:) !< Jacobian +real(kind_real), intent(in) :: H_Matrix_T(:,:) !< (Jacobian)^T +integer, intent(in) :: nprofelements !< number of elements in x profile +real(kind_real), intent(inout) :: DeltaProfile(:) !< x-xb +real(kind_real), intent(in) :: B_matrix(:,:) !< state error covariance +type(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: r_matrix !< observation error covariance +integer, intent(out) :: Status !< check if Cholesky decomposition fails + +! Local declarations: +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_NewtonFewChans" +integer :: Element +integer :: i +real(kind_real) :: HB(nChans,nprofelements) ! Scratch vector +real(kind_real) :: Q(nChans) ! Q = U^-1.V +real(kind_real) :: U(nChans,nChans) ! U = H.B.H^T + R +real(kind_real) :: V(nChans) ! V = (y-y(x_n))-H^T(xb-x_n) + +Status = 0 + +!--------------------------------------------------------------------------- +! 1. Calculate the U and V vectors for the three forms of R matrix allowed +! for. +!--------------------------------------------------------------------------- +HB = matmul(H_matrix, B_matrix) +U = matmul(HB, H_matrix_T) + +! Plus sign is not in error - it reverses sign of DeltaProfile to change +! from xn-xb to xb-xn + +V = DeltaBT + matmul(H_matrix, DeltaProfile) + +!--------------------------------------------------------------------------- +! 1.1. Add the R matrix into the U matrix. U = U + R +!--------------------------------------------------------------------------- +!U = U + R_Matrix +call r_matrix % add_to_matrix(U, U) + +! Calculate Q=(U^-1).V +!------ + +call Ops_Cholesky (U, & + V, & + nChans, & + Q, & + Status) +if (Status /= 0) goto 9999 + +! Delta profile is (HB)^T.Q +!------ + +DeltaProfile = matmul(transpose(HB), Q) + +9999 continue +end subroutine ufo_rttovonedvarcheck_NewtonFewChans + +!------------------------------------------------------------------------------ +!> Update the profile if number of channels is more than number of elements in +!! the profile +!! +!! \details Heritage: Ops_SatRad_NewtonManyChans.f90 +!! +!! Updates the profile vector Delta_Profile according to Rodgers (1976), Eqn. +!! 100, extended to allow for additional cost function terms. +!! +!! x_(n+1) = xb + U^-1.V
+!! where
U=(B^-1 + H^T R^-1 H + J2)
+!! V=H^T R^-1 [(ym-y(x_n))+H(x_n-xb)] - J1
+!! and
J_extra=J0+J1.(x-xb)+(x-xb)^T.J2.(x-xb) is the additional cost +!! function
+!! x is an atmospheric state vector, subscripted b=background,n=nth +!! iteration
+!! ym is the measurement vector (i.e. observed brightness temperatures)
+!! y(xn) is the observation vector calculated for xn
+!! ym and y(xn) are not used individually at all, hence these are input +!! as a difference vector DeltaBT.
+!! B is the background error covariance matrix
+!! R is the combined forward model and ob error covariance matrix
+!! H is the forward model gradient (w.r.t. xn) matrix
+!! H' is the transpose of H
+!! +!! When J_extra is zero this is simply Rogers (1976), Eqn. 100. +!! +!! U^-1.V is solved using Cholesky decomposition. +!! +!! This routine should be used when: +!! -# The length of the observation vector is greater than the length +!! of the state vector +!! +!! References: +!! +!! Rodgers, Retrieval of atmospheric temperature and composition from +!! remote measurements of thermal radiation, Rev. Geophys.Sp.Phys. 14, 1976. +!! +!! Rodgers, inverse Methods for Atmospheres: Theory and Practice. World +!! Scientific Publishing, 2000. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_NewtonManyChans (DeltaBT, & + nChans, & + H_Matrix, & + H_Matrix_T, & + nprofelements, & + DeltaProfile, & + B_inverse, & + r_matrix, & + Status) + +implicit none + +! subroutine arguments: +real(kind_real), intent(in) :: DeltaBT(:) !< y-y(x) +integer, intent(in) :: nChans !< number of channels +real(kind_real), intent(in) :: H_Matrix(:,:) !< Jacobian +real(kind_real), intent(in) :: H_Matrix_T(:,:) !< (Jacobian)^T +integer, intent(in) :: nprofelements !< number of elements in profile vector +real(kind_real), intent(inout) :: DeltaProfile(:) !< x-xb +real(kind_real), intent(in) :: B_inverse(:,:) !< inverse state error covariance +type(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: r_matrix !< observation error covariance +integer, intent(out) :: Status !< check if Cholesky decomposition fails + +! Local declarations: +character(len=*), parameter :: RoutineName = 'ufo_rttovonedvarcheck_NewtonManyChans' +real(kind_real) :: HTR(nprofelements, nChans) ! Scratch vector +real(kind_real) :: U(nprofelements, nprofelements) ! U = H.B.H^T + R +real(kind_real) :: V(nprofelements) ! V = (y-y(x_n))-H^T(xb-x_n) + +Status = 0 + +!--------------------------------------------------------------------------- +! 1. Calculate HTR for the three allowed forms of R matrix. if required, the +! matrix is tested to determine whether it is stored as an inverse and +! inverted if not. +!--------------------------------------------------------------------------- +!HTR = matmul(H_matrix_T, R_inverse) +call r_matrix % multiply_inverse_matrix(H_matrix_T,HTR) + +!--------------------------------------------------------------------------- +! 2. Calculate U and V +!--------------------------------------------------------------------------- + +U = matmul(HTR, H_matrix) +V = matmul(HTR, DeltaBT) +V = V + matmul(U, DeltaProfile) + +!--------------------------------------------------------------------------- +! 3. Add on inverse of B-matrix to U. +!--------------------------------------------------------------------------- + +U = U + B_inverse + +!--------------------------------------------------------------------------- +! 5. Calculate new profile increment. +!--------------------------------------------------------------------------- + +call Ops_Cholesky (U, & + V, & + nprofelements, & + DeltaProfile, & + Status) + +end subroutine ufo_rttovonedvarcheck_NewtonManyChans + +!--------------------------------------------------- + +end module ufo_rttovonedvarcheck_minimize_newton_mod diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_utils_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_utils_mod.f90 new file mode 100644 index 000000000..e87930860 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_minimize_utils_mod.f90 @@ -0,0 +1,1238 @@ +! (C) Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module containing subroutines used by both minimizers. + +module ufo_rttovonedvarcheck_minimize_utils_mod + +use kinds +use ufo_constants_mod, only: grav, zero, t0c, half, one, two, min_q +use fckit_log_module, only : fckit_log +use ufo_geovals_mod +use ufo_rttovonedvarcheck_constants_mod +use ufo_rttovonedvarcheck_ob_mod +use ufo_rttovonedvarcheck_profindex_mod +use ufo_rttovonedvarcheck_rsubmatrix_mod +use ufo_rttovonedvarcheck_utils_mod, only: ufo_rttovonedvarcheck +use ufo_vars_mod +use ufo_utils_mod, only: Ops_SatRad_Qsplit, Ops_QSat, Ops_QSatWat, cmp_strings + +implicit none +private + +! subroutines - public +public ufo_rttovonedvarcheck_GeoVaLs2ProfVec +public ufo_rttovonedvarcheck_ProfVec2GeoVaLs +public ufo_rttovonedvarcheck_check_geovals +public ufo_rttovonedvarcheck_CostFunction +public ufo_rttovonedvarcheck_CheckIteration +public ufo_rttovonedvarcheck_CheckCloudyIteration +public ufo_rttovonedvarcheck_PrintIterInfo + +character(len=max_string) :: message + +contains + +!------------------------------------------------------------------------------- +!> Copy geovals data (and ob) to profile. +!! +!! \details Heritage: Ops_SatRad_RTprof2Vec_RTTOV12.f90 +!! +!! Convert profile data from the GeoVaLs format (and ob) into the minimisation +!! format vector profile. We only copy fields that are being retrieved, as indicated by +!! the profindex structure. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_GeoVaLs2ProfVec( geovals, & ! in + profindex, & ! in + ob, & ! in + prof_x ) ! out + +implicit none + +! subroutine arguments: +type(ufo_geovals), intent(in) :: geovals !< model data at obs location +type(ufo_rttovonedvarcheck_profindex), intent(in) :: profindex !< index array for x vector +type(ufo_rttovonedvarcheck_ob), intent(in) :: ob !< satellite metadata +real(kind_real), intent(out) :: prof_x(:) !< x vector + +! Local arguments: +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_GeoVaLs2ProfVec" +character(len=max_string) :: varname + +type(ufo_geoval), pointer :: geoval +integer :: nlevels +integer :: ii +real(kind_real), allocatable :: humidity_total(:) +real(kind_real), allocatable :: emiss_pc(:) +real(kind_real) :: u, v ! components for windspeed calculation + +!------------------------------------------------------------------------------- + +prof_x(:) = zero +nlevels = profindex % nlevels + +!------------------------------ +! 2. Set multi-level variables +!------------------------------ + +! Note: GeoVaLs are surface -> TOA; prof_x is TOA -> surface + +! Note that if number of profile levels is less than number of pressure levels +! we assume the levels are from the surface upwards (remember that RTTOV levels +! are upside-down) + +! var_ts - air_temperature - K +if (profindex % t(1) > 0) then + call ufo_geovals_get_var(geovals, var_ts, geoval) + prof_x(profindex % t(1):profindex % t(2)) = geoval%vals(nlevels:1:-1, 1) ! K +end if + +! var_q - specific_humidity - kg/kg +! for retrieval is ln(g/kg) +if (profindex % q(1) > 0) then + call ufo_geovals_get_var(geovals, var_q, geoval) + prof_x(profindex % q(1):profindex % q(2)) = & + log (geoval%vals(nlevels:1:-1, 1) * 1000.0_kind_real) ! ln(g/kg) +end if + +! var_q - specific_humidity - kg/kg +! var_clw = "mass_content_of_cloud_liquid_water_in_atmosphere_layer" - kg/kg +! var_cli = "mass_content_of_cloud_ice_in_atmosphere_layer" - kg/kg +! for retrieval is ln(g/kg) +if (profindex % qt(1) > 0) then + allocate(humidity_total(nlevels)) + humidity_total(:) = zero + + ! Get humidity data from geovals + call ufo_geovals_get_var(geovals, var_q, geoval) + humidity_total(:) = humidity_total(:) + geoval%vals(nlevels:1:-1, 1) + call ufo_geovals_get_var(geovals, var_clw, geoval) + humidity_total(:) = humidity_total(:) + geoval%vals(nlevels:1:-1, 1) + call ufo_geovals_get_var(geovals, var_cli, geoval) + humidity_total(:) = humidity_total(:) + geoval%vals(nlevels:1:-1, 1) + + ! Convert from kg/kg to ln(g/kg) + prof_x(profindex % qt(1):profindex % qt(2)) = log (humidity_total * 1000.0_kind_real) ! ln(g/kg) + + deallocate(humidity_total) +end if + +!---------------------------- +! 3. Single-valued variables +!---------------------------- + +! var_sfc_t2m = "surface_temperature" +if (profindex % t2 > 0) then + call ufo_geovals_get_var(geovals, var_sfc_t2m, geoval) + prof_x(profindex % t2) = geoval%vals(1, 1) +end if + +! var_sfc_q2m = "specific_humidity_at_two_meters_above_surface" (kg/kg) +! for retrieval is ln(g/kg) +if (profindex % q2 > 0) then + call ufo_geovals_get_var(geovals, var_sfc_q2m, geoval) + prof_x(profindex % q2) = log (geoval%vals(1, 1) * 1000.0_kind_real) ! ln(g/kg) +end if + +! var_sfc_p2m = "air_pressure_at_two_meters_above_surface" ! (Pa) +if (profindex % pstar > 0) then + call ufo_geovals_get_var(geovals, var_sfc_p2m, geoval) + prof_x(profindex % pstar) = geoval%vals(1, 1) / 100.0_kind_real ! Pa to hPa +end if + +! var_sfc_tskin = "skin_temperature" ! (K) +if (profindex % tstar > 0) then + call ufo_geovals_get_var(geovals, var_sfc_tskin, geoval) + prof_x(profindex % tstar) = geoval%vals(1, 1) +end if + +! This has been left in for future development +! cloud top pressure +!if (profindex % cloudtopp > 0) then +! prof_x(profindex % cloudtopp) = ob % cloudtopp ! carried around as hPa +!end if + +! This has been left in for future development +! cloud fraction +!if (profindex % cloudfrac > 0) then +! prof_x(profindex % cloudfrac) = ob % cloudfrac +!end if + +! Windspeed - var_u = "eastward_wind" +! - var_v = "northward_wind" +! - windsp = sqrt (u*u + v*v) +if (profindex % windspeed > 0) then + call ufo_geovals_get_var(geovals, trim(var_u), geoval) + u = geoval % vals(1, 1) + call ufo_geovals_get_var(geovals, trim(var_v), geoval) + v = geoval % vals(1, 1) + prof_x(profindex % windspeed) = sqrt(u ** 2 + v ** 2) +end if + +!---------------------------- +! 4. Emissivities +! This has been left in for future development +!---------------------------- + +! Microwave Emissivity +!if (profindex % mwemiss(1) > 0) then +! ! Check that emissivity map is the correct size for the profile +! if ((profindex % mwemiss(2) - profindex % mwemiss(1) + 1) /= size(EmissMap)) then +! call abor1_ftn("mwemiss size differs from emissivity map") +! end if +! ! Copy microwave emissivity to profile +! prof_x(profindex % mwemiss(1):profindex % mwemiss(2)) = ob % emiss(EmissMap) +!end if + +! Retrieval of emissivity principal components +!if (profindex % emisspc(1) > 0) THEN +! ! convert ob % emiss to emiss pc +! allocate(emiss_pc(profindex % emisspc(2)-profindex % emisspc(1)+1)) +! call ob % pcemis % emistoPC(ob % channels_used(:), ob % emiss(:), emiss_pc(:)) +! prof_x(profindex % emisspc(1):profindex % emisspc(2)) = emiss_pc +! deallocate(emiss_pc) +!end if + +end subroutine ufo_rttovonedvarcheck_GeoVaLs2ProfVec + +!------------------------------------------------------------------------------- +!> Copy profile data to geovals (and ob). +!! +!! \details Heritage: Ops_SatRad_Vec2RTprof_RTTOV12.f90 +!! +!! Convert profile data to the GeoVaLs (and ob) format. We only copy fields +!! that are being retrieved, as indicated by the profindex structure. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_ProfVec2GeoVaLs(geovals, & ! inout + profindex, & ! in + ob, & ! inout + prof_x, & ! in + UseQtsplitRain) ! in + +implicit none + +! subroutine arguments: +type(ufo_geovals), intent(inout) :: geovals !< model data at obs location +type(ufo_rttovonedvarcheck_profindex), intent(in) :: profindex !< index array for x vector +type(ufo_rttovonedvarcheck_ob), intent(inout) :: ob !< satellite metadata +real(kind_real), intent(in) :: prof_x(:) !< x vector +logical, intent(in) :: UseQtsplitRain !< flag to choose whether to split rain in qt + +! Local arguments: +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_ProfVec2GeoVaLs" +character(len=max_string) :: varname +integer :: gv_index, i, ii +integer :: nlevels +integer :: EmissElement +type(ufo_geoval), pointer :: geoval +real(kind_real), allocatable :: pressure(:) +real(kind_real), allocatable :: humidity_total(:) +real(kind_real), allocatable :: q(:) +real(kind_real), allocatable :: ql(:) +real(kind_real), allocatable :: qi(:) +real(kind_real), allocatable :: emiss_pc(:) +real(kind_real) :: u, v, windsp ! variable needed for the windspeed calculation + +!------------------------------------------------------------------------------- + +nlevels = profindex % nlevels + +!------------------------------ +! 2. Set multi-level variables +!------------------------------ + +! Note GeoVaLs are surface -> TOA ; profile is TOA -> surface + +! Note that if number of profile levels is less than number of pressure levels +! we assume the levels are from the surface upwards (remember that RTTOV levels +! are upside-down) + +! var_ts - air_temperature - K +if (profindex % t(1) > 0) then + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(var_ts, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(nlevels:1:-1,1) = prof_x(profindex % t(1):profindex % t(2)) ! K +end if + +! var_q = "specific_humidity" ! kg/kg +! for retrieval is ln(g/kg) +if (profindex % q(1) > 0) then + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(var_q, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(nlevels:1:-1,1) = EXP (prof_x(profindex % q(1):profindex % q(2))) / & + 1000.0_kind_real ! ln(g/kg) => kg/kg +end if + +! var_q = "specific_humidity" ! kg/kg +! var_clw = "mass_content_of_cloud_liquid_water_in_atmosphere_layer" - kg/kg +! var_cli = "mass_content_of_cloud_ice_in_atmosphere_layer" - kg/kg +! for retrieval is ln(g/kg) +if (profindex % qt(1) > 0) then + nlevels = profindex % nlevels + allocate(pressure(nlevels)) + allocate(humidity_total(nlevels)) + allocate(q(nlevels)) + allocate(ql(nlevels)) + allocate(qi(nlevels)) + + ! Convert from ln(g/kg) to kg/kg + humidity_total(nlevels:1:-1) = EXP (prof_x(profindex % qt(1):profindex % qt(2))) / & + 1000.0_kind_real ! ln(g/kg) => kg/kg + + ! var_prs = "air_pressure" Pa + call ufo_geovals_get_var(geovals, var_prs, geoval) + pressure(:) = geoval%vals(:, 1) ! Pa + + ! Split qtotal to q(water_vapour), q(liquid), q(ice) + call Ops_SatRad_Qsplit ( 1, & + pressure(:), & + ob % background_T(:), & + humidity_total, & + q(:), & + ql(:), & + qi(:), & + UseQtsplitRain) + + ! Assign values to geovals + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(var_q, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(:,1) = q(:) + + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(var_clw, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(:,1) = ql(:) + + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(var_cli, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(:,1) = qi(:) + + deallocate(pressure) + deallocate(humidity_total) + deallocate(q) + deallocate(ql) + deallocate(qi) + +end if + +!---------------------------- +! 3. Single-valued variables +!---------------------------- + +! var_sfc_t2m = "surface_temperature" +if (profindex % t2 > 0) then + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(var_sfc_t2m, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(1,1) = prof_x(profindex % t2) ! K +end if + +! var_sfc_q2m = "specific_humidity_at_two_meters_above_surface" ! (kg/kg) +! for retrieval is ln(g/kg) +if (profindex % q2 > 0) then + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(var_sfc_q2m, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(1,1) = EXP (prof_x(profindex % q2)) / 1000.0_kind_real ! ln(g/kg) => kg/kg +end if + +! var_sfc_p2m = "air_pressure_at_two_meters_above_surface" ! (Pa) +if (profindex % pstar > 0) then + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(var_sfc_p2m, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(1,1) = prof_x(profindex % pstar) * 100.0_kind_real +end if + +! var_sfc_tskin = "skin_temperature" ! (K) +if (profindex % tstar > 0) then + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(var_sfc_tskin, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(1,1) = prof_x(profindex % tstar) +end if + +! This has been left in for future development +! cloud top pressure - passed through via the ob +!if (profindex % cloudtopp > 0) then +! ob % cloudtopp = prof_x(profindex % cloudtopp) ! stored in ob as hPa +!end if + +! This has been left in for future development +! cloud fraction - passed through via the ob +!if (profindex % cloudfrac > 0) then +! ob % cloudfrac = prof_x(profindex % cloudfrac) +!end if + +! windspeed +if (profindex % windspeed > 0) then + call ufo_geovals_get_var(geovals, trim(var_u), geoval) + u = geoval % vals(1, 1) + call ufo_geovals_get_var(geovals, trim(var_v), geoval) + v = geoval % vals(1, 1) + windsp = sqrt (u ** 2 + v ** 2) + + ! The ratio of new windsp to old windsp gives the fractional change. + ! This is then applied to each component of the wind. + if (windsp > zero) then + u = u * (prof_x(profindex % windspeed) / windsp) + v = v * (prof_x(profindex % windspeed) / windsp) + else + u = zero + v = zero + end if + + ! Write back updated u component + gv_index = 0 + do i=1,geovals%nvar + if (trim(var_u) == trim(geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(1,1) = u + + ! Write back updated v component + gv_index = 0 + do i=1,geovals%nvar + if (trim(var_v) == trim(geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(1,1) = v +end if + +!---------------------------- +! 4. Emissivities +! This has been left in for future development +!------------------------ + +! Retrieval of microwave emissivity directly +!if (profindex % mwemiss(1) > 0) THEN +! do ii = 1, size(ob % channels_used) +! EmissElement = EmissElements(ob % channels_used(ii)) +! ob % emiss(ii) = prof_x(profindex % mwemiss(1) + EmissElement - 1) +! end do +!end if + +! Retrieval of emissivity principal components +!if (profindex % emisspc(1) > 0) THEN +! allocate(emiss_pc(profindex % emisspc(2)-profindex % emisspc(1)+1)) +! emiss_pc = prof_x(profindex % emisspc(1):profindex % emisspc(2)) +! ! convert emiss_pc to ob % emissivity using +! call ob % pcemis % pctoemis(size(ob % channels_used), ob % channels_used, & +! size(emiss_pc), emiss_pc(:), ob % emiss(:)) +! deallocate(emiss_pc) +!end if + +end subroutine ufo_rttovonedvarcheck_ProfVec2GeoVaLs + +!------------------------------------------------------------------------------- +!> Check the geovals are ready for the first iteration +!! +!! \details Heritage: Ops_SatRad_SetUpRTprofBg_RTTOV12.f90 +!! +!! Check the geovals profile is ready for the first iteration. The +!! only check included at the moment if the first calculation for +!! q total. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_check_geovals(self, geovals, profindex, surface_type) + +implicit none + +! subroutine arguments: +type(ufo_rttovonedvarcheck), intent(in) :: self +type(ufo_geovals), intent(inout) :: geovals !< model data at obs location +type(ufo_rttovonedvarcheck_profindex), intent(in) :: profindex !< index array for x vector +integer, intent(in) :: surface_type !< surface type for cold surface check + +character(len=*), parameter :: routinename = "ufo_rttovonedvarcheck_check_geovals" +character(len=max_string) :: varname +type(ufo_geoval), pointer :: geoval +integer :: gv_index, i ! counters +integer :: nlevels +real(kind_real), allocatable :: temperature(:) ! temperature (K) +real(kind_real), allocatable :: pressure(:) ! pressure (Pa) +real(kind_real), allocatable :: qsaturated(:) +real(kind_real), allocatable :: humidity_total(:) +real(kind_real), allocatable :: q(:) ! specific humidity (kg/kg) +real(kind_real), allocatable :: ql(:) +real(kind_real), allocatable :: qi(:) +real(kind_real) :: skin_t, pressure_2m, temperature_2m, NewT +integer :: level_1000hpa, level_950hpa + +write(message, *) routinename, " : started" +call fckit_log % debug(message) + +! ------------------------------------------- +! Load variables needed by multiple routines +! ------------------------------------------- + +nlevels = profindex % nlevels +allocate(temperature(nlevels)) +allocate(pressure(nlevels)) +call ufo_geovals_get_var(geovals, trim(var_ts), geoval) +temperature(:) = geoval%vals(:, 1) ! K +call ufo_geovals_get_var(geovals, trim(var_prs), geoval) +pressure(:) = geoval%vals(:, 1) ! Pa + +!--------------------------------------------------- +! 1. Make sure q and q2m does not exceed saturation +!--------------------------------------------------- +if (profindex % q(1) > 0 .or. profindex % qt(1) > 0) then + allocate(q(nlevels)) + allocate(qsaturated(nlevels)) + + ! Get humidity - kg/kg + varname = trim(var_q) + call ufo_geovals_get_var(geovals, varname, geoval) + q(:) = geoval%vals(:, 1) + + ! Calculated saturation humidity + if (self % UseRHwaterForQC) then + call Ops_QsatWat (qsaturated(:), & ! out + temperature(:), & ! in + pressure(:), & ! in + nlevels) ! in + else + call Ops_Qsat (qsaturated(:), & ! out + temperature(:), & ! in + pressure(:), & ! in + nlevels) ! in + end if + + ! Limit q + where (q > qsaturated) + q = qsaturated + end where + where (q < min_q) + q = min_q + end where + + ! Assign values to geovals q + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(varname, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index) % vals(:,1) = q(:) + + deallocate(q) + deallocate(qsaturated) +end if + +if (profindex % q2 > 0) then + allocate(q(1)) + allocate(qsaturated(1)) + + ! Get humidity - kg/kg + varname = trim(var_sfc_q2m) + call ufo_geovals_get_var(geovals, varname, geoval) + q(1) = geoval%vals(1, 1) + + ! Calculated saturation humidity + if (self % UseRHwaterForQC) then + call Ops_QsatWat (qsaturated(1:1), & ! out + temperature(1:1), & ! in + pressure(1:1), & ! in + 1) ! in + else + call Ops_Qsat (qsaturated(1:1), & ! out + temperature(1:1), & ! in + pressure(1:1), & ! in + 1) ! in + end if + + ! Limit q + if (q(1) > qsaturated(1)) q(1) = qsaturated(1) + if (q(1) < min_q) q(1) = min_q + + ! Assign values to geovals q + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(varname, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index) % vals(1,1) = q(1) + + deallocate(q) + deallocate(qsaturated) +end if + +!------------------------- +! 2. Specific humidity total +!------------------------- + +if (profindex % qt(1) > 0) then + + allocate(humidity_total(nlevels)) + allocate(q(nlevels)) + allocate(ql(nlevels)) + allocate(qi(nlevels)) + + humidity_total(:) = 0.0 + ! Water vapour + call ufo_geovals_get_var(geovals, trim(var_q), geoval) + humidity_total(:) = humidity_total(:) + geoval%vals(:, 1) + + ! Cloud liquid water + call ufo_geovals_get_var(geovals, trim(var_clw), geoval) + where (geoval%vals(:, 1) < 0.0) geoval%vals(:, 1) = 0.0 + humidity_total(:) = humidity_total(:) + geoval%vals(:, 1) + + ! Split qtotal to q(water_vapour), q(liquid), q(ice) + call Ops_SatRad_Qsplit ( 1, & + pressure(:), & + temperature(:), & + humidity_total, & + q(:), & + ql(:), & + qi(:), & + self % UseQtsplitRain) + + ! Assign values to geovals q + varname = trim(var_q) ! kg/kg + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(varname, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index) % vals(:,1) = q(:) + + ! Assign values to geovals q clw + varname = trim(var_clw) ! kg/kg + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(varname, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index) % vals(:,1) = ql(:) + + ! Assign values to geovals ciw + varname = trim(var_cli) ! kg/kg + gv_index = 0 + do i=1,geovals%nvar + if (cmp_strings(varname, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index)%vals(:,1) = qi(:) + + deallocate(humidity_total) + deallocate(q) + deallocate(ql) + deallocate(qi) + +end if + +!---- +! Legacy Ops_SatRad_SetUpRTprofBg_RTTOV12.f90 - done here to make sure geovals are updated +! Reset low level temperatures over seaice and cold, low land as per Ops_SatRad_SetUpRTprofBg.F90 +! N.B. I think this should be flagged so it's clear that the background has been modified +!---- +if(surface_type /= RTsea .and. self % UseColdSurfaceCheck) then + + ! Get skin temperature + call ufo_geovals_get_var(geovals, var_sfc_tskin, geoval) + skin_t = geoval%vals(1, 1) + + ! Get 2m pressure + call ufo_geovals_get_var(geovals, var_sfc_p2m, geoval) + pressure_2m = geoval%vals(1, 1) + + ! Get 2m temperature + call ufo_geovals_get_var(geovals, var_sfc_t2m, geoval) + temperature_2m = geoval%vals(1, 1) + + if(skin_t < 271.4_kind_real .and. & + pressure_2m > 95000.0_kind_real) then + + level_1000hpa = minloc(abs(pressure - 100000.0_kind_real),DIM=1) + level_950hpa = minloc(abs(pressure - 95000.0_kind_real),DIM=1) + + NewT = temperature(level_950hpa) + if(pressure_2m > 100000.0_kind_real) then + NewT = max(NewT, temperature(level_1000hPa)) + end if + NewT = min(NewT, 271.4_kind_real) + + temperature(level_1000hPa) = max(temperature(level_1000hPa), NewT) + temperature_2m = max(temperature_2m, NewT) + skin_t = max(skin_t, NewT) + + ! Put updated values back into geovals + ! Temperature + gv_index = 0 + varname = trim(var_ts) + do i=1,geovals%nvar + if (cmp_strings(varname, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index) % vals(level_1000hPa,1) = temperature(level_1000hPa) + + ! 2m Temperature + gv_index = 0 + varname = trim(var_sfc_t2m) + do i=1,geovals%nvar + if (cmp_strings(varname, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index) % vals(1,1) = temperature_2m + + ! Skin Temperature + gv_index = 0 + varname = trim(var_sfc_tskin) + do i=1,geovals%nvar + if (cmp_strings(varname, geovals%variables(i))) gv_index = i + end do + geovals%geovals(gv_index) % vals(1,1) = skin_t + + endif + +endif + +! Tidy up +if (allocated(temperature)) deallocate(temperature) +if (allocated(pressure)) deallocate(pressure) +if (allocated(qsaturated)) deallocate(qsaturated) +if (allocated(humidity_total)) deallocate(humidity_total) +if (allocated(q)) deallocate(q) +if (allocated(ql)) deallocate(ql) +if (allocated(qi)) deallocate(qi) + +write(message, *) routinename, " : ended" +call fckit_log % debug(message) + +end subroutine ufo_rttovonedvarcheck_check_geovals + +!------------------------------------------------------------------------------- +!> Calculate the cost function. +!! +!! \details Heritage: Ops_SatRad_CostFunction.f90 +!! +!! Calculate the cost function from the input delta's and error +!! covariances. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_CostFunction(DeltaProf, b_inv, & + DeltaObs, r_matrix, & + Jcost) + +implicit none + +! subroutine arguments: +real(kind_real), intent(in) :: DeltaProf(:) +real(kind_real), intent(in) :: b_inv(:,:) +real(kind_real), intent(in) :: DeltaObs(:) +type(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: r_matrix +real(kind_real), intent(out) :: Jcost(3) + +! Local arguments: +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_CostFunction" +integer :: y_size +real(kind_real) :: Jb, Jo, Jcurrent +real(kind_real), allocatable :: RinvDeltaY(:) + +!------------------------------------------------------------------------------- + +allocate(RinvDeltaY, source=DeltaObs) + +y_size = size(DeltaObs) +Jcost(:) = zero +call r_matrix % multiply_inverse_vector(DeltaObs, RinvDeltaY) + +Jo = half * dot_product(DeltaObs, RinvDeltaY) +Jb = half * dot_product(DeltaProf, (matmul(b_inv, DeltaProf))) +Jcurrent = Jb + Jo + +Jcost(1) = (Jo + Jb) * two / real (y_size, kind_real) ! Normalize cost by nchans +Jcost(2) = Jb * two / real (y_size, kind_real) ! Normalize cost by nchans +Jcost(3) = Jo * two / real (y_size, kind_real) ! Normalize cost by nchans + +write(message,*) "Jo, Jb, Jcurrent = ", Jo, Jb, Jcost(1) +call fckit_log % debug(message) + +deallocate(RinvDeltaY) + +end subroutine ufo_rttovonedvarcheck_CostFunction + +!------------------------------------------------------------------------------- +!> Constrain profile humidity and check temperature values are okay +!! +!! \details Heritage: Ops_SatRad_CheckIteration.f90 +!! +!! Check humidity and temperature profiles. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_CheckIteration (self, & + geovals, & + profindex, & + profile, & + OutOfRange) + +implicit none + +! subroutine arguments: +type(ufo_rttovonedvarcheck), intent(in) :: self +type(ufo_geovals), intent(in) :: geovals +type(ufo_rttovonedvarcheck_profindex), intent(in) :: profindex +real(kind_real), intent(inout) :: profile(:) +logical, intent(out) :: OutOfRange + +! Local declarations: +real(kind_real), allocatable :: qsaturated(:) +real(kind_real), allocatable :: scaled_qsaturated(:) +real(kind_real) :: q2_sat(1) +real(kind_real), allocatable :: Plevels_1DVar(:) +real(kind_real) :: Pstar_Pa(1) +real(kind_real), allocatable :: Temp(:) +real(kind_real) :: Temp2(1) +real(kind_real) :: rtbase +integer :: nlevels_q +integer :: toplevel_q +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_CheckIteration" +type(ufo_geoval), pointer :: geoval +character(len=max_string) :: varname +integer :: ii +integer :: nlevels_1dvar + +! Setup +OutOfRange = .false. +nlevels_1dvar = self % nlevels +allocate(Temp(nlevels_1dvar)) + +!--------------------- +! 1. Check Temperatures +!--------------------- + +!---- +! 1.1) levels +!---- + +if (profindex % t(1) > 0) then + Temp = profile(profindex % t(1):profindex % t(2)) + if (any (Temp < minTemperature) .or. any (Temp > MaxTemperature)) then + OutOfRange = .true. + end if +else + varname = var_ts + call ufo_geovals_get_var(geovals, varname, geoval) + ! Note: profile is TOA -> surface + Temp = geoval%vals(nlevels_1dvar:1:-1, 1) ! K +end if + +!---- +! 1.2) surface air +!---- + +if (profindex % t2 > 0) then + Temp2 = profile(profindex % t2) + if (Temp2(1) < minTemperature .or. Temp2(1) > MaxTemperature) then + OutOfRange = .true. + end if +else + varname = var_sfc_t2m + call ufo_geovals_get_var(geovals, varname, geoval) + Temp2 = geoval%vals(1, 1) ! K +end if + +!---- +! 1.3) surface skin +!---- + +if (profindex % tstar > 0) then + if (profile(profindex % tstar) < minTemperature .or. & + profile(profindex % tstar) > MaxTemperature) then + OutOfRange = .true. + end if +end if + +!--------------------- +! 2. Constrain humidity +!--------------------- + +Constrain: if (.not. OutOfRange) then + + ! Work out the number of humidity levels and allocate size to arrays + + if (profindex % q(1) > 0) then + nlevels_q = profindex % q(2) - profindex % q(1) + 1 + else if (profindex % qt(1) > 0) then + nlevels_q = profindex % qt(2) - profindex % qt(1) + 1 + allocate (scaled_qsaturated(nlevels_q)) + else + nlevels_q = nlevels_1dvar + end if + toplevel_q = nlevels_1dvar - nlevels_q + 1 + allocate (qsaturated(nlevels_q)) + + + ! Get pressure + varname = var_prs + call ufo_geovals_get_var(geovals, varname, geoval) + if (.not. allocated(Plevels_1DVar)) allocate(Plevels_1DVar(nlevels_q)) + ! Note: profile is TOA -> surface + Plevels_1DVar(:) = geoval%vals(nlevels_1dvar:1:-1, 1) ! K + + !---- + ! 2.1) Levels + !---- + + ! Note that if number of profile levels is less than number of pressure levels + ! we assume the levels are from the surface upwards (remember that RTTOV levels + ! are upside-down) + + if (profindex % q(1) > 0) then + + if (self % useRHwaterForQC) then + call Ops_QsatWat(qsaturated(1:nlevels_q), & ! out (qsat levels) + Temp(1:nlevels_q), & ! in (t levels) + Plevels_1DVar(1:nlevels_q), & ! in (p levels) + nlevels_q) ! in + else + call Ops_Qsat(qsaturated(1:nlevels_q), & ! out + Temp(1:nlevels_q), & ! in + Plevels_1DVar(1:nlevels_q), & ! in + nlevels_q) ! in + end if + + qsaturated(1:nlevels_q) = log (qsaturated(1:nlevels_q) * 1000.0_kind_real) + where (profile(profindex % q(1):profindex % q(2)) > qsaturated(1:nlevels_q)) + profile(profindex % q(1):profindex % q(2)) = qsaturated(1:nlevels_q) + end where + + end if + + if (profindex % qt(1) > 0) then + + ! Qtotal is not included in the relative humidity quality control changes + call Ops_Qsat (qsaturated(1:nlevels_q), & ! out + Temp(1:nlevels_q), & ! in + Plevels_1DVar(1:nlevels_q), & ! in + nlevels_q) ! in + + ! scaled_qsaturated is generated as an upper limit on the value of qtot , and + ! is set to 2*qsat. + scaled_qsaturated(1:nlevels_q) = log (two * qsaturated(1:nlevels_q) * 1000.0_kind_real) + where (profile(profindex % qt(1):profindex % qt(2)) > scaled_qsaturated(1:nlevels_q)) + profile(profindex % qt(1):profindex % qt(2)) = scaled_qsaturated(1:nlevels_q) + end where + + end if + + !---- + ! 2.2) Surface + !---- + + if (profindex % q2 > 0) then + varname = var_sfc_p2m + call ufo_geovals_get_var(geovals, varname, geoval) + Pstar_Pa(1) = geoval%vals(1, 1) + if (self % useRHwaterForQC) then + call Ops_QsatWat (q2_sat(1:1), & ! out + Temp2, & ! in + Pstar_Pa(1:1), & ! in + 1) ! in + else + call Ops_Qsat (q2_sat(1:1), & ! out + Temp2, & ! in + Pstar_Pa(1:1), & ! in + 1) ! in + end if + q2_sat(1) = log (q2_sat(1) * 1000.0_kind_real) + if (profile(profindex % q2) > q2_sat(1)) then + profile(profindex % q2) = q2_sat(1) + end if + end if + +! This has been left in for future development +! !---- +! ! 2.3) Grey cloud +! !---- +! +! if (profindex % cloudtopp > 0) then +! profile(profindex % CloudFrac) = min (profile(profindex % CloudFrac), 1.0) +! profile(profindex % CloudFrac) = max (profile(profindex % CloudFrac), 0.0) +! profile(profindex % CloudTopP) = max (profile(profindex % CloudTopP), 100.0) +! if (LimitCTPtorTBase) then +! rtbase = maxval (Plevels_RTModel(:)) +! profile(profindex % CloudTopP) = min (profile(profindex % CloudTopP), Pstar_mb, rtbase) +! else +! profile(profindex % CloudTopP) = min (profile(profindex % CloudTopP), Pstar_mb) +! end if +! end if +! +! This has been left in for future development +! !---- +! ! 2.4) Surface emissivity PCs +! !---- +! +! if (profindex % emisspc(1) > 0) then +! where (profile(profindex % emisspc(1):profindex % emisspc(2)) > EmisEigenVec % PCmax(1:nemisspc)) +! profile(profindex % emisspc(1):profindex % emisspc(2)) = EmisEigenVec % PCmax(1:nemisspc) +! end where +! where (profile(profindex % emisspc(1):profindex % emisspc(2)) < EmisEigenVec % PCmin(1:nemisspc)) +! profile(profindex % emisspc(1):profindex % emisspc(2)) = EmisEigenVec % PCmin(1:nemisspc) +! end where +! end if +! +! This has been left in for future development +! !-------- +! ! 2.5) Cloud profiles +! !-------- +! +! if (profindex % cf(1) > 0) then +! where (profile(profindex % cf(1):profindex % cf(2)) <= 0.001) +! profile(profindex % cf(1):profindex % cf(2)) = 0.001 +! end where +! where (profile(profindex % cf(1):profindex % cf(2)) >= 1.0) +! profile(profindex % cf(1):profindex % cf(2)) = 0.999 +! end where +! end if +! +! if (profindex % ql(1) > 0 .AND. .not. useLogForCloud) then +! where (profile(profindex % ql(1):profindex % ql(2)) < 0) +! profile(profindex % ql(1):profindex % ql(2)) = 0 +! end where +! end if +! +! if (profindex % qi(1) > 0 .AND. .not. useLogForCloud) then +! where (profile(profindex % qi(1):profindex % qi(2)) < 0) +! profile(profindex % qi(1):profindex % qi(2)) = 0 +! end where +! end if + +end if Constrain + +! -------- +! Tidy up +! -------- +if (allocated(qsaturated)) deallocate(qsaturated) +if (allocated(scaled_qsaturated)) deallocate(scaled_qsaturated) +if (allocated(Plevels_1DVar)) deallocate(Plevels_1DVar) +if (allocated(Temp)) deallocate(Temp) + +end subroutine ufo_rttovonedvarcheck_CheckIteration + +!------------------------------------------------------------------------------- +!> Check cloud during iteration. +!! +!! \details Heritage: Ops_SatRad_CheckCloudyIteration.f90 +!! +!! For a 1dvar profile using cloud variables, check that the liquid water path +!! and the ice water path are sensible. if they are beyond sensible values stop +!! 1dvar and reject profile +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_CheckCloudyIteration( & + geovals, & ! in + profindex, & ! in + nlevels_1dvar, & ! in + OutOfRange, & ! out + OutLWP ) ! out + +implicit none + +type(ufo_geovals), intent(in) :: geovals +type(ufo_rttovonedvarcheck_profindex), intent(in) :: profindex +integer, intent(in) :: nlevels_1dvar +logical, intent(out) :: OutOfRange +real(kind_real), optional, intent(out) :: OutLWP + +! Local variables: +real(kind_real) :: LWP +real(kind_real) :: IWP +real(kind_real) :: dp +real(kind_real) :: meanql +real(kind_real) :: meanqi +integer :: i +integer :: nlevels_q, toplevel_q +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_CheckCloudyIteration" + +real(kind_real), parameter :: MaxLWP = two +real(kind_real), parameter :: MaxIWP = 3.0_kind_real +real(kind_real) :: Plevels_1DVar(nlevels_1dvar) +type(ufo_geoval), pointer :: geoval +real(kind_real) :: clw(nlevels_1dvar) +real(kind_real) :: ciw(nlevels_1dvar) +character(len=max_string) :: varname + +!------------------------------------------------------------------------------- + +!initialise +OutOfRange = .false. +IWP = zero +LWP = zero + +! Get pressure from geovals +varname = var_prs +call ufo_geovals_get_var(geovals, varname, geoval) +Plevels_1DVar(:) = geoval%vals(:, 1) ! K + +! Get clw from geovals +varname = var_clw +call ufo_geovals_get_var(geovals, varname, geoval) +clw = geoval%vals(:, 1) + +! Get ciw from geovals +varname = var_cli +call ufo_geovals_get_var(geovals, varname, geoval) +ciw = geoval%vals(:, 1) + +! Work out the number of humidity levels +if ( profindex % q(1) > 0 ) then + nlevels_q = profindex % q(2) - profindex % q(1) + 1 +else if ( profindex % qt(1) > 0 ) then + nlevels_q = profindex % qt(2) - profindex % qt(1) + 1 +else if ( profindex % ql(1) > 0 ) then + nlevels_q = profindex % ql(2) - profindex % ql(1) + 1 +else if ( profindex % qi(1) > 0 ) then + nlevels_q = profindex % qi(2) - profindex % qi(1) + 1 +else + nlevels_q = nlevels_1dvar +end if +toplevel_q = nlevels_1dvar - nlevels_q + 1 + +!is the profile cloudy? +!if it is do the test + +if (any(ciw(:) > zero) .or. & + any(clw(:) > zero)) then + +!1.1 compute iwp, lwp + + do i=1, nlevels_1dvar-1 + + dp = Plevels_1DVar(i) - Plevels_1DVar(i+1) + + ! Calculate layer mean from CloudIce on levels + meanqi = half * & + (ciw(i) + ciw(i+1)) + if (meanqi > zero) then + IWP = IWP + dp * meanqi + end if + + ! Calculate layer mean from CLW on levels + meanql = half * (clw(i) + clw(i+1)) + if (meanql > zero) then + LWP = LWP + dp * meanql + end if + + end do + + IWP = IWP / grav + LWP = LWP / grav + + +!2.1 test if lwp iwp exceeds thresholds + + if ((IWP > MaxIWP) .or. (LWP > MaxLWP)) then + call fckit_log % debug("lwp or iwp exceeds thresholds") + OutOfRange = .true. + write(message,*) "lwp and iwp = ",LWP,IWP + call fckit_log % debug(message) + else + call fckit_log % debug("lwp and iwp less than thresholds") + write(message,*) "lwp and iwp = ",LWP,IWP + call fckit_log % debug(message) + end if + +end if + +if (present(OutLWP)) OutLWP = LWP + +end subroutine ufo_rttovonedvarcheck_CheckCloudyIteration + +!----------------------------------------------------------- +!> Print detailed information for each iteration for diagnostics +!! +!! \author Met Office +!! +!! \date 29/03/2021: Created +!! +subroutine ufo_rttovonedvarcheck_PrintIterInfo(yob, hofx, channels, & + guessprofile, backprofile, & + diffprofile, binv, hmatrix) + +implicit none + +real(kind_real), intent(in) :: yob(:) +real(kind_real), intent(in) :: hofx(:) +integer, intent(in) :: channels(:) +real(kind_real), intent(in) :: guessprofile(:) +real(kind_real), intent(in) :: backprofile(:) +real(kind_real), intent(in) :: diffprofile(:) +real(kind_real), intent(in) :: binv(:,:) ! (nprofelements,nprofelements) +real(kind_real), intent(in) :: hmatrix(:,:) ! (nchans,nprofelements) + +integer :: obs_size, profile_size, ii, jj +character(len=12) :: chans_fmt, prof_fmt +character(len=3) :: txt_nchans, txt_nprof +character(len=10) :: int_fmt + +obs_size = size(yob) +write( unit=txt_nchans,fmt='(i3)' ) obs_size +write( unit=chans_fmt,fmt='(a)' ) '(' // trim(txt_nchans) // 'E30.16)' +write( unit=int_fmt,fmt='(a)' ) '(' // trim(txt_nchans) // 'I30)' + +profile_size = size(guessprofile) +write( unit=txt_nprof,fmt='(i3)' ) profile_size +write( unit=prof_fmt,fmt='(a)' ) '(' // trim(txt_nprof) // 'E30.16)' + +write(*,*) "Start print iter info" + +! Print obs info +write(*,"(2A30)") "yob", "hofx" +do ii = 1, obs_size + write(*,"(2E30.16)") yob(ii),hofx(ii) +end do + +! Print profile info +write(*,"(3A30)") "guessprofile", "backprofile", "diffprofile" +do ii = 1, profile_size + write(*,"(3E30.16)") guessprofile(ii),backprofile(ii), diffprofile(ii) +end do + +! Print b inv +write(*,"(2A30)") "B inverse" +do ii = 1, profile_size + write(*,prof_fmt) binv(:,ii) +end do + +! Print hmatrix +write(*,"(2A30)") "hmatrix" +write(*, int_fmt) channels(:) +do ii = 1, profile_size + write(*,chans_fmt) hmatrix(:,ii) +end do + +write(*,*) "Finished print iter info" + +end subroutine ufo_rttovonedvarcheck_PrintIterInfo + +! ---------------------------------------------------------- + +end module ufo_rttovonedvarcheck_minimize_utils_mod diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_mod.f90 new file mode 100644 index 000000000..4a55f2c64 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_mod.f90 @@ -0,0 +1,347 @@ +! (C) Copyright 2017-2020 Met Office +! +! this software is licensed under the terms of the apache licence version 2.0 +! which can be obtained at http://www.apache.org/licenses/license-2.0. + +!> The main Fortran module for implementing the rttov onedvar check + +module ufo_rttovonedvarcheck_mod + +use fckit_configuration_module, only: fckit_configuration +use fckit_log_module, only : fckit_log +use iso_c_binding +use kinds +use missing_values_mod +use obsspace_mod +use oops_variables_mod +use ufo_geovals_mod +use ufo_metoffice_bmatrixstatic_mod +use ufo_metoffice_rmatrixradiance_mod +use ufo_radiancerttov_mod +use ufo_rttovonedvarcheck_constants_mod +use ufo_rttovonedvarcheck_minimize_utils_mod +use ufo_rttovonedvarcheck_minimize_newton_mod +use ufo_rttovonedvarcheck_minimize_ml_mod +use ufo_rttovonedvarcheck_ob_mod +use ufo_rttovonedvarcheck_obs_mod +use ufo_rttovonedvarcheck_pcemis_mod +use ufo_rttovonedvarcheck_profindex_mod +use ufo_rttovonedvarcheck_rsubmatrix_mod +use ufo_rttovonedvarcheck_utils_mod +use ufo_vars_mod + +implicit none +private +public :: ufo_rttovonedvarcheck_create +public :: ufo_rttovonedvarcheck_delete +public :: ufo_rttovonedvarcheck_apply + +! ------------------------------------------------------------------------------ +contains +! ------------------------------------------------------------------------------ +!> Setup the main rttov onedvar object in Fortran +!! +!! \details Makes a call to the main setup routine. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_create(self, obsspace, f_conf, channels, & + onedvarflag, passflag) + + implicit none + type(ufo_rttovonedvarcheck), intent(inout) :: self !< rttovonedvarcheck main object + type(c_ptr), value, intent(in) :: obsspace !< observation database pointer + type(fckit_configuration), intent(in) :: f_conf !< yaml file contents + integer(c_int), intent(in) :: channels(:) !< all channels that can be used in 1D-Var + integer(c_int), intent(in) :: onedvarflag !< flag from qc flags + integer(c_int), intent(in) :: passflag !< pass flag from qc flags + + self % obsdb = obsspace + self % onedvarflag = onedvarflag + self % passflag = passflag + + call ufo_rttovonedvarcheck_setup(self, f_conf, channels) ! from init + +end subroutine ufo_rttovonedvarcheck_create + +! ------------------------------------------------------------------------------ +!> Delete the main rttov onedvar object in Fortran +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_delete(self) + + implicit none + type(ufo_rttovonedvarcheck), intent(inout) :: self !< rttovonedvarcheck main object + + if (allocated(self % retrieval_variables)) deallocate(self % retrieval_variables) + if (allocated(self % channels)) deallocate(self % channels) + +end subroutine ufo_rttovonedvarcheck_delete + +! ------------------------------------------------------------------------------ +!> The main routine that applys the rttov onedvar filter +!! +!! \details Heritage : Ops_SatRad_Do1DVar_RTTOV12.f90 +!! +!! This routine is called from the c++ apply method. The filter performs +!! a 1D-Var minimization using rttov +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_apply(self, f_conf, vars, retrieval_vars, geovals, apply) + use ufo_utils_mod, only: cmp_strings + + implicit none + type(ufo_rttovonedvarcheck), intent(inout) :: self !< rttovonedvarcheck main object + type(fckit_configuration), intent(in) :: f_conf !< yaml file contents + type(oops_variables), intent(in) :: vars !< channels for 1D-Var + type(oops_variables), intent(in) :: retrieval_vars !< retrieval variables for 1D-Var + type(ufo_geovals), intent(in) :: geovals !< model values at observation space + logical, intent(in) :: apply(:) !< qc manager flags + + type(ufo_rttovonedvarcheck_obs) :: obs ! data for all observations read from db + type(ufo_metoffice_bmatrixstatic) :: full_bmatrix ! full bmatrix read from file + type(ufo_geovals) :: local_geovals ! geoval for one observation + type(ufo_rttovonedvarcheck_ob) :: ob ! observation data for a single observation + type(ufo_rttovonedvarcheck_profindex) :: prof_index ! index for mapping geovals to 1d-var state profile + type(ufo_metoffice_rmatrixradiance) :: full_rmatrix ! full r_matrix read from file + type(ufo_rttovonedvarcheck_rsubmatrix) :: r_submatrix ! r_submatrix object + type(ufo_geovals) :: hofxdiags ! hofxdiags containing jacobian + type(ufo_rttovonedvarcheck_pcemis), target :: IR_pcemis ! Infrared principal components object + type(ufo_geoval), pointer :: geoval + character(len=max_string) :: sensor_id + character(len=max_string) :: var + character(len=max_string) :: varname + character(len=max_string) :: message + integer :: jvar, ivar, jobs, band, ii ! counters + integer :: nchans_used ! counter for number of channels used for an ob + integer :: jchans_used + integer :: fileunit ! unit number for reading in files + integer :: apply_count + integer :: nprofelements ! number of elements in 1d-var state profile + integer, allocatable :: fields_in(:) + real(kind_real) :: missing ! missing value + real(kind_real) :: t1, t2 ! timing + real(kind_real), allocatable :: b_matrix(:,:) ! 1d-var profile b matrix + real(kind_real), allocatable :: b_inverse(:,:) ! inverse for each 1d-var profile b matrix + real(kind_real), allocatable :: b_sigma(:) ! b_matrix diagonal error + logical :: file_exists ! check if a file exists logical + logical :: onedvar_success + logical :: cloud_retrieval = .false. + type(ufo_radiancerttov) :: rttov_simobs + + ! ------------------------------------------ + ! 1. Setup + ! ------------------------------------------ + missing = missing_value(missing) + + ! Setup rttov simobs + call rttov_simobs % setup(f_conf, self % channels) + + ! Setup IR emissivity - if needed + if (self % pcemiss) then + if (len(self % EmisAtlas) > 4) then + call IR_pcemis % setup(self % EmisEigVecPath, self % EmisAtlas) + else + call IR_pcemis % setup(self % EmisEigVecPath) + end if + end if + + ! Setup full B matrix object + call full_bmatrix % setup(self % retrieval_variables, self % b_matrix_path, & + self % qtotal) + + ! Setup full R matrix object + call full_rmatrix % setup(self % r_matrix_path) + + ! Check if cloud retrievals needed + do ii = 1, size(self % retrieval_variables) + if (cmp_strings(self % retrieval_variables(ii), "cloud_top_pressure")) then + write(*,*) "Simple cloud is part of the state vector" + cloud_retrieval = .true. + end if + end do + + ! Create profile index for mapping 1d-var profile to b-matrix + call prof_index % setup(full_bmatrix, self%nlevels) + + ! Read in observation data from obsspace + call obs % setup(self, prof_index % nprofelements, geovals, vars, IR_pcemis) + + ! Initialize data arrays + allocate(b_matrix(prof_index % nprofelements,prof_index % nprofelements)) + allocate(b_inverse(prof_index % nprofelements,prof_index % nprofelements)) + allocate(b_sigma(prof_index % nprofelements)) + + ! Decide on loop parameters - testing + if (self % StartOb == 0) self % StartOb = 1 + if (self % FinishOb == 0) self % FinishOb = obs % iloc + if (self % StartOb > self % FinishOb) then + write(message,*) "start loop ",self % StartOb," is greater than finish loop ",self % FinishOb + call abor1_ftn(message) + end if + + ! ------------------------------------------ + ! 2. Beginning main observation loop + ! ------------------------------------------ + write(*,*) "Beginning loop over observations: ",trim(self%qcname) + apply_count = 0 + obs_loop: do jobs = self % StartOb, self % FinishOb + if (apply(jobs)) then + + apply_count = apply_count + 1 + write(message, *) "starting obs number ",jobs + call fckit_log % debug(message) + !--------------------------------------------------- + ! 2.1 Setup Jb terms + !--------------------------------------------------- + ! create one ob geovals from full all obs geovals + call ufo_geovals_copy_one(local_geovals, geovals, jobs) + call ufo_rttovonedvarcheck_check_geovals(self, local_geovals, & + prof_index, obs % surface_type(jobs)) + + ! create b matrix arrays for this single observation location + call full_bmatrix % reset( obs % lat(jobs), & ! in + b_matrix, b_inverse, b_sigma ) ! out + + !--------------------------------------------------- + ! 2.2 Setup Jo terms + !--------------------------------------------------- + ! Channel selection based on previous filters flags + nchans_used = 0 + do jvar = 1, self%nchans + if( obs % QCflags(jvar,jobs) == self % passflag ) then + nchans_used = nchans_used + 1 + end if + end do + if (nchans_used == 0) then + write(message, *) "No channels selected for observation number ", & + jobs, " : skipping" + call fckit_log % debug(message) + cycle obs_loop + end if + + ! setup ob data for this observation + call ob % setup(nchans_used, self % nlevels, prof_index % nprofelements, self % nchans) + ob % forward_mod_name = self % forward_mod_name + ob % latitude = obs % lat(jobs) + ob % longitude = obs % lon(jobs) + ob % elevation = obs % elevation(jobs) + ob % sensor_zenith_angle = obs % sat_zen(jobs) + ob % sensor_azimuth_angle = obs % sat_azi(jobs) + ob % solar_zenith_angle = obs % sol_zen(jobs) + ob % solar_azimuth_angle = obs % sol_azi(jobs) + ob % channels_all = self % channels + ob % surface_type = obs % surface_type(jobs) + ob % retrievecloud = cloud_retrieval + ob % pcemis => IR_pcemis + ob % calc_emiss = obs % calc_emiss(jobs) + if(self % RTTOV_mwscattSwitch) ob % mwscatt = .true. + if(self % RTTOV_usetotalice) ob % mwscatt_totalice = .true. + + ! Store background T in ob data space + call ufo_geovals_get_var(local_geovals, var_ts, geoval) + ob % background_T(:) = geoval%vals(:, 1) ! K + + ! Create obs vector and r matrix + jchans_used = 0 + do jvar = 1, self%nchans + if( obs % QCflags(jvar,jobs) == self % passflag ) then + jchans_used = jchans_used + 1 + ob % yobs(jchans_used) = obs % yobs(jvar, jobs) + ob % channels_used(jchans_used) = self % channels(jvar) + ob % emiss(jchans_used) = obs % emiss(jvar, jobs) + end if + end do + call r_submatrix % setup(nchans_used, ob % channels_used, full_rmatrix=full_rmatrix) + + ! Setup hofxdiags for this retrieval + call ufo_geovals_setup(hofxdiags, retrieval_vars, 1) + + if (self % FullDiagnostics) then + call ob % info() + call r_submatrix % info() + write(*, *) "Observations used = ",ob % yobs(:) + write(*,*) "ob % emiss = ",ob % emiss + write(*,*) "ob % calc_emiss = ",ob % calc_emiss + write(*,*) "Channel selection = " + write(*,'(15I5)') ob % channels_used + write(*,*) "All Channels = " + write(*,'(15I5)') ob % channels_all + end if + + !--------------------------------------------------- + ! 2.3 Call minimization + !--------------------------------------------------- + if (self % UseMLMinimization) then + call ufo_rttovonedvarcheck_minimize_ml(self, ob, & + r_submatrix, b_matrix, b_inverse, b_sigma, & + local_geovals, hofxdiags, rttov_simobs, & + prof_index, onedvar_success) + else + call ufo_rttovonedvarcheck_minimize_newton(self, ob, & + r_submatrix, b_matrix, b_inverse, b_sigma, & + local_geovals, hofxdiags, rttov_simobs, & + prof_index, onedvar_success) + end if + + obs % output_BT(:, jobs) = ob % output_BT(:) + obs % background_BT(:, jobs) = ob % background_BT(:) + obs % output_profile(:,jobs) = ob % output_profile(:) + obs % final_cost(jobs) = ob % final_cost + obs % LWP(jobs) = ob % LWP + obs % niter(jobs) = ob % niter + + ! Set QCflags based on output from minimization + if (.NOT. onedvar_success) then + do jvar = 1, self%nchans + if( obs % QCflags(jvar,jobs) == 0 ) then + obs % QCflags(jvar,jobs) = self % onedvarflag + end if + end do + end if + + ! Tidy up memory specific to a single observation + call ufo_geovals_delete(local_geovals) + call ufo_geovals_delete(hofxdiags) + call ob % delete() + call r_submatrix % delete() + + else + call fckit_log % info("Final 1Dvar cost, apply = F") + + endif + end do obs_loop + + !--------------------------------------------------- + ! 3.0 Return variables and tidy up + !--------------------------------------------------- + + write(message, *) "Total number of observations = ", obs % iloc + call fckit_log % info(message) + write(message, *) "Number tested by 1dvar = ", apply_count + call fckit_log % info(message) + + ! Put qcflags and output variables into observation space + call obs % output(self % obsdb, prof_index, vars, self % nchans) + + ! Tidy up memory used for all observations + call full_bmatrix % delete() + call full_rmatrix % delete() + call obs % delete() + if (self % pcemiss) call IR_pcemis % delete() + if (allocated(b_matrix)) deallocate(b_matrix) + if (allocated(b_inverse)) deallocate(b_inverse) + if (allocated(b_sigma)) deallocate(b_sigma) + call rttov_simobs % delete() + +end subroutine ufo_rttovonedvarcheck_apply + +end module ufo_rttovonedvarcheck_mod diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_ob_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_ob_mod.f90 new file mode 100644 index 000000000..d786fadba --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_ob_mod.f90 @@ -0,0 +1,200 @@ +! (C) copyright 2020 Met Office +! +! this software is licensed under the terms of the apache licence version 2.0 +! which can be obtained at http://www.apache.org/licenses/license-2.0. + +!> Fortran module which contains the observation metadata for a single observation + +module ufo_rttovonedvarcheck_ob_mod + +use kinds +use missing_values_mod +use ufo_constants_mod, only: zero +use ufo_geovals_mod +use ufo_rttovonedvarcheck_constants_mod +use ufo_rttovonedvarcheck_utils_mod +use ufo_rttovonedvarcheck_pcemis_mod + +implicit none +private + +! Ob info type definition +type, public :: ufo_rttovonedvarcheck_ob + + character(len=max_string) :: forward_mod_name !< forward model name (RTTOV only one at the moment) + integer :: nlocs !< number of locations = 1 + integer :: surface_type !< surface type of observation + integer :: niter + integer, allocatable :: channels_used(:) !< channels used for this observation + integer, allocatable :: channels_all(:) !< all channels used for output + real(kind_real) :: latitude !< latitude of observation + real(kind_real) :: longitude !< longitude of observation + real(kind_real) :: elevation !< elevation above sea level of observation + real(kind_real) :: sensor_zenith_angle !< sensor zenith of observation + real(kind_real) :: sensor_azimuth_angle !< sensor azimuth of observation + real(kind_real) :: solar_zenith_angle !< solar zenith of observation + real(kind_real) :: solar_azimuth_angle !< solar azimuth of observation + real(kind_real) :: cloudtopp !< cloud top pressure (used in if cloudy retrieval used) + real(kind_real) :: cloudfrac !< cloud fraction (used in if cloudy retrieval used) + real(kind_real) :: final_cost !< final cost at solution + real(kind_real) :: LWP !< retrieved liquid water path. This is output for future filters + real(kind_real), allocatable :: yobs(:) !< satellite BTs + real(kind_real), allocatable :: emiss(:) !< surface emissivity + real(kind_real), allocatable :: background_T(:) !< background temperature used by qsplit + real(kind_real), allocatable :: output_profile(:) !< retrieved state at converge as profile vector + real(kind_real), allocatable :: output_BT(:) !< Brightness temperatures using retrieved state + real(kind_real), allocatable :: background_BT(:) !< Brightness temperatures from 1st itreration + logical :: retrievecloud !< flag to turn on retrieve cloud + logical :: mwscatt !< flag to use rttov-scatt model through the interface + logical :: mwscatt_totalice !< flag to use total ice (rather then ciw) for rttov-scatt simulations + logical, allocatable :: calc_emiss(:) !< flag to decide if RTTOV calculates emissivity + type(ufo_rttovonedvarcheck_pcemis), pointer :: pcemis !< pointer to the IR pc emissivity object + +contains + procedure :: setup => ufo_rttovonedvarcheck_InitOb + procedure :: delete => ufo_rttovonedvarcheck_DeleteOb + procedure :: info => ufo_rttovonedvarcheck_PrintOb + +end type ufo_rttovonedvarcheck_ob + +contains + +!------------------------------------------------------------------------------- +!> Initialize observation object +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_InitOb(self, & ! out + nchans, & ! in + nlevels, & ! in + nprofelements, & ! in + nchans_all ) ! in + +implicit none + +! subroutine arguments: +class(ufo_rttovonedvarcheck_ob), intent(out) :: self !< observation metadata type +integer, intent(in) :: nchans !< number of channels used for this particular observation +integer, intent(in) :: nlevels !< number of levels in the profile +integer, intent(in) :: nprofelements !< number of profile elements used +integer :: nchans_all !< Size of all channels in ObsSpace + +character(len=*), parameter :: routinename = "ufo_rttovonedvarcheck_InitOb" +real(kind_real) :: missing + +missing = missing_value(missing) + +call self % delete() + +allocate(self % yobs(nchans)) +allocate(self % channels_used(nchans)) +allocate(self % channels_all(nchans_all)) +allocate(self % emiss(nchans_all)) +allocate(self % background_T(nlevels)) +allocate(self % output_profile(nprofelements)) +allocate(self % output_BT(nchans_all)) +allocate(self % background_BT(nchans_all)) +allocate(self % calc_emiss(nchans_all)) + +self % yobs(:) = missing +self % emiss(:) = zero +self % background_T(:) = missing +self % output_profile(:) = missing +self % output_BT(:) = missing +self % background_BT(:) = missing +self % calc_emiss(:) = .true. + +end subroutine ufo_rttovonedvarcheck_InitOb + +!------------------------------------------------------------------------------ +!> Delete the single observation object +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_DeleteOb(self) ! inout + +implicit none + +! subroutine arguments: +class(ufo_rttovonedvarcheck_ob), intent(inout) :: self !< observation metadata type + +character(len=*), parameter :: routinename = "ufo_rttovonedvarcheck_DeleteOb" +real(kind_real) :: missing + +missing = missing_value(missing) + +self % nlocs = 1 +self % latitude = missing +self % longitude = missing +self % elevation = missing +self % surface_type = 0 +self % niter = 0 +self % sensor_zenith_angle = missing +self % sensor_azimuth_angle = missing +self % solar_zenith_angle = missing +self % solar_azimuth_angle = missing +self % cloudtopp = 500.0_kind_real +self % cloudfrac = zero +self % final_cost = missing +self % LWP = missing +self % retrievecloud = .false. +self % mwscatt = .false. +self % mwscatt_totalice = .false. + +if (allocated(self % yobs)) deallocate(self % yobs) +if (allocated(self % channels_used)) deallocate(self % channels_used) +if (allocated(self % channels_all)) deallocate(self % channels_all) +if (allocated(self % emiss)) deallocate(self % emiss) +if (allocated(self % background_T)) deallocate(self % background_T) +if (allocated(self % output_profile)) deallocate(self % output_profile) +if (allocated(self % output_BT)) deallocate(self % output_BT) +if (allocated(self % background_BT)) deallocate(self % background_BT) +if (allocated(self % calc_emiss)) deallocate(self % calc_emiss) + +self % pcemis => null() + +end subroutine ufo_rttovonedvarcheck_DeleteOb + +!------------------------------------------------------------------------------ +!> Print information about the single observation object +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_PrintOb(self) + +implicit none + +! subroutine arguments: +class(ufo_rttovonedvarcheck_ob), intent(inout) :: self !< observation metadata type + +character(len=20) :: surface_type + +if (self % surface_type == RTLand) then + surface_type = "land" +else if (self % surface_type == RTSea) then + surface_type = "sea" +else if (self % surface_type == RTIce) then + surface_type = "ice" +else + surface_type = "unknown" +end if + +write(*,"(A,2F8.2)") "Lat,Long:",self % latitude, self % longitude +write(*,*) "Surface type for RTTOV: ",surface_type +write(*,"(A,F8.2)") "Surface height:",self % elevation +write(*,"(A,F8.2)") "Satellite zenith angle: ",self % sensor_zenith_angle +write(*,"(A,F8.2)") "Solar zenith angle: ",self % solar_zenith_angle +write(*,"(A)") "Background T profile: " +write(*,"(10F8.2)") self % background_T + +end subroutine + +!------------------------------------------------------------------------------- + +end module ufo_rttovonedvarcheck_ob_mod diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_obs_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_obs_mod.f90 new file mode 100644 index 000000000..ac59dd10a --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_obs_mod.f90 @@ -0,0 +1,507 @@ +! (C) copyright 2020 Met Office +! +! this software is licensed under the terms of the apache licence version 2.0 +! which can be obtained at http://www.apache.org/licenses/license-2.0. + +!> Fortran module which contains the observation data for a all the obs space + +module ufo_rttovonedvarcheck_obs_mod + +use kinds +use iso_c_binding +use missing_values_mod +use obsspace_mod +use oops_variables_mod +use ufo_constants_mod, only: zero, Pa_to_hPa +use ufo_geovals_mod +use ufo_rttovonedvarcheck_constants_mod +use ufo_rttovonedvarcheck_pcemis_mod +use ufo_rttovonedvarcheck_profindex_mod +use ufo_rttovonedvarcheck_utils_mod +use ufo_vars_mod + +implicit none +private + +! Ob info type definition +type, public :: ufo_rttovonedvarcheck_obs + +integer :: iloc +integer, allocatable :: QCflags(:,:) ! current qc flags needed for channel selection +real(kind_real), allocatable :: yobs(:,:) ! observation value from obs files +real(kind_real), allocatable :: ybias(:,:) ! observation bias from obs files +real(kind_real), allocatable :: lat(:) ! observation latitude +real(kind_real), allocatable :: lon(:) ! observation longitude +real(kind_real), allocatable :: elevation(:) ! observation elevation +real(kind_real), allocatable :: sat_zen(:) ! observation satellite zenith angle +real(kind_real), allocatable :: sat_azi(:) ! observation satellite azimuth angle +real(kind_real), allocatable :: sol_zen(:) ! observation solar zenith angle +real(kind_real), allocatable :: sol_azi(:) ! observation solar azimuth angle +integer, allocatable :: surface_type(:) ! surface type +integer, allocatable :: niter(:) ! number of iterations +real(kind_real), allocatable :: final_cost(:) ! final cost at solution +real(kind_real), allocatable :: LWP(:) ! liquid water path from final iteration +real(kind_real), allocatable :: emiss(:,:) ! initial surface emissivity +real(kind_real), allocatable :: output_profile(:,:) ! output profile +real(kind_real), allocatable :: output_BT(:,:) ! output brightness temperature +real(kind_real), allocatable :: background_BT(:,:) ! 1st iteration brightness temperature +logical, allocatable :: calc_emiss(:) ! flag to request RTTOV calculate first guess emissivity +logical :: Store1DVarLWP ! flag to output the LWP if the profile converges + +contains + procedure :: setup => ufo_rttovonedvarcheck_obs_setup + procedure :: delete => ufo_rttovonedvarcheck_obs_delete + procedure :: output => ufo_rttovonedvarcheck_obs_output + +end type + +contains + +!------------------------------------------------------------------------------- +!> Initialize observation object +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_obs_setup(self, & ! out + config, & ! in + nprofelements, & ! in + geovals, & ! in + vars, & ! in + ir_pcemis ) + +implicit none + +! subroutine arguments: +class(ufo_rttovonedvarcheck_obs), intent(out) :: self !< observation metadata type +type(ufo_rttovonedvarcheck), intent(in) :: config !< observation metadata type +integer, intent(in) :: nprofelements !< number of profile elements +type(ufo_geovals), intent(in) :: geovals !< model data at obs location +type(oops_variables), intent(in) :: vars !< channels for 1D-Var +type(ufo_rttovonedvarcheck_pcemis) :: ir_pcemis !< Infrared principal components object + +character(len=*), parameter :: routinename = "ufo_rttovonedvarcheck_obs_init" +real(kind_real) :: missing +integer :: jvar !< counters +integer :: jobs !< counters +character(len=max_string) :: var +character(len=max_string) :: varname +logical :: variable_present = .false. +logical :: model_surface_present = .false. +type(ufo_geoval), pointer :: geoval + +missing = missing_value(missing) +self % iloc = obsspace_get_nlocs(config % obsdb) + +! allocate arrays +allocate(self % yobs(config % nchans, self % iloc)) +allocate(self % ybias(config % nchans, self % iloc)) +allocate(self % QCflags(config % nchans, self % iloc)) +allocate(self % lat(self % iloc)) +allocate(self % lon(self % iloc)) +allocate(self % elevation(self % iloc)) +allocate(self % sat_zen(self % iloc)) +allocate(self % sat_azi(self % iloc)) +allocate(self % sol_zen(self % iloc)) +allocate(self % sol_azi(self % iloc)) +allocate(self % surface_type(self % iloc)) +allocate(self % niter(self % iloc)) +allocate(self % final_cost(self % iloc)) +allocate(self % LWP(self % iloc)) +allocate(self % emiss(config % nchans, self % iloc)) +allocate(self % output_profile(nprofelements, self % iloc)) +allocate(self % output_BT(config % nchans, self % iloc)) +allocate(self % background_BT(config % nchans, self % iloc)) +allocate(self % calc_emiss(self % iloc)) + +! initialize arrays +self % yobs(:,:) = missing +self % ybias(:,:) = zero +self % QCflags(:,:) = 0 +self % lat(:) = missing +self % lon(:) = missing +self % elevation(:) = missing +self % sat_zen(:) = missing +self % sat_azi(:) = zero +self % sol_zen(:) = zero +self % sol_azi(:) = zero +self % surface_type(:) = RTSea +self % niter(:) = 0 +self % final_cost(:) = missing +self % LWP(:) = missing +self % emiss(:,:) = zero +self % output_profile(:,:) = missing +self % output_BT(:,:) = missing +self % background_BT(:,:) = missing +self % calc_emiss(:) = .true. +self % Store1DVarLWP = config % Store1DVarLWP + +! read in observations and associated errors / biases for full ObsSpace +do jvar = 1, config % nchans + + var = vars % variable(jvar) + call obsspace_get_db(config % obsdb, "FortranQC", trim(var), self % QCflags(jvar,:)) + call obsspace_get_db(config % obsdb, "ObsValue", trim(var), self % yobs(jvar,:)) + + ! Optionally get the observation bias + variable_present = obsspace_has(config % obsdb, "ObsBias", trim(var)) + if (variable_present) then + call obsspace_get_db(config % obsdb, "ObsBias", trim(var), self % ybias(jvar,:)) + end if + +end do + +! The obs bias may contain missing values, and the intention is for those +! entries to be zero. Go through the bias data and replace the missing values +! with zero. +do jobs = 1, self % iloc + do jvar = 1, config % nchans + if (self % ybias(jvar, jobs) == missing) then + self % ybias(jvar, jobs) = zero + endif + enddo +enddo + +if (.not. variable_present) write(*,*) "Using uncorrected brightness temperature" + +! Subtract bias from the observations (apply bias correction) +self % yobs = self % yobs - self % ybias + +! Read in prerequisites +call obsspace_get_db(config % obsdb, "MetaData", "latitude", self % lat(:)) +call obsspace_get_db(config % obsdb, "MetaData", "longitude", self % lon(:)) +call obsspace_get_db(config % obsdb, "MetaData", "sensor_zenith_angle", self % sat_zen(:)) + +! Read in optional angles +variable_present = obsspace_has(config % obsdb, "MetaData", "sensor_azimuth_angle") +if (variable_present) then + call obsspace_get_db(config % obsdb, "MetaData", "sensor_azimuth_angle", self % sat_azi(:)) +end if + +variable_present = obsspace_has(config % obsdb, "MetaData", "solar_zenith_angle") +if (variable_present) then + call obsspace_get_db(config % obsdb, "MetaData", "solar_zenith_angle", self % sol_zen(:)) +end if + +variable_present = obsspace_has(config % obsdb, "MetaData", "solar_azimuth_angle") +if (variable_present) then + call obsspace_get_db(config % obsdb, "MetaData", "solar_azimuth_angle", self % sol_azi(:)) +end if + +! Read in elevation for all obs +if (obsspace_has(config % obsdb, "MetaData", "elevation")) then + call obsspace_get_db(config % obsdb, "MetaData", "elevation", self % elevation(:)) +else if (obsspace_has(config % obsdb, "MetaData", "surface_height")) then + call obsspace_get_db(config % obsdb, "MetaData", "surface_height", self % elevation(:)) +else if (obsspace_has(config % obsdb, "MetaData", "model_orography")) then + call obsspace_get_db(config % obsdb, "MetaData", "model_orography", self % elevation(:)) +else if (ufo_vars_getindex(geovals % variables, 'surface_altitude') > 0) then + call ufo_geovals_get_var(geovals, 'surface_altitude', geoval) + self % elevation(:) = geoval%vals(1, 1) +else + self % elevation(:) = zero +endif + +! Read in surface type from model data +call ufo_geovals_get_var(geovals, "surface_type", geoval) +self % surface_type(:) = geoval%vals(1, 1) + +! Setup emissivity +if (config % pcemiss) then + write(*,*) "PC emissivity being used" + call ufo_rttovonedvarcheck_obs_InitIREmiss(self, config % nchans, ir_pcemis) +else + write(*,*) "Conventional emissivity being used" + call ufo_rttovonedvarcheck_obs_InitMWEmiss(self, config) +end if + +end subroutine ufo_rttovonedvarcheck_obs_setup + +!------------------------------------------------------------------------------ +!> Delete the observation object +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_obs_delete(self) ! inout + +implicit none + +! subroutine arguments: +class(ufo_rttovonedvarcheck_obs), intent(inout) :: self !< observation metadata type + +character(len=*), parameter :: routinename = "ufo_rttovonedvarcheck_obs_delete" + +! deallocate arrays +if (allocated(self % QCflags)) deallocate(self % QCflags) +if (allocated(self % yobs)) deallocate(self % yobs) +if (allocated(self % ybias)) deallocate(self % ybias) +if (allocated(self % lat)) deallocate(self % lat) +if (allocated(self % lon)) deallocate(self % lon) +if (allocated(self % elevation)) deallocate(self % elevation) +if (allocated(self % sat_zen)) deallocate(self % sat_zen) +if (allocated(self % sat_azi)) deallocate(self % sat_azi) +if (allocated(self % sol_zen)) deallocate(self % sol_zen) +if (allocated(self % sol_azi)) deallocate(self % sol_azi) +if (allocated(self % surface_type)) deallocate(self % surface_type) +if (allocated(self % niter)) deallocate(self % niter) +if (allocated(self % final_cost)) deallocate(self % final_cost) +if (allocated(self % LWP)) deallocate(self % LWP) +if (allocated(self % emiss)) deallocate(self % emiss) +if (allocated(self % output_profile)) deallocate(self % output_profile) +if (allocated(self % output_BT)) deallocate(self % output_BT) +if (allocated(self % background_BT)) deallocate(self % background_BT) +if (allocated(self % calc_emiss)) deallocate(self % calc_emiss) + +end subroutine ufo_rttovonedvarcheck_obs_delete + +!------------------------------------------------------------------------------ +!> Initialize the microwave emissivity array +!! +!! \details Heritage: Ops_SatRad_InitEmissivity.f90 - the MW part only +!! +!! \author Met Office +!! +!! \date 06/08/2020: Created +!! +subroutine ufo_rttovonedvarcheck_obs_InitMWEmiss(self, config) + +implicit none + +! subroutine arguments: +type(ufo_rttovonedvarcheck_obs), intent(inout) :: self !< observation metadata type +type(ufo_rttovonedvarcheck), intent(in) :: config !< main rttovonedvarcheck type + +integer :: i + +!------------- +! 2.1 Defaults +!------------- + +do i = 1, self % iloc + + ! Only calculate in RTTOV over sea + if (self % surface_type(i) == RTSea) then + self % calc_emiss(i) = .true. + else + self % calc_emiss(i) = .false. + end if + + ! The default emissivity for land is a very crude estimate - the same + ! for all surface types and all frequencies. However, we do not use + ! channels which see the surface over land where we rely on this default. + self % emiss(:,i) = zero + if (self % surface_type(i) == RTLand) then + self % emiss(:,i) = config % EmissLandDefault + else if (self % surface_type(i) == RTIce) then + self % emiss(:,i) = config % EmissSeaIceDefault + end if + +end do + +end subroutine + +!------------------------------------------------------------------------------ +!> Initialize the infrared emissivity array +!! +!! \details Heritage: Ops_SatRad_InitEmissivity.f90 - the IR part only +!! +!! \author Met Office +!! +!! \date 06/08/2020: Created +!! +subroutine ufo_rttovonedvarcheck_obs_InitIREmiss(self, nchans, ir_pcemis) + +implicit none + +! subroutine arguments: +class(ufo_rttovonedvarcheck_obs), intent(inout) :: self !< observation metadata type +integer, intent(in) :: nchans !< total number of channels +type(ufo_rttovonedvarcheck_pcemis) :: ir_pcemis !< Infrared principal components object + +! local variables +real(kind_real), allocatable :: EmissPC(:,:) +integer :: i +integer :: nemisspc = 5 +integer :: emis_x +integer :: emis_y + +! Allocate and setup defaults - get RTTOV to calculate +self % emiss(:,:) = zero +self % calc_emiss(:) = .true. + +allocate(EmissPC(self % iloc,nemisspc)) + +!------------------------- +! 1.2 Principal components +!------------------------- + +! Initialise emissivity using principal components +if (ir_pcemis % initialised) then + + ! Don't do this if the RTTOV CAMEL atlas is being used. + ! Defaults above will let the RTTOV camel atlas be used. + !if (associated (ir_pcemiss % emis_eigen % PCGuess) .and. (.not. RTTOV_UseAtlas)) then + if (associated (ir_pcemis % emis_eigen % PCGuess)) then + + do i = 1, self % iloc + + ! Skip obs that may have invalid geographical coordinates + !IF (BTEST (Obs % QCflags(i), QC_ModelDomain)) CYCLE + + ! If there's an atlas available, try to use it. + if (associated (ir_pcemis % emis_atlas % EmisPC)) then + + ! Find the nearest lat/lon + emis_y = nint((self % lat(i) + 90.0_kind_real) / & + ir_pcemis % emis_atlas % gridstep + 1) + emis_x = nint((self % lon(i) + 180.0_kind_real) / & + ir_pcemis % emis_atlas % gridstep + 1) + if (emis_x > ir_pcemis % emis_atlas % nlon) then + emis_x = emis_x - ir_pcemis % emis_atlas % nlon + end if + + ! If the atlas is valid at this point, then use it, + ! otherwise use PCGuess. NB: missing or sea points are + ! flagged as -9.99 in the atlas. + if (any (ir_pcemis % emis_atlas % EmisPC(emis_x,emis_y,:) > -9.99_kind_real)) then + EmissPC(i,:) = ir_pcemis % emis_atlas % EmisPC(emis_x,emis_y,1:nemisspc) + else + EmissPC(i,:) = ir_pcemis % emis_eigen % PCGuess(1:nemisspc) + !! Flag invalid atlas points over land as bad surface + !IF (Obs % surface(i) == RTland) THEN + ! Obs % QCflags(i) = IBSET (Obs % QCflags(i), QC_BadSurface) + !END IF + END IF + + ELSE ! If no atlas present, use PCGuess. + EmissPC(i,:) = ir_pcemis % emis_eigen % PCGuess(1:nemisspc) + END IF + + end do + + end if + +end if + +if (allocated(EmissPC)) deallocate(EmissPC) + +end subroutine + +!------------------------------------------------------------------------------ +!> Store the 1D-Var analysis variables in obsspace for future assessment +!! +!! \details Heritage: Ops_SatRad_SetOutput_RTTOV12 +!! +!! \author Met Office +!! +!! \date 02/09/2020: Created +!! +subroutine ufo_rttovonedvarcheck_obs_output(self, obsdb, prof_index, vars, nchans) + +implicit none + +! subroutine arguments: +class(ufo_rttovonedvarcheck_obs), intent(inout) :: self !< observation metadata type +type(c_ptr), value, intent(in) :: obsdb !< pointer to the observation space +type(ufo_rttovonedvarcheck_profindex), intent(in) :: prof_index !< index to elements in the profile +type(oops_variables), intent(in) :: vars !< channels for 1D-Var +integer, intent(in) :: nchans ! number of channels in the obsspace + +! local variables +integer :: jvar ! counter +integer :: nobs ! number of observations to be written to database +character(len=max_string) :: var +real(kind_real), allocatable :: surface_pressure(:) +real(kind_real) :: missing + +missing = missing_value(missing) + +! Put QC flags and retrieved BT's back in database +do jvar = 1, nchans + var = vars % variable(jvar) + call obsspace_put_db(obsdb, "FortranQC", trim(var), self % QCflags(jvar,:)) + call obsspace_put_db(obsdb, "OneDVar", trim(var), self % output_BT(jvar,:)) + call obsspace_put_db(obsdb, "OneDVarBack", trim(var), self % background_BT(jvar,:)) +end do + +! Output Diagnostics +call obsspace_put_db(obsdb, "OneDVar", "FinalCost", self % final_cost(:)) +nobs = size(self % final_cost(:)) +call obsspace_put_db(obsdb, "OneDVar", "n_iterations", self % niter(:)) + +if (self % Store1DVarLWP) then + call obsspace_put_db(obsdb, "OneDVar", "LWP", self % LWP(:)) +end if + +!-- +! Output Retrieved profiles into ObsSpace +!-- +! 1) Temperature levels +! 2) Humidity levels: convert q to rh (%) +! 3) Ozone levels - no planned implementation +! 4) Cloud Liquid Water from qtotal +! 4b) Cloud Ice Water if rttovscat is activated from qtotal + +!-- +! 5) Surface pressure +!-- +if (prof_index % pstar > 0) THEN + allocate(surface_pressure(nobs)) + surface_pressure(:) = self % output_profile(prof_index % pstar, :) + where (surface_pressure /= missing) + surface_pressure = surface_pressure / Pa_to_hPa ! hPa to Pa + end where + call obsspace_put_db(obsdb, "OneDVar", trim(var_ps), surface_pressure) + deallocate(surface_pressure) +end if + +!-- +! 6) Surface temperature +!-- +if (prof_index % t2 > 0) THEN + call obsspace_put_db(obsdb, "OneDVar", trim(var_sfc_t2m), & + self % output_profile(prof_index % t2, :)) +end if + +!-- +! 7) Surface humidity +!-- + +!-- +! 8) Surface Windspeed +!-- +! Windspeed retrieval is directionless, i.e., there are no separate u and v +! components. +if (prof_index % windspeed > 0) then + call obsspace_put_db(obsdb, "OneDVar", "surface_wind_speed", & + self % output_profile(prof_index % windspeed, :)) +end if + +!-- +! 9) Skin temperature +!-- +if (prof_index % tstar > 0) then + call obsspace_put_db(obsdb, "OneDVar", trim(var_sfc_tskin), & + self % output_profile(prof_index % tstar, :)) +end if + +!-- +! 10) Total ozone - no planned implementation +! 11) Cloud top pressure +! 12) Cloud fraction +! 14) IWP only meaningful is mwscattswitch is activated which means model levels too.... +! 15) Microwave emissivity +! 16/17) QC related not being ported. +! 18) Cloud type +! 19) Channel information +! 20) RTTOVSCATT cloud profiles +! 21) IR cloud profiles + +end subroutine + +!------------------------------------------------------------------------------- + +end module ufo_rttovonedvarcheck_obs_mod diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_pcemis_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_pcemis_mod.f90 new file mode 100644 index 000000000..87c6e71f0 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_pcemis_mod.f90 @@ -0,0 +1,455 @@ +! (C) copyright 2020 Met Office +! +! this software is licensed under the terms of the apache licence version 2.0 +! which can be obtained at http://www.apache.org/licenses/license-2.0. + +!> Fortran module which contains the methods for Infrared +!> Principal Component Emissivity +!> The science can be found at: +!> Pavelin, E., Candy, B., 2014. Assimilation of surface-sensitive infrared radiances over land: +!> Estimation of land surface temperature and emissivity. Q. J. R. Metorol. Soc., 140, 1198-1208. + +module ufo_rttovonedvarcheck_pcemis_mod + +use fckit_log_module, only : fckit_log +use kinds +use ufo_rttovonedvarcheck_constants_mod +use ufo_utils_mod, only : ufo_utils_iogetfreeunit + +implicit none +private + +!< Emissivity eigen vector type definition +type, public :: ufo_rttovonedvarcheck_EmisEigenvec + integer :: NChans + integer :: NumEV + integer, pointer :: Channels(:) => null() + real(kind_real), pointer :: Mean(:) => null() + real(kind_real), pointer :: PCmin(:) => null() + real(kind_real), pointer :: PCmax(:) => null() + real(kind_real), pointer :: PCguess(:) => null() + real(kind_real), pointer :: EV(:,:) => null() + real(kind_real), pointer :: EV_Inverse(:,:) => null() +end type ufo_rttovonedvarcheck_EmisEigenvec + +!< Emissivity eigen vector atlas type definition +type ufo_rttovonedvarcheck_EmisAtlas + integer :: Nlat + integer :: Nlon + integer :: Npc + real(kind_real) :: gridstep + real(kind_real), POINTER :: EmisPC(:,:,:) +end type ufo_rttovonedvarcheck_EmisAtlas + +!< Principal component emissivity type definition +type, public :: ufo_rttovonedvarcheck_pcemis + + type(ufo_rttovonedvarcheck_EmisEigenvec) :: emis_eigen + type(ufo_rttovonedvarcheck_EmisAtlas) :: emis_atlas + logical :: initialised = .false. + +contains + procedure :: setup => ufo_rttovonedvarcheck_InitPCemis + procedure :: delete => ufo_rttovonedvarcheck_DeletePCemis + procedure :: info => ufo_rttovonedvarcheck_PrintPCemis + procedure :: emistopc => ufo_rttovonedvarcheck_EmisToPC + procedure :: pctoemis => ufo_rttovonedvarcheck_PCToEmis + procedure :: emisktopc => ufo_rttovonedvarcheck_EmisKToPC + +end type ufo_rttovonedvarcheck_pcemis + +contains + +!------------------------------------------------------------------------------- +!> Initialize PC emissivity object +!! +!! \author Met Office +!! +!! \date 04/08/2020: Created +!! +subroutine ufo_rttovonedvarcheck_InitPCEmis(self, filepath, atlaspath) + +implicit none + +! subroutine arguments: +class(ufo_rttovonedvarcheck_pcemis), intent(out) :: self !< PC emissivity type +character(len=*), intent(in) :: filepath +character(len=*), intent(in), optional :: atlaspath + +character(len=*), parameter :: routinename = "ufo_rttovonedvarcheck_InitPCEmis" +logical :: file_exists ! Check if a file exists logical +integer :: fileunit ! Unit number for reading in files + +! Read eigenvector file +inquire(file=trim(filepath), exist=file_exists) +if (file_exists) then + fileunit = ufo_utils_iogetfreeunit() + open(unit = fileunit, file = trim(filepath)) + call ufo_rttovonedvarcheck_GetEmisEigenVec(self, fileunit) + close(unit = fileunit) + call fckit_log % info("rttovonedvarcheck EmisEigenVec file exists and read in") +else + call abor1_ftn("rttovonedvarcheck EmisEigenVec file not found: aborting") +end if + +self % initialised = .true. + +! Read in emissivity atlas if file path present - +! if not a first guess will be used from eigenvector file +if (present(atlaspath)) then + inquire(file=trim(atlaspath), exist=file_exists) + if (file_exists) then + fileunit = ufo_utils_iogetfreeunit() + open(unit = fileunit, file = trim(filepath)) + call ufo_rttovonedvarcheck_GetEmisAtlas(self, fileunit) + close(unit = fileunit) + call fckit_log % info("rttovonedvarcheck Emis Atlas file exists and read in") + else + call abor1_ftn("rttovonedvarcheck Emis Atlas file not found but requested: aborting") + end if +end if + +call self % info() + +end subroutine ufo_rttovonedvarcheck_InitPCEmis + +!------------------------------------------------------------------------------- +!> Read the emissivity eigen vector from file +!! +!! Heritage: Ops_SatRad_GetEmisEigenVec +!! +!! \author Met Office +!! +!! \date 04/08/2020: Created +!! +subroutine ufo_rttovonedvarcheck_GetEmisEigenVec (self, & + fileunit ) + +implicit none + +! Subroutine arguments: +type(ufo_rttovonedvarcheck_pcemis), intent(out) :: self !< PC emissivity type +integer, intent(in) :: fileunit + +! Local declarations: +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_GetEmisEigenVec" +integer :: eigversion +integer :: i +integer :: readstatus +character(len=max_string) :: message + +!---------------------------------------------- +! 1. Read header information and allocate arrays +!---------------------------------------------- + +read(fileunit, *, iostat = readstatus) eigversion, self % emis_eigen % Nchans, & + self % emis_eigen % NumEV + +allocate (self % emis_eigen % Channels( self % emis_eigen % Nchans)) +allocate (self % emis_eigen % Mean( self % emis_eigen % Nchans)) +allocate (self % emis_eigen % PCmin( self % emis_eigen % NumEV)) +allocate (self % emis_eigen % PCmax( self % emis_eigen % NumEV)) +allocate (self % emis_eigen % PCguess( self % emis_eigen % NumEV)) +allocate (self % emis_eigen % EV( self % emis_eigen % NumEV, self % emis_eigen % Nchans)) +if (eigversion >= 2) then + allocate (self % emis_eigen % EV_Inverse( self % emis_eigen % Nchans, self % emis_eigen % NumEV)) +end if + +!-------------------------------------------------------- +! 2. Read the channels, mean emissivities and eigenvectors +!-------------------------------------------------------- + +read (fileunit, *, iostat = readstatus) self % emis_eigen % Channels(:) +read (fileunit, *, iostat = readstatus) self % emis_eigen % Mean(:) +read (fileunit, *, iostat = readstatus) self % emis_eigen % PCmin(:) +read (fileunit, *, iostat = readstatus) self % emis_eigen % PCmax(:) +read (fileunit, *, iostat = readstatus) self % emis_eigen % PCguess(:) +do i = 1, self % emis_eigen % NumEV + read (fileunit, *, iostat = readstatus) self % emis_eigen % EV(i,:) +end do + +! Has there been an error in the read? +if (readstatus /= 0) then + write(message,*) RoutineName, & + 'Problem reading in emis eigenvectors - please check the file ' + call abor1_ftn(message) +end if + +if (eigversion >= 2) then + do i = 1, self % emis_eigen % Nchans + read (fileunit, *, iostat = readstatus) self % emis_eigen % EV_Inverse(i,:) + end do + ! Has there been an error in the read? + if (readstatus /= 0) then + write(message,*) RoutineName, & + 'Problem reading in inverse emis eigenvectors - please check the file ' + call abor1_ftn(message) + end if +end if + +write(*, '(A,I0,A,I0,A)') 'Finished reading ',self % emis_eigen % NumEV, & + ' emissivity eigenvectors on ', & + self % emis_eigen % Nchans,' channels.' + +end subroutine ufo_rttovonedvarcheck_GetEmisEigenVec + +!------------------------------------------------------------------------------- +!> Read the emissivity eigen atlas from file +!! +!! Heritage: Ops_SatRad_GetEmisAtlas +!! +!! \author Met Office +!! +!! \date 16/09/2020: Created +!! +subroutine ufo_rttovonedvarcheck_GetEmisAtlas (self, fileunit) + +implicit none + +! Subroutine arguments: +type(ufo_rttovonedvarcheck_pcemis), intent(out) :: self !< PC emissivity type +integer, intent(in) :: fileunit + +! Local declarations: +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_GetEmisAtlas" +character(len=max_string) :: message +integer :: readstatus +integer :: i +integer :: j + +!---------------------------------------------- +! 1. Read header information and allocate arrays +!---------------------------------------------- + +read (fileunit, *, iostat = readstatus) self % emis_atlas % Nlat, & + self % emis_atlas % Nlon, & + self % emis_atlas % Npc, & + self % emis_atlas % gridstep + +allocate (self % emis_atlas % EmisPC(self % emis_atlas % Nlon, & + self % emis_atlas % Nlat, & + self % emis_atlas % Npc)) + +!-------------------------------------------------------- +! 2. Read the emissivity PCs +!-------------------------------------------------------- + +do i = 1, self % emis_atlas % nlon + do j = 1, self % emis_atlas % nlat + read (fileunit, '(12F10.6)', iostat = readstatus) self % emis_atlas % EmisPC(i,j,:) + end do +end do + +! Has there been an error in the read? +if (readstatus /= 0) then + write(message,*) RoutineName, & + 'Problem reading in EmisAtlas - please check the file' + call abor1_ftn(message) +else + write (*, '(A,I0,A)') 'Finished reading IR emissivity atlas with ', & + self % emis_atlas % Npc, ' principal components.' +end if + +end subroutine ufo_rttovonedvarcheck_GetEmisAtlas + +!------------------------------------------------------------------------------ +!> Delete the PC emissivity object +!! +!! \details Heritage: Ops_SatRad_KillEmisEigenVec +!! +!! \author Met Office +!! +!! \date 04/08/2020: Created +!! +subroutine ufo_rttovonedvarcheck_DeletePCEmis(self) ! inout + +implicit none + +! subroutine arguments: +class(ufo_rttovonedvarcheck_pcemis), intent(inout) :: self !< PC emissivity type + +character(len=*), parameter :: routinename = "ufo_rttovonedvarcheck_DeletePCEmis" + +self % emis_eigen % NChans = 0 +self % emis_eigen % NumEV = 0 +if (associated (self % emis_eigen % Channels)) deallocate (self % emis_eigen % Channels) +if (associated (self % emis_eigen % Mean)) deallocate (self % emis_eigen % Mean) +if (associated (self % emis_eigen % PCmin)) deallocate (self % emis_eigen % PCmin) +if (associated (self % emis_eigen % PCmax)) deallocate (self % emis_eigen % PCmax) +if (associated (self % emis_eigen % PCguess)) deallocate (self % emis_eigen % PCguess) +if (associated (self % emis_eigen % EV)) deallocate (self % emis_eigen % EV) +if (associated (self % emis_eigen % EV_Inverse)) deallocate (self % emis_eigen % EV_Inverse) + +end subroutine ufo_rttovonedvarcheck_DeletePCEmis + +!------------------------------------------------------------------------------ +!> Print information about the PC Emissivity object +!! +!! \author Met Office +!! +!! \date 04/08/2020: Created +!! +subroutine ufo_rttovonedvarcheck_PrintPCEmis(self) + +implicit none + +! subroutine arguments: +class(ufo_rttovonedvarcheck_pcemis), intent(inout) :: self !< PC emissivity type + +write(*,*) "Printing contents of PC emiss" +write(*,*) "emis_eigen % Channels = ",self % emis_eigen % Channels + +end subroutine ufo_rttovonedvarcheck_PrintPCEmis + +!------------------------------------------------------------------------------ +!> Convert from spectral emissivity to principal component weights. +!! This is used to convert CAMEL emissivities to PC weights for the 1D-Var. +!! +!! \details Heritage: Ops_SatRad_EmissToPC.f90 +!! +!! \author Met Office +!! +!! \date 04/08/2020: Created +!! +subroutine ufo_rttovonedvarcheck_EmisToPC (self, & + Channels, & + Emissivity, & + PC) + +implicit none + +! Subroutine arguments +class(ufo_rttovonedvarcheck_pcemis), intent(inout) :: self !< PC emissivity type +integer, intent(in) :: Channels(:) +real(kind_real), intent(in) :: Emissivity(:) +real(kind_real), intent(out) :: PC(:) + +! Local declarations: +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_EmisToPC" +real(kind_real) :: Temp_Emissivity(size(Emissivity)) +integer :: npc +character(len=max_string) :: message + +npc = size(PC) + +if (associated(self % emis_eigen % EV_Inverse)) then + + ! Convert from emissivity to sine transform + Temp_Emissivity(:) = asin( 2.0_kind_real * Emissivity(:) - 1.0_kind_real ) + + ! Subtract means from transformed emissivities + Temp_Emissivity(:) = Temp_Emissivity(:) - self % emis_eigen % Mean(Channels(:)) + + ! Calculate PC weights from emissivity spectrum + PC(1:npc) = matmul(Temp_Emissivity(:), self % emis_eigen % EV_Inverse(Channels(:),1:npc)) + +else + + write(message, *) RoutineName, & + 'Missing inverse eigenvector matrix - cannot convert emissivities to PCs' + call abor1_ftn(message) + +end if + +end subroutine ufo_rttovonedvarcheck_EmisToPC + +!------------------------------------------------------------------------------- +!> Transform from principal components to emissivity spectrum. +!! +!! \details Heritage: Ops_SatRad_PCToEmiss.f90 +!! +!! \author Met Office +!! +!! \date 04/08/2020: Created +!! +subroutine ufo_rttovonedvarcheck_PCToEmis (self, & + NumChans, & + Channels, & + NumPC, & + PC, & + Emissivity) + +implicit none + +! Subroutine arguments: +class(ufo_rttovonedvarcheck_pcemis), intent(inout) :: self !< PC emissivity type +integer, intent(in) :: NumChans +integer, intent(in) :: Channels(NumChans) +integer, intent(in) :: NumPC +real(kind_real), intent(in) :: PC(NumPC) +real(kind_real), intent(out) :: Emissivity(NumChans) + +! Local declarations: +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_PCToEmis" +real(kind_real) :: BigEOF(NumChans,NumChans) +real(kind_real) :: BigPC(NumChans) + +! Populate PC array with nchans elements +BigPC(:) = 0.0_kind_real +BigPC(1:NumPC) = PC(:) + +! Populate EOF array with nchans elements +BigEOF(:,:) = 0.0_kind_real +BigEOF(1:NumPC,:) = self % emis_eigen % EV(1:NumPC,Channels) + +! Calculate reconstructed emissivity spectrum +Emissivity = matmul(BigPC, BigEOF) + +! Add means (these may have been subtracted off, otherwise they are zero) +Emissivity(:) = Emissivity(:) + self % emis_eigen % Mean(Channels) + +! Convert from sine transform to physical emissivity +Emissivity(1:NumChans) = 0.5_kind_real * (sin(Emissivity(1:NumChans)) + 1.0_kind_real) + +end subroutine ufo_rttovonedvarcheck_PCToEmis + +!------------------------------------------------------------------------------- +!> Transform from principal components to emissivity spectrum. +!! +!! \details Heritage: Ops_SatRad_EmissKToPC.f90 +!! +!! \author Met Office +!! +!! \date 04/08/2020: Created +!! +subroutine ufo_rttovonedvarcheck_EmisKToPC (self, & + NumChans, & + Channels, & + NumPC, & + Emissivity, & + Emissivity_K, & + PC_K) + +implicit none + +! Subroutine arguments +class(ufo_rttovonedvarcheck_pcemis), intent(inout) :: self !< PC emissivity type +integer, intent(in) :: NumChans +integer, intent(in) :: Channels(NumChans) +integer, intent(in) :: NumPC +real(kind_real), intent(in) :: Emissivity(NumChans) +real(kind_real), intent(in) :: Emissivity_K(NumChans) +real(kind_real), intent(out) :: PC_K(NumChans,NumPC) + +! Local declarations: +character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_EmisKToPC" +real(kind_real) :: JEMatrix_element +integer :: ichan + +do ichan = 1, NumChans + ! Calculate diagonal matrix elements of emissivity Jacobians + ! Convert Emissivity_K to sine transform + ! cos(asin(2x-1)) === sqrt(1-(1-2x)^2) + JEMatrix_element = Emissivity_K(ichan) * 0.5_kind_real* & + sqrt (1.0_kind_real - (1.0_kind_real - 2.0_kind_real * Emissivity(ichan)) ** 2) + +! EOF array === EmisEigenvec % EV for the appropriate channel selection +! N.B. Note 'manual' transposition of matrix here + PC_K(ichan,1:NumPC) = self % emis_eigen % EV(1:NumPC,Channels(ichan)) * JEMatrix_element +end do + +end subroutine ufo_rttovonedvarcheck_EmisKToPC + +!------------------------------------------------------------------------------- + +end module ufo_rttovonedvarcheck_pcemis_mod diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_profindex_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_profindex_mod.f90 new file mode 100644 index 000000000..8f3eb388c --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_profindex_mod.f90 @@ -0,0 +1,239 @@ +! (C) British Crown Copyright 2017-2018 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module containing profile index + +module ufo_rttovonedvarcheck_profindex_mod + +use kinds +use fckit_log_module, only : fckit_log +use ufo_metoffice_bmatrixstatic_mod +use ufo_rttovonedvarcheck_constants_mod + +implicit none +private + +type, public :: ufo_rttovonedvarcheck_profindex + + ! general + integer :: nprofelements !< number of profile elements being used + integer :: nlevels !< number of model levels + + ! atmosphere (locate start and end points of relevant fields) + integer :: t(2) !< temperature profile + integer :: q(2) !< water vapour profile (specific humidity) + integer :: ql(2) !< liquid water profile + integer :: qt(2) !< total water profile + integer :: qi(2) !< frozen ice profile + integer :: cf(2) !< cloud fraction profile + integer :: o3total !< total column ozone + integer :: o3profile(2) !< ozone profile + integer :: lwp !< liquid water path + + ! surface + integer :: t2 !< screen temperature + integer :: q2 !< screen specific humidity + integer :: rh2 !< screen relative humidity + integer :: tstar !< skin temperature + integer :: pstar !< surface pressure + integer :: mwemiss(2) !< microwave emissivity + integer :: emisspc(2) !< emissivity principal components + integer :: windspeed !< surface windspeed + + ! cloud (single-level grey cloud only) + integer :: cloudtopp !< single-level cloud top pressure + integer :: cloudfrac !< effective cloud fraction + + ! other + integer :: t70hpa !< temperature at 70hpa - used for ozone profile, not currently implemented + integer :: t700hpa !< temperature at 700hpa - used for ozone profile, not currently implemented + integer :: t950hpa !< temperature at 950hpa - used for ozone profile, not currently implemented + integer :: t1000hpa !< temperature at 1000hpa - used for ozone profile, not currently implemented + integer :: qsurf !< surface specific humidity + +contains + procedure :: setup => ufo_rttovonedvarcheck_profindex_setup + procedure :: delete => ufo_rttovonedvarcheck_profindex_delete + +end type + +contains + +!------------------------------------------------------------------------------- +!> Profile index setup +!! +!! \details Heritage: Ops_SatRad_MapProfileToB +!! +!! Setup the profile index which requires the bmatrix object. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_profindex_setup(self, bmatrix, nlevels) + +implicit none + +! subroutine arguments: +class(ufo_rttovonedvarcheck_profindex), intent(inout) :: self !< profindex structure +type(ufo_metoffice_bmatrixstatic), intent(in) :: bmatrix !< state error covariances +integer, intent(in) :: nlevels !< number of model levels + +! local constants: +character(len=*), parameter :: routinename = "ufo_rttovonedvarcheck_profindex_setup" + +! local variables: +integer :: i,j +integer :: firstelement +integer :: lastelement +integer :: nelements + +! Initialise to zeros +call self % delete() +nelements = 0 +self % nlevels = nlevels + +!loop through fields in bmatrix. the number of elements that the field is composed of +!is held in bmatrix % fields(i,2). +do j = 1, bmatrix % nfields + firstelement = nelements + 1 + lastelement = nelements + bmatrix % fields(j,2) + + !assign start and end points. if the field wasn't found then assign a value of + !zero, which indicates absence. + + select case( bmatrix % fields(j,1) ) + + !---------- + !atmosphere (set start and end points for multi-level fields) + !---------- + + case( ufo_metoffice_fieldtype_t ) + self % t(1) = firstelement + self % t(2) = lastelement + + case( ufo_metoffice_fieldtype_q ) + self % q(1) = firstelement + self % q(2) = lastelement + + case( ufo_metoffice_fieldtype_ql ) + call abor1_ftn("ufo_metoffice_fieldtype_ql: Not currently implemented aborting") + + case( ufo_metoffice_fieldtype_qi ) + call abor1_ftn("ufo_metoffice_fieldtype_qi: Not currently implemented aborting") + + case( ufo_metoffice_fieldtype_cf ) + call abor1_ftn("ufo_metoffice_fieldtype_cf: Not currently implemented aborting") + + case( ufo_metoffice_fieldtype_qt ) + self % qt(1) = firstelement + self % qt(2) = lastelement + + case( ufo_metoffice_fieldtype_o3profile ) + call abor1_ftn("ufo_metoffice_fieldtype_o3profile: Not currently implemented aborting") + + case( ufo_metoffice_fieldtype_o3total ) + call abor1_ftn("ufo_metoffice_fieldtype_o3total: Not currently implemented aborting") + + !------- + !surface + !------- + + case( ufo_metoffice_fieldtype_t2 ) + self % t2 = firstelement + + case( ufo_metoffice_fieldtype_q2 ) + self % q2 = firstelement + + case( ufo_metoffice_fieldtype_tstar ) + self % tstar = firstelement + + case( ufo_metoffice_fieldtype_pstar ) + self % pstar = firstelement + + case( ufo_metoffice_fieldtype_windspeed ) + self % windspeed = firstelement + + case( ufo_metoffice_fieldtype_mwemiss ) + call abor1_ftn("ufo_metoffice_fieldtype_mwemiss: Not currently implemented aborting") + + case( ufo_metoffice_fieldtype_emisspc ) + call abor1_ftn("ufo_metoffice_fieldtype_emisspc: Not currently implemented aborting") + + !------------------------------------ + !cloud (single-level grey cloud only) + !------------------------------------ + + case( ufo_metoffice_fieldtype_cloudtopp ) + call abor1_ftn("ufo_metoffice_fieldtype_cloudtopp: Not currently implemented aborting") + + case( ufo_metoffice_fieldtype_cloudfrac ) + call abor1_ftn("ufo_metoffice_fieldtype_cloudfrac: Not currently implemented aborting") + + case( ufo_metoffice_fieldtype_not_used ) ! currently unused + continue + + case default + write(*,*) 'invalid field type in b matrix file: ',j + cycle + + end select + + if ( firstelement /= 0 ) nelements = nelements + bmatrix % fields(j,2) + +end do + +self % nprofelements = nelements + +end subroutine ufo_rttovonedvarcheck_profindex_setup + +!------------------------------------------------------------------------------- +!> Delete profile index +!! +!! \details Heritage: Ops_SatRad_InitProfInfo.f90 +!! +!! Reset profile index +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_profindex_delete(self) + +implicit none +class(ufo_rttovonedvarcheck_profindex), intent(inout) :: self !< profile index structure + +! Zero all values +self % nprofelements = 0 +self % t = 0 +self % q = 0 +self % ql = 0 +self % qt = 0 +self % qi = 0 +self % cf = 0 +self % o3total = 0 +self % o3profile = 0 +self % t2 = 0 +self % q2 = 0 +self % rh2 = 0 +self % tstar = 0 +self % pstar = 0 +self % windspeed = 0 +self % t70hpa = 0 +self % t700hpa = 0 +self % t950hpa = 0 +self % t1000hpa = 0 +self % qsurf = 0 +self % lwp = 0 +self % mwemiss = 0 +self % cloudtopp = 0 +self % cloudfrac = 0 +self % emisspc = 0 + +end subroutine ufo_rttovonedvarcheck_profindex_delete + +! ------------------------------------------------------------------------------ + +end module ufo_rttovonedvarcheck_profindex_mod diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_rsubmatrix_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_rsubmatrix_mod.f90 new file mode 100644 index 000000000..ba4cc7672 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_rsubmatrix_mod.f90 @@ -0,0 +1,313 @@ +! (C) British Crown Copyright 2017-2018 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran derived type to hold data for the observation covariance + +module ufo_rttovonedvarcheck_rsubmatrix_mod + +use kinds +use ufo_rttovonedvarcheck_constants_mod, only: max_string +use ufo_metoffice_rmatrixradiance_mod + +implicit none +private + +type, public :: ufo_rttovonedvarcheck_rsubmatrix + + integer :: nchans !< number of channels used in current r matrix + real(kind_real), allocatable :: matrix(:,:) !< full matrix + real(kind_real), allocatable :: inv_matrix(:,:) !< inverse full matrix + real(kind_real), allocatable :: diagonal(:) !< diagonal matrix + logical :: diagonal_flag !< flag to use diagonal r-matrix + logical :: full_flag !< flag to use full r-matrix + +contains + procedure :: setup => rsubmatrix_setup + procedure :: delete => rsubmatrix_delete + procedure :: info => rsubmatrix_print + procedure :: multiply_vector => rsubmatrix_multiply + procedure :: multiply_matrix => rsubmatrix_multiply_matrix + procedure :: multiply_inverse_vector => rsubmatrix_inv_multiply + procedure :: multiply_inverse_matrix => rsubmatrix_multiply_inv_matrix + procedure :: add_to_matrix => rsubmatrix_add_to_u + +end type ufo_rttovonedvarcheck_rsubmatrix + +! ------------------------------------------------------------------------------ +contains +! ------------------------------------------------------------------------------ +!> Setup for the r sub-matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rsubmatrix_setup(self, nchans, channels, full_rmatrix) + +implicit none +class(ufo_rttovonedvarcheck_rsubmatrix), intent(inout) :: self +integer, intent(in) :: nchans +integer, intent(in) :: channels(:) +type(ufo_metoffice_rmatrixradiance), intent(in) :: full_rmatrix + +character(len=*), parameter :: & + routinename = "rsubmatrix_setup" +character(len=max_string) :: err_msg +integer :: ii, ff, ss +character(len=max_string) :: mat_type + +self % nchans = nchans +self % full_flag = .false. +self % diagonal_flag = .false. + +! Get r matrix type from full matrix +if (full_rmatrix % rtype == 1) then + mat_type = "full" +else if (full_rmatrix % rtype == 2) then + mat_type = "diagonal" +else + call abor1_ftn('Unknown r matrix type') +end if + +! Setup correct r matrix +select case (trim(mat_type)) + case ("full") + ! full rmatrix not setup yet but in principle this is + ! a start as to how it would be initialised. + !allocate(self % matrix(nchans,nchans)) + !allocate(self % inv_matrix(nchans,nchans)) + !self % matrix(:,:) = 0.0_kind_real + !self % inv_matrix(:,:) = 0.0_kind_real + !self % full_flag = .true. + !do ii=1,nchans + ! self % matrix(ii,ii) = obs_error(ii) * obs_error(ii) + ! self % inv_matrix(ii,ii) = 1.0_kind_real / & + ! (obs_error(ii) * obs_error(ii)) + !end do + call abor1_ftn('full r matrix under development - use a diagonal') + case ("diagonal") + allocate(self % diagonal(nchans)) + self % diagonal(:) = 0.0_kind_real + self % diagonal_flag = .true. + + do ff=1,full_rmatrix % nchans + do ss=1,self % nchans + if (full_rmatrix % channels(ff) == channels(ss)) then + self % diagonal(ss) = full_rmatrix % errors(ff) * full_rmatrix % errors(ff) + end if + end do + end do + case default + call abor1_ftn('Unknown r matrix type') +end select + +end subroutine rsubmatrix_setup + +! ------------------------------------------------------------------------------ +!> Delete method for the r_matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rsubmatrix_delete(self) + +implicit none +class(ufo_rttovonedvarcheck_rsubmatrix), intent(inout) :: self !< R mtrix structure + +if (allocated(self % matrix)) deallocate(self % matrix) +if (allocated(self % inv_matrix)) deallocate(self % inv_matrix) +if (allocated(self % diagonal)) deallocate(self % diagonal) + +end subroutine rsubmatrix_delete + +! ------------------------------------------------------------------------------ +!> Multiply a vector by the r-matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rsubmatrix_multiply(self,xin,xout) + +implicit none +class(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: self +real(kind_real), intent(in) :: xin(:) +real(kind_real), intent(inout) :: xout(:) + +if (size(xout) /= self % nchans) then + call abor1_ftn("rsubmatrix_multiply: arrays incompatible sizes") +end if + +! Full R matrix +if (self % full_flag) xout(:) = matmul(xin(:), self % matrix(:,:)) + +! Diagonal R matrix +if (self % diagonal_flag) xout(:) = xin(:) * self % diagonal(:) + +end subroutine rsubmatrix_multiply + +! ------------------------------------------------------------------------------ +!> Multiply a matrix by the r-matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rsubmatrix_multiply_matrix(self,xin,xout) + +implicit none +class(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: self +real(kind_real), intent(in) :: xin(:,:) +real(kind_real), intent(inout) :: xout(:,:) + +integer :: ii + +if (size(xout, 2) /= self % nchans) then + call abor1_ftn("rsubmatrix_multiply_matrix: arrays incompatible sizes") +end if + +! Full R matrix +if (self % full_flag) xout = matmul(xin, self % matrix(:,:)) + +! Diagonal R matrix +if (self % diagonal_flag) then + do ii=1, self % nchans + xout(:,ii) = xin(:,ii) * self % diagonal(ii) + end do +end if + +end subroutine rsubmatrix_multiply_matrix + +! ------------------------------------------------------------------------------ +!> Multiply a vector by the inverse of the r-matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rsubmatrix_inv_multiply(self,xin,xout) + +implicit none +class(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: self +real(kind_real), intent(in) :: xin(:) +real(kind_real), intent(inout) :: xout(:) + +if (size(xout) /= self % nchans) then + call abor1_ftn("rsubmatrix_inv_multiply: arrays incompatible sizes") +end if + +! Full R matrix +if (self % full_flag) xout(:) = matmul(xin(:), self % inv_matrix(:,:)) + +! Diagonal R matrix +if (self % diagonal_flag) xout(:) = xin(:) / self % diagonal(:) + +end subroutine rsubmatrix_inv_multiply + +! ------------------------------------------------------------------------------ +!> Multiply a matrix by the inverse of the r-matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rsubmatrix_multiply_inv_matrix(self,xin,xout) + +implicit none +class(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: self +real(kind_real), intent(in) :: xin(:,:) +real(kind_real), intent(out) :: xout(:,:) + +integer :: ii + +if (size(xout, 2) /= self % nchans) then + call abor1_ftn("rsubmatrix_multiply_inv_matrix: arrays incompatible sizes") +end if + +! Full R matrix +if (self % full_flag) xout = matmul(xin, self % inv_matrix(:,:)) + +! Diagonal R matrix +if (self % diagonal_flag) then + do ii=1, self % nchans + xout(:,ii) = xin(:,ii) / self % diagonal(ii) + end do +end if + +end subroutine rsubmatrix_multiply_inv_matrix + +! ------------------------------------------------------------------------------ +!> Add a matrix to the r-matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rsubmatrix_add_to_u(self,uin,uout) + +implicit none +class(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: self +real(kind_real), intent(in) :: uin(:,:) +real(kind_real), intent(inout) :: uout(:,:) + +integer :: ii + +if (size(uout) /= self % nchans * self % nchans) then + call abor1_ftn("rsubmatrix_add_to_u: arrays incompatible sizes") +end if + +! Full R matrix +if (self % full_flag) uout = uin + self % matrix + +! Diagonal R matrix +if (self % diagonal_flag) then + do ii=1, self % nchans + uout(ii,ii) = uin(ii,ii) + self % diagonal(ii) + end do +end if + +end subroutine rsubmatrix_add_to_u + +! ------------------------------------------------------------------------------ +!> Print the contents of the r-matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rsubmatrix_print(self) + +implicit none +class(ufo_rttovonedvarcheck_rsubmatrix), intent(in) :: self + +integer :: ii + +if (self % full_flag) then + + write(*,*) "Full R matrix used printing diagonal" + write(*,*) "nchans = ",self % nchans + write(*,*) "Matrix diagonal elements = " + do ii = 1, self % nchans + write(*,*) self % matrix(ii,ii) + end do + write(*,*) "Inverse Matrix diagonal elements = " + do ii = 1, self % nchans + write(*,*) self % inv_matrix(ii,ii) + end do + +end if + +if (self % diagonal_flag) then + + write(*,*) "Diagonal R matrix used" + write(*,*) "nchans = ",self % nchans + write(*,*) "Diagonal = ",self % diagonal(:) + +end if + +end subroutine rsubmatrix_print + +end module ufo_rttovonedvarcheck_rsubmatrix_mod diff --git a/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_utils_mod.f90 b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_utils_mod.f90 new file mode 100644 index 000000000..665be5fb7 --- /dev/null +++ b/src/ufo/filters/rttovonedvarcheck/ufo_rttovonedvarcheck_utils_mod.f90 @@ -0,0 +1,246 @@ +! (C) copyright 2020 Met Office +! +! this software is licensed under the terms of the apache licence version 2.0 +! which can be obtained at http://www.apache.org/licenses/license-2.0. + +!> Fortran module containing main type, setup and utilities for the +!! main rttovonedvarcheck object + +module ufo_rttovonedvarcheck_utils_mod + +use fckit_configuration_module, only: fckit_configuration +use iso_c_binding +use kinds +use ufo_rttovonedvarcheck_constants_mod + +implicit none +private + +public ufo_rttovonedvarcheck_setup + +!=============================================================================== +! type definitions +!=============================================================================== + +!--------------- +! 1. 1DVar type +!--------------- + +type, public :: ufo_rttovonedvarcheck + character(len=max_string) :: qcname !< name of the filter + character(len=max_string) :: b_matrix_path !< path to the b-matrix file + character(len=max_string) :: r_matrix_path !< path to the r-matrix file + character(len=max_string) :: forward_mod_name !< forward model name (only RTTOV at the moment) + character(len=max_string), allocatable :: retrieval_variables(:) !< list of variables which form the 1D-var retrieval vector + type(c_ptr) :: obsdb !< pointer to the observation space + integer(c_int) :: onedvarflag !< flag uased by the qc manager for a 1D-var check + integer(c_int) :: passflag !< flag uased by the qc manager to flag good data + integer :: nlevels ! number 1D-Var model levels + integer :: nmvars !< number of variables being used in the retrieval + integer :: nchans !< maximum number of channels (channels can be removed by previous qc checks) + integer(c_int), allocatable :: channels(:) !< integer list of channels + integer :: StartOb !< starting ob number for testing + integer :: FinishOb !< finishing ob number for testing + logical :: qtotal !< flag to enable total humidity retrievals + logical :: UseQtsplitRain !< flag to choose whether to split rain in qsplit routine + logical :: RTTOV_mwscattSwitch !< flag to switch on RTTOV-scatt + logical :: RTTOV_usetotalice !< flag for use of total ice in RTTOV MW scatt + logical :: UseMLMinimization !< flag to turn on marquardt-levenberg minimizer + logical :: UseJforConvergence !< flag to Use J for convergence + logical :: UseRHwaterForQC !< flag to use water in relative humidity check + logical :: Store1DVarLWP !< Output the LWP if the profile converges + logical :: UseColdSurfaceCheck !< flag to use cold water check to adjust starting surface parameters + logical :: FullDiagnostics !< flag to turn on full diagnostics + logical :: pcemiss !< flag gets turned off in emissivity eigen vector file is present + integer :: Max1DVarIterations !< maximum number of iterations + integer :: JConvergenceOption !< integer to select convergence option + integer :: IterNumForLWPCheck !< choose which iteration to start checking LWP + integer :: MaxMLIterations !< maximum number of iterations for internal Marquardt-Levenberg loop + real(kind_real) :: ConvergenceFactor !< 1d-var convergence if using change in profile + real(kind_real) :: Cost_ConvergenceFactor !< 1d-var convergence if using % change in cost + real(kind_real) :: EmissLandDefault !< default emissivity value to use over land + real(kind_real) :: EmissSeaIceDefault !< default emissivity value to use over sea ice + character(len=max_string) :: EmisEigVecPath !< path to eigen vector file for IR PC emissivity + character(len=max_string) :: EmisAtlas !< path to the emissivity atlas for IR PC emissivity +end type ufo_rttovonedvarcheck + +contains + +!------------------------------------------------------------------------------ +!> Setup the defaults for the main rttovonedvarcheck object and read in the +!! contents of the yaml file. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_rttovonedvarcheck_setup(self, f_conf, channels) + +implicit none + +! subroutine arguments +type(ufo_rttovonedvarcheck), intent(inout) :: self +type(fckit_configuration), intent(in) :: f_conf !< yaml file contents +integer(c_int), intent(in) :: channels(:) + +! local variables +character(len=max_string) :: tmp +character(len=:), allocatable :: str +character(len=:), allocatable :: str_array(:) + +! Setup core paths and names +self % qcname = "rttovonedvarcheck" +call f_conf % get_or_die("BMatrix",str) +self % b_matrix_path = str +call f_conf % get_or_die("RMatrix",str) +self % r_matrix_path = str +call f_conf % get_or_die("ModName",str) +self % forward_mod_name = str +call f_conf % get_or_die("nlevels",self % nlevels) + +! Variables for profile (x,xb) +self % nmvars = f_conf % get_size("retrieval variables") +allocate(self % retrieval_variables(self % nmvars)) +call f_conf % get_or_die("retrieval variables", str_array) +self % retrieval_variables(1:self % nmvars) = str_array + +! Satellite channels +self % nchans = size(channels) +allocate(self % channels(self % nchans)) +self % channels(:) = channels(:) + +! Flag for total humidity +call f_conf % get_or_die("qtotal", self % qtotal) + +! Flag to choose whether to split rain in qsplit routine +call f_conf % get_or_die("UseQtSplitRain", self % UseQtsplitRain) + +! Flag for RTTOV MW scatt +call f_conf % get_or_die("RTTOVMWScattSwitch", self % RTTOV_mwscattSwitch) + +! Flag for use of total ice in RTTOV MW scatt +call f_conf % get_or_die("RTTOVUseTotalIce", self % RTTOV_usetotalice) + +! Flag to turn on marquardt-levenberg minimiser +call f_conf % get_or_die("UseMLMinimization", self % UseMLMinimization) + +! Flag to Use J for convergence +call f_conf % get_or_die("UseJforConvergence", self % UseJforConvergence) + +! Flag to use water in relative humidity check +call f_conf % get_or_die("UseRHwaterForQC", self % UseRHwaterForQC) + +! Flag to use cold water check to adjust starting surface parameters +call f_conf % get_or_die("UseColdSurfaceCheck", self % UseColdSurfaceCheck) + +! Flag to output the LWP if the profile converges +call f_conf % get_or_die("Store1DVarLWP", self % Store1DVarLWP) + +! Flag to turn on full diagnostics +call f_conf % get_or_die("FullDiagnostics", self % FullDiagnostics) + +! maximum number of iterations allowed +call f_conf % get_or_die("Max1DVarIterations", self % Max1DVarIterations) + +! integer to select convergence option +! 1= percentage change in cost tested between iterations +! otherwise = absolute change in cost tested between iterations +call f_conf % get_or_die("JConvergenceOption", self % JConvergenceOption) + +! Choose which iteration to start checking the liquid water path +call f_conf % get_or_die("IterNumForLWPCheck", self % IterNumForLWPCheck) + +! Convergence factor used when the absolute difference in the profile is used +! to determine convergence. +call f_conf % get_or_die("ConvergenceFactor", self % ConvergenceFactor) + +! Cost threshold for convergence check when cost function value is used for convergence +call f_conf % get_or_die("CostConvergenceFactor", self % Cost_ConvergenceFactor) + +! Maximum number of iterations for internal Marquardt-Levenberg loop +call f_conf % get_or_die("MaxMLIterations", self % MaxMLIterations) + +! Starting observation number for loop - used for testing +call f_conf % get_or_die("StartOb", self % StartOb) + +! Finishing observation number for loop - used for testing +call f_conf % get_or_die("FinishOb", self % FinishOb) + +! Default emissivity value to use over land +call f_conf % get_or_die("EmissLandDefault", self % EmissLandDefault) + +! Default emissivity value to use over seaice +call f_conf % get_or_die("EmissSeaIceDefault", self % EmissSeaIceDefault) + +! Default eigen value path is blank but needs to be present if using PC emiss +call f_conf % get_or_die("EmisEigVecPath",str) +self % EmisEigVecPath = str +self % pcemiss = .false. +if (len(trim(self % EmisEigVecPath)) > 4) then + self % pcemiss = .true. +end if + +! Default emis atlas path is blank +call f_conf % get_or_die("EmisAtlas",str) +self % EmisAtlas = str + +! Print self +if (self % FullDiagnostics) then + call ufo_rttovonedvarcheck_print(self) +end if + +end subroutine ufo_rttovonedvarcheck_setup + +!------------------------------------------------------------------------------ +!> Print contents of rttovonedvarcheck object +!! +!! \author Met Office +!! +!! \date 03/08/2020: Created +!! +subroutine ufo_rttovonedvarcheck_print(self) + +implicit none + +type(ufo_rttovonedvarcheck), intent(in) :: self + +integer :: ii + +write(*,*) "qcname = ", trim(self % qcname) +write(*,*) "b_matrix_path = ", trim(self % b_matrix_path) +write(*,*) "r_matrix_path = ", trim(self % r_matrix_path) +write(*,*) "forward_mod_name = ", trim(self % forward_mod_name) +write(*,*) "retrieval_variables = " +do ii = 1, self % nmvars + write(*,*) trim(self % retrieval_variables(ii))," " +end do +write(*,*) "nlevels = ",self % nlevels +write(*,*) "nmvars = ",self % nmvars +write(*,*) "nchans = ",self % nchans +write(*,*) "channels(:) = ",self % channels(:) +write(*,*) "qtotal = ",self % qtotal +write(*,*) "RTTOV_mwscattSwitch = ",self % RTTOV_mwscattSwitch +write(*,*) "RTTOV_usetotalice = ",self % RTTOV_usetotalice +write(*,*) "UseMLMinimization = ",self % UseMLMinimization +write(*,*) "UseJforConvergence = ",self % UseJforConvergence +write(*,*) "UseRHwaterForQC = ", self % UseRHwaterForQC +write(*,*) "UseColdSurfaceCheck = ", self % UseColdSurfaceCheck +write(*,*) "UseQtsplitRain = ",self % UseQtsplitRain +write(*,*) "FullDiagnostics = ",self % FullDiagnostics +write(*,*) "Max1DVarIterations = ",self % Max1DVarIterations +write(*,*) "JConvergenceOption = ",self % JConvergenceOption +write(*,*) "IterNumForLWPCheck = ",self % IterNumForLWPCheck +write(*,*) "ConvergenceFactor = ",self % ConvergenceFactor +write(*,*) "CostConvergenceFactor = ",self % Cost_ConvergenceFactor +write(*,*) "MaxMLIterations = ",self % MaxMLIterations +write(*,*) "EmissLandDefault = ",self % EmissLandDefault +write(*,*) "EmissSeaIceDefault = ",self % EmissSeaIceDefault +write(*,*) "Use PC for Emissivity = ", self % pcemiss +write(*,*) "EmisEigVecPath = ",self % EmisEigVecPath +write(*,*) "EmisAtlas = ",self % EmisAtlas + +end subroutine ufo_rttovonedvarcheck_print + +! ------------------------------------------------------------------------------ + +end module ufo_rttovonedvarcheck_utils_mod diff --git a/src/ufo/geos_aero/CMakeLists.txt b/src/ufo/geos_aero/CMakeLists.txt deleted file mode 100644 index 1ca05af80..000000000 --- a/src/ufo/geos_aero/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -# (C) Copyright 2019 UCAR. -# -# This software is licensed under the terms of the Apache Licence Version 2.0 -# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - -set ( geosaod_files - ObsGeosAod.h - ObsGeosAod.cc - ObsGeosAodTLAD.h - ObsGeosAodTLAD.cc - ObsGeosAod.interface.F90 - ObsGeosAod.interface.h - ObsGeosAodTLAD.interface.F90 - ObsGeosAodTLAD.interface.h - ufo_geosaod_mod.F90 - ufo_geosaod_tlad_mod.F90 -) - -PREPEND( _p_geosaod_files "geos_aero" ${geosaod_files} ) - -set ( geosaod_src_files - ${_p_geosaod_files} - PARENT_SCOPE -) diff --git a/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.cc b/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.cc index 80fbb8823..b5100d9a5 100644 --- a/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.cc +++ b/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.cc @@ -50,9 +50,11 @@ ObsGnssroBendMetOffice::~ObsGnssroBendMetOffice() { // ----------------------------------------------------------------------------- void ObsGnssroBendMetOffice::simulateObs(const GeoVaLs & gom, ioda::ObsVector & ovec, - ObsDiagnostics &) const { + ObsDiagnostics & ydiags) const { + oops::Log::trace() << "Starting simulateObs" << std::endl; ufo_gnssro_bendmetoffice_simobs_f90(keyOperGnssroBendMetOffice_, gom.toFortran(), odb_, - ovec.size(), ovec.toFortran()); + ovec.size(), ovec.toFortran(), ydiags.toFortran()); + oops::Log::trace() << "Finishing simulateObs" << std::endl; } // ----------------------------------------------------------------------------- diff --git a/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.interface.F90 b/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.interface.F90 index 7e7a3710b..8befcb37d 100644 --- a/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.interface.F90 +++ b/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.interface.F90 @@ -8,8 +8,11 @@ module ufo_gnssro_bendmetoffice_mod_c use fckit_configuration_module, only: fckit_configuration + use fckit_log_module, only : fckit_log use iso_c_binding use ufo_gnssro_bendmetoffice_mod + use ufo_geovals_mod + use ufo_geovals_mod_c, only: ufo_geovals_registry implicit none private @@ -60,23 +63,34 @@ end subroutine ufo_gnssro_bendmetoffice_delete_c ! ------------------------------------------------------------------------------ subroutine ufo_gnssro_bendmetoffice_simobs_c(c_key_self, c_key_geovals, c_obsspace, & - c_nobs, c_hofx, c_vert_interp_ops, & - c_pseudo_ops) bind(c,name='ufo_gnssro_bendmetoffice_simobs_f90') + c_nobs, c_hofx, c_key_obs_diags) & + bind(c,name='ufo_gnssro_bendmetoffice_simobs_f90') implicit none -integer(c_int), intent(in) :: c_key_self -integer(c_int), intent(in) :: c_key_geovals -type(c_ptr), value, intent(in) :: c_obsspace -integer(c_int), intent(in) :: c_nobs -real(c_double), intent(inout) :: c_hofx(c_nobs) -logical, intent(in) :: c_vert_interp_ops -logical, intent(in) :: c_pseudo_ops - -type(ufo_gnssro_BendMetOffice), pointer :: self - -character(len=*), parameter :: myname_="ufo_gnssro_bendmetoffice_simobs_c" -call ufo_gnssro_BendMetOffice_registry%get(c_key_self, self) -call self%opr_simobs(c_key_geovals, c_obsspace, c_hofx) +integer(c_int), intent(in) :: c_key_self ! Key giving pointer to self object +integer(c_int), intent(in) :: c_key_geovals ! Key giving pointer to geovals object +type(c_ptr), value, intent(in) :: c_obsspace ! Pointer to obs-space object +integer(c_int), intent(in) :: c_nobs ! Number of observations +real(c_double), intent(inout) :: c_hofx(c_nobs) ! Array of calculated H(x) object +integer(c_int), intent(in) :: c_key_obs_diags ! Key giving pointer to obs diagnostics object + +type(ufo_gnssro_BendMetOffice), pointer :: self ! Self object +type(ufo_geovals), pointer :: obs_diags ! Observations diagnostics +type(ufo_geovals), pointer :: geovals ! Geovals object +character(len=*), parameter :: myname_="ufo_gnssro_bendmetoffice_simobs_c" +character(len=200) :: output_message ! Message to be output + +write(output_message, *) 'TRACE: Beginning interface', c_key_obs_diags, c_key_geovals, c_key_self +call fckit_log % info(output_message) + +call ufo_gnssro_BendMetOffice_registry % get(c_key_self, self) +call ufo_geovals_registry % get(c_key_obs_diags, obs_diags) +call ufo_geovals_registry % get(c_key_geovals, geovals) + +call self%simobs(geovals, c_obsspace, c_hofx, obs_diags) + +write(output_message, *) 'TRACE: Finishing interface' +call fckit_log % info(output_message) end subroutine ufo_gnssro_bendmetoffice_simobs_c diff --git a/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.interface.h b/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.interface.h index 24a52d909..ab39d48a9 100644 --- a/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.interface.h +++ b/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOffice.interface.h @@ -26,7 +26,7 @@ extern "C" { void ufo_gnssro_bendmetoffice_setup_f90(F90hop &, const eckit::Configuration * const *); void ufo_gnssro_bendmetoffice_delete_f90(F90hop &); void ufo_gnssro_bendmetoffice_simobs_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, - const int &, double &); + const int &, double &, const F90goms &); // ----------------------------------------------------------------------------- } // extern C diff --git a/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOfficeTLAD.cc b/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOfficeTLAD.cc index c4a0980ac..a56358bfe 100644 --- a/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOfficeTLAD.cc +++ b/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOfficeTLAD.cc @@ -29,7 +29,7 @@ static LinearObsOperatorMaker ObsGnssroBendMetOfficeTLAD::ObsGnssroBendMetOfficeTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOperGnssroBendMetOffice_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOperGnssroBendMetOffice_(0), varin_() { const eckit::LocalConfiguration obsOptions(config, "obs options"); const eckit::Configuration * configc = &obsOptions; @@ -54,23 +54,24 @@ ObsGnssroBendMetOfficeTLAD::~ObsGnssroBendMetOfficeTLAD() { void ObsGnssroBendMetOfficeTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_gnssro_bendmetoffice_tlad_settraj_f90(keyOperGnssroBendMetOffice_, geovals.toFortran(), odb_); + ufo_gnssro_bendmetoffice_tlad_settraj_f90(keyOperGnssroBendMetOffice_, geovals.toFortran(), + obsspace()); } // ----------------------------------------------------------------------------- void ObsGnssroBendMetOfficeTLAD::simulateObsTL( const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_gnssro_bendmetoffice_simobs_tl_f90(keyOperGnssroBendMetOffice_, geovals.toFortran(), odb_, - ovec.size(), ovec.toFortran()); + ufo_gnssro_bendmetoffice_simobs_tl_f90(keyOperGnssroBendMetOffice_, geovals.toFortran(), + obsspace(), ovec.size(), ovec.toFortran()); } // ----------------------------------------------------------------------------- void ObsGnssroBendMetOfficeTLAD::simulateObsAD( GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_gnssro_bendmetoffice_simobs_ad_f90(keyOperGnssroBendMetOffice_, geovals.toFortran(), odb_, - ovec.size(), ovec.toFortran()); + ufo_gnssro_bendmetoffice_simobs_ad_f90(keyOperGnssroBendMetOffice_, geovals.toFortran(), + obsspace(), ovec.size(), ovec.toFortran()); } // ----------------------------------------------------------------------------- diff --git a/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOfficeTLAD.h b/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOfficeTLAD.h index 5cb852a38..7c215a185 100644 --- a/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOfficeTLAD.h +++ b/src/ufo/gnssro/BendMetOffice/ObsGnssroBendMetOfficeTLAD.h @@ -56,7 +56,6 @@ class ObsGnssroBendMetOfficeTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOperGnssroBendMetOffice_; - const ioda::ObsSpace& odb_; std::unique_ptr varin_; }; diff --git a/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_mod.F90 b/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_mod.F90 index 3b09ca596..f92da7f00 100644 --- a/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_mod.F90 +++ b/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_mod.F90 @@ -14,12 +14,12 @@ module ufo_gnssro_bendmetoffice_mod use ufo_vars_mod use ufo_geovals_mod use ufo_geovals_mod_c, only: ufo_geovals_registry -use ufo_basis_mod, only: ufo_basis use vert_interp_mod use lag_interp_mod, only: lag_interp_const, lag_interp_smthWeights use obsspace_mod use missing_values_mod use ufo_gnssro_ukmo1d_utils_mod +use ufo_utils_refractivity_calculator, only: ufo_calculate_refractivity use fckit_log_module, only : fckit_log implicit none @@ -27,9 +27,10 @@ module ufo_gnssro_bendmetoffice_mod private !> Fortran derived type for gnssro trajectory -type, extends(ufo_basis) :: ufo_gnssro_BendMetOffice +type :: ufo_gnssro_BendMetOffice logical :: vert_interp_ops logical :: pseudo_ops + real(kind_real) :: min_temp_grad contains procedure :: setup => ufo_gnssro_bendmetoffice_setup procedure :: simobs => ufo_gnssro_bendmetoffice_simobs @@ -50,21 +51,23 @@ subroutine ufo_gnssro_bendmetoffice_setup(self, f_conf) call f_conf%get_or_die("vert_interp_ops", self % vert_interp_ops) call f_conf%get_or_die("pseudo_ops", self % pseudo_ops) +call f_conf%get_or_die("min_temp_grad", self % min_temp_grad) end subroutine ufo_gnssro_bendmetoffice_setup ! ------------------------------------------------------------------------------ ! 1-dimensional GNSS-RO forward operator for the Met Office system ! ------------------------------------------------------------------------------ -subroutine ufo_gnssro_bendmetoffice_simobs(self, geovals, hofx, obss) +subroutine ufo_gnssro_bendmetoffice_simobs(self, geovals, obss, hofx, obs_diags) implicit none ! Arguments to this routine - class(ufo_gnssro_BendMetOffice), intent(in) :: self ! The object in which this operator is contained - type(ufo_geovals), intent(in) :: geovals ! The model values, interpolated to the obsevation locations - real(kind_real), intent(inout) :: hofx(:) ! The model forecast of the observations - type(c_ptr), value, intent(in) :: obss ! The observations, and meta-data for those observations + class(ufo_gnssro_BendMetOffice), intent(in) :: self ! The object in which this operator is contained + type(ufo_geovals), intent(in) :: geovals ! The model values, interpolated to the observation locations + real(kind_real), intent(inout) :: hofx(:) ! The model forecast of the observations + type(c_ptr), value, intent(in) :: obss ! The observations, and meta-data for those observations + type(ufo_geovals), intent(inout) :: obs_diags ! Observations diagnostics character(len=*), parameter :: myname_ = "ufo_gnssro_bendmetoffice_simobs" integer, parameter :: max_string = 800 @@ -85,10 +88,26 @@ subroutine ufo_gnssro_bendmetoffice_simobs(self, geovals, hofx, obss) real(kind_real), allocatable :: undulation(:) ! Undulation - height of the geoid above the ellipsoid logical :: flip_data ! Whether to reverse the order of the model data logical :: BAErr ! Was there an error in the calculation? + integer :: iVar ! Loop variable, obs diagnostics variable number + real(kind_real), allocatable :: refractivity(:) ! Refractivity on various model levels + real(kind_real), allocatable :: model_heights(:) ! Geopotential heights that refractivity is calculated on write(err_msg,*) "TRACE: ufo_gnssro_bendmetoffice_simobs: begin" call fckit_log%info(err_msg) + ! If output to refractivity (and heights of the refractivity levels) is needed, + ! then use nval as a way to check whether the array has been initialised (since + ! it is called in a loop). + DO iVar = 1, obs_diags % nvar + IF (obs_diags % variables(ivar) == "refractivity" .OR. & + obs_diags % variables(ivar) == "model_heights") THEN + write(err_msg,*) "TRACE: ufo_gnssro_bendmetoffice_simobs: initialising obs_diags for " // & + obs_diags % variables(ivar) + call fckit_log%info(err_msg) + obs_diags % geovals(iVar) % nval = 0 + END IF + END DO + ! check if nlocs is consistent in geovals & hofx if (geovals%nlocs /= size(hofx)) then write(err_msg,*) myname_, ' error: nlocs inconsistent!' @@ -136,12 +155,8 @@ subroutine ufo_gnssro_bendmetoffice_simobs(self, geovals, hofx, obss) call obsspace_get_db(obss, "MetaData", "earth_radius_of_curvature", radius_curv) call obsspace_get_db(obss, "MetaData", "geoid_height_above_reference_ellipsoid", undulation) - call fckit_log%info(err_msg) - obs_loop: do iobs = 1, nobs - call fckit_log%info(err_msg) - if (flip_data) then call Ops_GPSRO_ForwardModel(prs % nval, & q % nval, & @@ -151,13 +166,16 @@ subroutine ufo_gnssro_bendmetoffice_simobs(self, geovals, hofx, obss) q % vals(q%nval:1:-1, iobs), & self % pseudo_ops, & self % vert_interp_ops, & + self % min_temp_grad, & 1, & impact_param(iobs:iobs), & radius_curv(iobs), & obsLat(iobs), & undulation(iobs), & hofx(iobs:iobs), & - BAErr) + BAErr, & + refractivity, & + model_heights) else call Ops_GPSRO_ForwardModel(prs % nval, & q % nval, & @@ -167,13 +185,16 @@ subroutine ufo_gnssro_bendmetoffice_simobs(self, geovals, hofx, obss) q % vals(:,iobs), & self % pseudo_ops, & self % vert_interp_ops, & + self % min_temp_grad, & 1, & impact_param(iobs:iobs), & radius_curv(iobs), & obsLat(iobs), & undulation(iobs), & hofx(iobs:iobs), & - BAErr) + BAErr, & + refractivity, & + model_heights) end if if (BAErr) then @@ -181,6 +202,34 @@ subroutine ufo_gnssro_bendmetoffice_simobs(self, geovals, hofx, obss) call fckit_log % info(err_msg) end if + ! If output to refractivity is needed, then initialise things + DO iVar = 1, obs_diags % nvar + IF (obs_diags % variables(ivar) == "refractivity") THEN + IF (iobs == 1) THEN + obs_diags % geovals(iVar) % nval = SIZE(refractivity) + ALLOCATE(obs_diags % geovals(iVar) % vals(SIZE(refractivity), obs_diags % nlocs)) + END IF + + IF (BAerr) THEN + obs_diags % geovals(iVar) % vals(:,iobs) = missing_value(obs_diags % geovals(iVar) % vals(1,1)) + ELSE + obs_diags % geovals(iVar) % vals(:,iobs) = refractivity(:) + END IF + END IF + + IF (obs_diags % variables(ivar) == "model_heights") THEN + IF (iobs == 1) THEN + obs_diags % geovals(iVar) % nval = SIZE(model_heights) + ALLOCATE(obs_diags % geovals(iVar) % vals(SIZE(model_heights), obs_diags % nlocs)) + END IF + + IF (BAerr) THEN + obs_diags % geovals(iVar) % vals(:,iobs) = missing_value(obs_diags % geovals(iVar) % vals(1,1)) + ELSE + obs_diags % geovals(iVar) % vals(:,iobs) = model_heights(:) + END IF + END IF + END DO end do obs_loop deallocate(obsLat) @@ -196,7 +245,7 @@ end subroutine ufo_gnssro_bendmetoffice_simobs ! ------------------------------------------------------------------------------ -SUBROUTINE Ops_GPSRO_ForwardModel(nlevP, & +SUBROUTINE Ops_GPSRO_ForwardModel(nlevp, & nlevq, & za, & zb, & @@ -204,22 +253,26 @@ SUBROUTINE Ops_GPSRO_ForwardModel(nlevP, & humidity, & GPSRO_pseudo_ops, & GPSRO_vert_interp_ops, & + GPSRO_min_temp_grad, & nobs, & zobs, & RO_Rad_Curv, & Latitude, & RO_geoid_und, & ycalc, & - BAErr) + BAErr, & + refractivity, & + model_heights) -INTEGER, INTENT(IN) :: nlevP ! no. of p levels in state vec. +INTEGER, INTENT(IN) :: nlevp ! no. of p levels in state vec. INTEGER, INTENT(IN) :: nlevq ! no. of theta levels -REAL(kind_real), INTENT(IN) :: za(1:nlevP) ! heights of rho levs -REAL(kind_real), INTENT(IN) :: zb(1:nlevQ) ! heights of theta levs -REAL(kind_real), INTENT(IN) :: pressure(1:nlevP) ! Model background pressure -REAL(kind_real), INTENT(IN) :: humidity(1:nlevQ) ! Model background specific humidity +REAL(kind_real), INTENT(IN) :: za(1:nlevp) ! heights of rho levs +REAL(kind_real), INTENT(IN) :: zb(1:nlevq) ! heights of theta levs +REAL(kind_real), INTENT(IN) :: pressure(1:nlevp) ! Model background pressure +REAL(kind_real), INTENT(IN) :: humidity(1:nlevq) ! Model background specific humidity LOGICAL, INTENT(IN) :: GPSRO_pseudo_ops ! Option: Use pseudo-levels in vertical interpolation? LOGICAL, INTENT(IN) :: GPSRO_vert_interp_ops ! Option: Use ln(p) for vertical interpolation? (rather than exner) +REAL(kind_real), INTENT(IN) :: GPSRO_min_temp_grad ! The minimum temperature gradient which is used INTEGER, INTENT(IN) :: nobs ! Number of observations in the profile REAL(kind_real), INTENT(IN) :: zobs(1:nobs) ! Impact parameter for the obs REAL(kind_real), INTENT(IN) :: RO_Rad_Curv ! Earth's radius of curvature for these observations @@ -227,15 +280,13 @@ SUBROUTINE Ops_GPSRO_ForwardModel(nlevP, & REAL(kind_real), INTENT(IN) :: RO_geoid_und ! Undulation - difference between the geoid and the ellipsoid REAL(kind_real), INTENT(INOUT) :: ycalc(1:nobs) ! Model forecast of the observations LOGICAL, INTENT(OUT) :: BAErr ! Was an error encountered during the calculation? -! +REAL(kind_real), INTENT(INOUT), ALLOCATABLE :: refractivity(:) ! Refractivity as calculated +REAL(kind_real), INTENT(INOUT), ALLOCATABLE :: model_heights(:) ! Height of the levels for refractivity +! ! Things that may need to be output, as they are used by the TL/AD calculation ! -REAL(kind_real), ALLOCATABLE :: z_pseudo(:) ! Heights of the pseudo levels | Allocated by -REAL(kind_real), ALLOCATABLE :: N_pseudo(:) ! Refractivity on the pseudo levels | Ops_GPSRO_refrac -INTEGER :: nb_pseudo ! Number of pseudo levels -REAL(kind_real) :: T(1:nlevq) ! Temperature on model levels -REAL(kind_real), ALLOCATABLE :: nr(:) ! Model calculation of impact parameters -REAL(kind_real) :: Refmodel(1:nlevq) ! model refractivity on theta levels +INTEGER :: nRefLevels ! Number of levels in refractivity calculation +REAL(kind_real), ALLOCATABLE :: nr(:) ! Model calculation of impact parameters ! ! Local parameters ! @@ -244,89 +295,54 @@ SUBROUTINE Ops_GPSRO_ForwardModel(nlevP, & ! ! Local variables ! -INTEGER :: nstate ! no. of levels in state vec. INTEGER :: num_pseudo ! Number of levels, including pseudo levels -INTEGER :: nb ! no. of non-pseudo levs -REAL(kind_real) :: x(1:nlevP+nlevQ) ! state vector +REAL(kind_real) :: x(1:nlevp+nlevq) ! state vector character(max_string) :: err_msg ! Error message to be output ! The model data must be on a staggered grid, with nlevp = nlevq+1 -IF (nlevP /= nlevQ + 1) THEN +IF (nlevp /= nlevq + 1) THEN write(err_msg,*) myname_ // ':' // ' Data must be on a staggered grid nlevp, nlevq = ', nlevp, nlevq call fckit_log % warning(err_msg) write(err_msg,*) myname_ // ':' // ' error: number of levels inconsistent!' call abor1_ftn(err_msg) END IF -nstate = nlevP + nlevq -nb = nlevq -x(1:nlevP) = pressure -x(nlevP+1:nstate) = humidity - -IF (GPSRO_pseudo_ops) THEN - num_pseudo = 2 * nlevq - 1 -ELSE - num_pseudo = nlevq -END IF -ALLOCATE(nr(1:num_pseudo)) - BAErr = .FALSE. -CALL Ops_GPSRO_refrac (nstate, & - nlevP, & - nb, & - nlevq, & - za, & - zb, & - x, & - GPSRO_pseudo_ops, & - GPSRO_vert_interp_ops, & - BAerr, & - Refmodel, & - T, & - z_pseudo, & - N_pseudo, & - nb_pseudo) +CALL ufo_calculate_refractivity (nlevp, & + nlevq, & + za, & + zb, & + pressure, & + humidity, & + GPSRO_pseudo_ops, & + GPSRO_vert_interp_ops, & + GPSRO_min_temp_grad, & + BAerr, & + nRefLevels, & + refractivity, & + model_heights) + +ALLOCATE(nr(1:nRefLevels)) ! no point proceeding further if ... IF (.NOT. BAerr) THEN - ! Pseudo levels - IF (GPSRO_pseudo_ops) THEN - ! 2. Calculate the refractive index * radius on theta model levels (or model impact parameter) - CALL Ops_GPSROcalc_nr (z_pseudo, & ! geopotential heights of pseudo levels - nb_pseudo, & ! number of model+pseudo-levels - RO_Rad_Curv, & ! radius of curvature of earth at observation - Latitude, & ! latitude at observation - RO_geoid_und, & ! geoid undulation above WGS-84 - n_pseudo, & ! refractivity of model on model+pseudo levels - nr) ! Calculated model impact parameters - - ! 3. Calculate model bending angle on observation impact parameters - CALL Ops_GPSROcalc_alpha (nobs, & ! size of ob. vector - nb_pseudo, & ! no. of refractivity levels - zobs, & ! obs impact parameters - n_pseudo, & ! refractivity values on model+pseudo levels - nr, & ! index * radius product - ycalc) ! forward modelled bending angle - ! Model levels only - ELSE - ! 2. Calculate the refractive index * radius on theta model levels (or model impact parameter) - CALL Ops_GPSROcalc_nr (zb, & ! geopotential heights of model levels - nb, & ! number of levels in zb - RO_Rad_Curv, & ! radius of curvature of earth at observation - Latitude, & ! latitude at observation - RO_geoid_und, & ! geoid undulation above WGS-84 - Refmodel, & ! refractivity of model on model levels - nr) ! Calculated model impact parameters - - ! 3. Calculate model bending angle on observation impact parameters - CALL Ops_GPSROcalc_alpha (nobs, & ! size of ob. vector - nb, & ! no. of refractivity levels - zobs, & ! obs impact parameters - Refmodel, & ! refractivity values on model levels - nr, & ! index * radius product - ycalc) ! forward modelled bending angle - END IF + ! 2. Calculate the refractive index * radius on theta model levels (or model impact parameter) + CALL Ops_GPSROcalc_nr (nRefLevels, & ! number of levels for refractivity calculation + model_heights, & ! geopotential heights of refractivity levels + refractivity, & ! refractivity of model + RO_Rad_Curv, & ! radius of curvature of earth at observation + Latitude, & ! latitude at observation + RO_geoid_und, & ! geoid undulation above WGS-84 + nr) ! Calculated model impact parameters + + ! 3. Calculate model bending angle on observation impact parameters + CALL Ops_GPSROcalc_alpha (nobs, & ! size of ob. vector + nRefLevels, & ! no. of refractivity levels + zobs, & ! obs impact parameters + refractivity, & ! refractivity values + nr, & ! index * radius product + ycalc) ! forward modelled bending angle END IF END SUBROUTINE Ops_GPSRO_ForwardModel diff --git a/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_tlad_mod.F90 b/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_tlad_mod.F90 index f70b83e33..1f3681918 100644 --- a/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_tlad_mod.F90 +++ b/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_tlad_mod.F90 @@ -19,8 +19,12 @@ module ufo_gnssro_bendmetoffice_tlad_mod use gnssro_mod_conf use missing_values_mod use fckit_log_module, only : fckit_log -use ufo_gnssro_bendmetoffice_tlad_utils_mod, only: Ops_GPSROcalc_alphaK, Ops_GPSROcalc_nrK, Ops_GPSRO_refracK -use ufo_gnssro_ukmo1d_utils_mod, only: Ops_GPSROcalc_nr, Ops_GPSRO_refrac +use ufo_gnssro_bendmetoffice_tlad_utils_mod, only: & + Ops_GPSROcalc_alphaK, Ops_GPSROcalc_nrK +use ufo_gnssro_ukmo1d_utils_mod, only: Ops_GPSROcalc_nr +use ufo_utils_refractivity_calculator, only: & + ufo_calculate_refractivity, ufo_refractivity_kmat + integer, parameter :: max_string=800 @@ -29,6 +33,7 @@ module ufo_gnssro_bendmetoffice_tlad_mod private logical :: vert_interp_ops logical :: pseudo_ops + real(kind_real) :: min_temp_grad integer :: nlevp, nlevq, nlocs, iflip real(kind_real), allocatable :: K(:,:) contains @@ -54,6 +59,7 @@ subroutine ufo_gnssro_bendmetoffice_setup(self, f_conf) call f_conf%get_or_die("vert_interp_ops", self % vert_interp_ops) call f_conf%get_or_die("pseudo_ops", self % pseudo_ops) +call f_conf%get_or_die("min_temp_grad", self % min_temp_grad) end subroutine ufo_gnssro_bendmetoffice_setup @@ -79,7 +85,6 @@ subroutine ufo_gnssro_bendmetoffice_tlad_settraj(self, geovals, obss) type(ufo_geoval), pointer :: prs ! The model geovals - atmospheric pressure type(ufo_geoval), pointer :: rho_heights ! The model geovals - heights of the pressure-levels type(ufo_geoval), pointer :: theta_heights ! The model geovals - heights of the theta-levels (stores q) - integer :: nstate ! The size of the state vector integer :: iobs ! Loop variable, observation number real(kind_real), allocatable :: obsLat(:) ! Latitude of the observation @@ -123,41 +128,42 @@ subroutine ufo_gnssro_bendmetoffice_tlad_settraj(self, geovals, obss) call obsspace_get_db(obss, "MetaData", "impact_parameter", impact_param) call obsspace_get_db(obss, "MetaData", "earth_radius_of_curvature", obsLocR) call obsspace_get_db(obss, "MetaData", "geoid_height_above_reference_ellipsoid", obsGeoid) - nstate = prs % nval + q % nval - ALLOCATE(self % K(1:self%nlocs, 1:nstate)) + ALLOCATE(self % K(1:self%nlocs, 1:prs%nval + q%nval)) ! For each observation, calculate the K-matrix obs_loop: do iobs = 1, self % nlocs if (self%iflip == 1) then - CALL jacobian_interface(prs % nval, & ! Number of pressure levels - q % nval, & ! Number of specific humidity levels + CALL jacobian_interface(prs % nval, & ! Number of pressure levels + q % nval, & ! Number of specific humidity levels rho_heights % vals(rho_heights%nval:1:-1, iobs), & ! Heights of the pressure levels theta_heights % vals(theta_heights%nval:1:-1, iobs), & ! Heights of the specific humidity levels q % vals(q%nval:1:-1, iobs), & ! Values of the specific humidity prs % vals(prs%nval:1:-1, iobs), & ! Values of the pressure - self % pseudo_ops, & ! Whether to use pseudo-levels in the calculation - self % vert_interp_ops, & ! Whether to interpolate using log(pressure) - obsLocR(iobs), & ! Local radius of curvature of the earth - obsLat(iobs), & ! Latitude of the observation - obsGeoid(iobs), & ! Geoid undulation at the tangent point - 1, & ! Number of observations in the profile - impact_param(iobs:iobs), & ! Impact parameter for this observation - self % K(iobs:iobs,1:nstate)) ! K-matrix (Jacobian of the observation with respect to the inputs) + self % pseudo_ops, & ! Whether to use pseudo-levels in the calculation + self % vert_interp_ops, & ! Whether to interpolate using log(pressure) + self % min_temp_grad, & ! Minimum allowed vertical temperature gradient + obsLocR(iobs), & ! Local radius of curvature of the earth + obsLat(iobs), & ! Latitude of the observation + obsGeoid(iobs), & ! Geoid undulation at the tangent point + 1, & ! Number of observations in the profile + impact_param(iobs:iobs), & ! Impact parameter for this observation + self % K(iobs:iobs,1:prs%nval+q%nval)) ! K-matrix (Jacobian of the observation with respect to the inputs) else - CALL jacobian_interface(prs % nval, & ! Number of pressure levels - q % nval, & ! Number of specific humidity levels - rho_heights % vals(:,iobs), & ! Heights of the pressure levels - theta_heights % vals(:,iobs), & ! Heights of the specific humidity levels - q % vals(:,iobs), & ! Values of the specific humidity - prs % vals(:,iobs), & ! Values of the pressure - self % pseudo_ops, & ! Whether to use pseudo-levels in the calculation - self % vert_interp_ops, & ! Whether to interpolate using log(pressure) - obsLocR(iobs), & ! Local radius of curvature of the earth - obsLat(iobs), & ! Latitude of the observation - obsGeoid(iobs), & ! Geoid undulation at the tangent point - 1, & ! Number of observations in the profile - impact_param(iobs:iobs), & ! Impact parameter for this observation - self % K(iobs:iobs,1:nstate)) ! K-matrix (Jacobian of the observation with respect to the inputs) + CALL jacobian_interface(prs % nval, & ! Number of pressure levels + q % nval, & ! Number of specific humidity levels + rho_heights % vals(:,iobs), & ! Heights of the pressure levels + theta_heights % vals(:,iobs), & ! Heights of the specific humidity levels + q % vals(:,iobs), & ! Values of the specific humidity + prs % vals(:,iobs), & ! Values of the pressure + self % pseudo_ops, & ! Whether to use pseudo-levels in the calculation + self % vert_interp_ops, & ! Whether to interpolate using log(pressure) + self % min_temp_grad, & ! Minimum allowed vertical temperature gradient + obsLocR(iobs), & ! Local radius of curvature of the earth + obsLat(iobs), & ! Latitude of the observation + obsGeoid(iobs), & ! Geoid undulation at the tangent point + 1, & ! Number of observations in the profile + impact_param(iobs:iobs), & ! Impact parameter for this observation + self % K(iobs:iobs,1:prs%nval+q%nval)) ! K-matrix (Jacobian of the observation with respect to the inputs) end if end do obs_loop @@ -341,7 +347,7 @@ end subroutine ufo_gnssro_bendmetoffice_tlad_delete !------------------------------------------------------------------------- ! Interface for calculating the K-matrix for calculating TL/AD !------------------------------------------------------------------------- -SUBROUTINE jacobian_interface(nlevP, & +SUBROUTINE jacobian_interface(nlevp, & nlevq, & za, & zb, & @@ -349,6 +355,7 @@ SUBROUTINE jacobian_interface(nlevP, & prs, & pseudo_ops, & vert_interp_ops, & + min_temp_grad, & ro_rad_curv, & latitude, & ro_geoid_und, & @@ -358,7 +365,7 @@ SUBROUTINE jacobian_interface(nlevP, & IMPLICIT NONE -INTEGER, INTENT(IN) :: nlevP ! The number of model pressure levels +INTEGER, INTENT(IN) :: nlevp ! The number of model pressure levels INTEGER, INTENT(IN) :: nlevq ! The number of model theta levels REAL(kind_real), INTENT(IN) :: za(:) ! The geometric height of the model pressure levels REAL(kind_real), INTENT(IN) :: zb(:) ! The geometric height of the model theta levels @@ -366,102 +373,77 @@ SUBROUTINE jacobian_interface(nlevP, & REAL(kind_real), INTENT(IN) :: prs(1:nlevp) ! The model values that are being perturbed LOGICAL, INTENT(IN) :: pseudo_ops ! Whether to use pseudo levels in the calculation LOGICAL, INTENT(IN) :: vert_interp_ops ! Whether to use exner for the vertical interpolation +REAL(kind_real), INTENT(IN) :: min_temp_grad ! The minimum allowed vertical temperature gradient REAL(kind_real), INTENT(IN) :: ro_rad_curv ! The earth's radius of curvature at the ob location REAL(kind_real), INTENT(IN) :: latitude ! The latitude of the ob location REAL(kind_real), INTENT(IN) :: ro_geoid_und ! The geoid undulation at the ob location INTEGER, INTENT(IN) :: nobs ! The number of observations in this column REAL(kind_real), INTENT(IN) :: zobs(:) ! The impact parameters of the column of observations -REAL(kind_real), INTENT(INOUT) :: K(:,:) ! The calculated K matrix +REAL(kind_real), INTENT(INOUT) :: K(:,:) ! The calculated K matrix ! ! Things that may need to be output, as they are used by the TL/AD calculation ! -REAL(kind_real), ALLOCATABLE :: z_pseudo(:) ! Heights of the pseudo levels | Allocated by -REAL(kind_real), ALLOCATABLE :: N_pseudo(:) ! Refractivity on the pseudo levels | Ops_GPSRO_refrac -INTEGER :: nb_pseudo ! Number of pseudo levels +REAL(kind_real), ALLOCATABLE :: model_heights(:) ! Heights of the pseudo levels +REAL(kind_real), ALLOCATABLE :: refractivity(:) ! Refractivity on the pseudo levels +INTEGER :: nRefLevels ! Number of pseudo levels REAL(kind_real) :: T(1:nlevq) ! Temperature on model levels REAL(kind_real), ALLOCATABLE :: nr(:) ! Model calculation of impact parameters -REAL(kind_real) :: ref_model(1:nlevq) ! model refractivity on theta levels ! ! Local variables ! -INTEGER :: nstate ! Number of levels in state vector INTEGER :: num_pseudo ! Number of levels, including pseudo levels -INTEGER :: nb ! Number of non-pseudo levs -REAL(kind_real) :: x(1:nlevP+nlevQ) ! state vector +REAL(kind_real) :: x(1:nlevp+nlevq) ! state vector LOGICAL :: BAErr ! Whether we encountered an error in calculating the refractivity CHARACTER(LEN=200) :: err_msg ! Output message ! Set up the size of the state -nstate = nlevP + nlevq -nb = nlevq -x(1:nlevP) = prs -x(nlevP+1:nstate) = q - -! If we are using pseudo-levels for the vertical interpolation, then calculate -! the number of vertical levels -IF (pseudo_ops) THEN - num_pseudo = 2 * nlevq - 1 -ELSE - num_pseudo = nlevq -END IF -ALLOCATE(nr(1:num_pseudo)) +x(1:nlevp) = prs +x(nlevp+1:nlevp+nlevq) = q BAErr = .FALSE. -! Calculate the refractivity -CALL Ops_GPSRO_refrac (nstate, & - nlevP, & - nb, & - nlevq, & - za, & - zb, & - x, & - pseudo_ops, & - vert_interp_ops, & - BAerr, & - ref_model, & - T, & - z_pseudo, & - N_pseudo, & - nb_pseudo) +CALL ufo_calculate_refractivity (nlevp, & + nlevq, & + za, & + zb, & + prs, & + q, & + pseudo_ops, & + vert_interp_ops, & + min_temp_grad, & + BAerr, & + nRefLevels, & + refractivity, & + model_heights) + +ALLOCATE(nr(1:nRefLevels)) IF (.NOT. BAErr) THEN - IF (pseudo_ops) THEN - ! 2. Calculate the refractive index * radius on theta model levels (or model impact parameter) - CALL Ops_GPSROcalc_nr (z_pseudo, & ! geopotential heights of pseudo levels - nb_pseudo, & ! number of model+pseudo-levels - RO_Rad_Curv, & ! radius of curvature of earth at observation - Latitude, & ! latitude at observation - RO_geoid_und, & ! geoid undulation above WGS-84 - n_pseudo, & ! refractivity of model on model+pseudo levels - nr) ! Calculated model impact parameters - - ELSE - ! 2. Calculate the refractive index * radius on theta model levels (or model impact parameter) - CALL Ops_GPSROcalc_nr (zb, & ! geopotential heights of model levels - nb, & ! number of levels in zb - RO_Rad_Curv, & ! radius of curvature of earth at observation - Latitude, & ! latitude at observation - RO_geoid_und, & ! geoid undulation above WGS-84 - ref_model, & ! refractivity of model on model levels - nr) ! Calculated model impact parameters - END IF + ! 2. Calculate the impact parameter (refractive index * radius) on refractivity levels + CALL Ops_GPSROcalc_nr (nRefLevels, & ! number of model+pseudo-levels + model_heights, & ! geopotential heights of pseudo levels + refractivity, & ! refractivity of model on model+pseudo levels + RO_Rad_Curv, & ! radius of curvature of earth at observation + Latitude, & ! latitude at observation + RO_geoid_und, & ! geoid undulation above WGS-84 + nr) ! Calculated model impact parameters ! Calculate the K-matrix (Jacobian) - CALL Ops_GPSRO_GetK(nstate, & - nlevp, & - nb, & + CALL Ops_GPSRO_GetK(nlevp, & + nRefLevels, & nlevq, & za, & zb, & - z_pseudo, & + model_heights, & pseudo_ops, & vert_interp_ops, & - x, & + min_temp_grad, & + prs, & + q, & ro_rad_curv, & latitude, & ro_geoid_und, & - ref_model, & + refractivity, & nobs, & zobs, & nr, & @@ -473,8 +455,8 @@ SUBROUTINE jacobian_interface(nlevP, & END IF DEALLOCATE(nr) -IF (ALLOCATED(z_pseudo)) DEALLOCATE(z_pseudo) -IF (ALLOCATED(N_pseudo)) DEALLOCATE(N_pseudo) +DEALLOCATE(refractivity) +DEALLOCATE(model_heights) END SUBROUTINE jacobian_interface @@ -482,16 +464,17 @@ END SUBROUTINE jacobian_interface !------------------------------------------------------------------------- ! Calculate the K-matrix (Jacobian) !------------------------------------------------------------------------- -SUBROUTINE Ops_GPSRO_GetK(nstate, & - nlevP, & - nb, & +SUBROUTINE Ops_GPSRO_GetK(nlevp, & + nRefLevels, & nlevq, & za, & zb, & - zb_pseudo, & + model_heights, & pseudo_ops, & vert_interp_ops, & - x, & + min_temp_grad, & + pressure, & + humidity, & ro_rad_curv, & latitude, & ro_geoid_und, & @@ -505,68 +488,59 @@ SUBROUTINE Ops_GPSRO_GetK(nstate, & ! IMPLICIT NONE - INTEGER, INTENT(IN) :: nstate - INTEGER, INTENT(IN) :: nlevP ! The number of model pressure levels - INTEGER, INTENT(IN) :: nb - INTEGER, INTENT(IN) :: nlevq ! The number of model theta levels - REAL(kind_real), INTENT(IN) :: za(:) ! The geometric height of the model pressure levels - REAL(kind_real), INTENT(IN) :: zb(:) ! The geometric height of the model theta levels - REAL(kind_real), INTENT(IN) :: zb_pseudo(:) ! The geometric height of the pseudo/model theta levels - LOGICAL, INTENT(IN) :: pseudo_ops ! Whether to use pseudo levels in the calculation - LOGICAL, INTENT(IN) :: vert_interp_ops ! Whether to use exner for the vertical interpolation - REAL(kind_real), INTENT(IN) :: x(:) ! The model values that are being perturbed - REAL(kind_real), INTENT(IN) :: ro_rad_curv ! The earth's radius of curvature at the ob location - REAL(kind_real), INTENT(IN) :: latitude ! The latitude of the ob location - REAL(kind_real), INTENT(IN) :: ro_geoid_und ! The geoid undulation at the ob location - REAL(kind_real), INTENT(IN) :: ref_model(nb) ! Model refractivity on theta levels - returned from forward model - INTEGER, INTENT(IN) :: nobs ! The number of observations in this column - REAL(kind_real), INTENT(IN) :: zobs(:) ! The impact parameters of the column of observations - REAL(kind_real), INTENT(IN) :: nr(nb) ! The impact parameters of the model data - REAL(kind_real), INTENT(OUT) :: K(nobs,nstate) ! The calculated K matrix - - REAL(kind_real) :: m1(nobs, nb) ! Intermediate term in the K-matrix calculation - REAL(kind_real), ALLOCATABLE :: dref_dp(:, :) ! Partial derivative of refractivity wrt. pressure - REAL(kind_real), ALLOCATABLE :: dref_dq(:, :) ! Partial derivative of refractivity wrt. specific humidity - REAL(kind_real) :: dnr_dref(nb, nb) ! Partial derivative of impact parameter wrt. refractivity - REAL(kind_real) :: dalpha_dref(nobs, nb) ! Partial derivative of bending angle wrt. refractivity - REAL(kind_real) :: dalpha_dnr(nobs, nb) ! Partial derivative of bending angle wrt. impact parameter + INTEGER, INTENT(IN) :: nlevp ! The number of model pressure levels + INTEGER, INTENT(IN) :: nRefLevels ! Number of refractivity levels + INTEGER, INTENT(IN) :: nlevq ! The number of model theta levels + REAL(kind_real), INTENT(IN) :: za(:) ! The geometric height of the model pressure levels + REAL(kind_real), INTENT(IN) :: zb(:) ! The geometric height of the model theta levels + REAL(kind_real), INTENT(IN) :: model_heights(:) ! The geometric height of the refractivity levels + LOGICAL, INTENT(IN) :: pseudo_ops ! Whether to use pseudo levels in the calculation + LOGICAL, INTENT(IN) :: vert_interp_ops ! Whether to use exner for the vertical interpolation + REAL(kind_real), INTENT(IN) :: min_temp_grad ! Minimum allowed vertical temperature gradient + REAL(kind_real), INTENT(IN) :: pressure(nlevp) ! Model pressure + REAL(kind_real), INTENT(IN) :: humidity(nlevq) ! Model specific humidity + REAL(kind_real), INTENT(IN) :: ro_rad_curv ! The earth's radius of curvature at the ob location + REAL(kind_real), INTENT(IN) :: latitude ! The latitude of the ob location + REAL(kind_real), INTENT(IN) :: ro_geoid_und ! The geoid undulation at the ob location + REAL(kind_real), INTENT(IN) :: ref_model(nRefLevels) ! Model refractivity on theta levels - returned from forward model + INTEGER, INTENT(IN) :: nobs ! The number of observations in this column + REAL(kind_real), INTENT(IN) :: zobs(:) ! The impact parameters of the column of observations + REAL(kind_real), INTENT(IN) :: nr(nRefLevels) ! The impact parameters of the model data + REAL(kind_real), INTENT(OUT) :: K(nobs,nlevp+nlevq) ! The calculated K matrix + + REAL(kind_real) :: m1(nobs, nRefLevels) ! Intermediate term in the K-matrix calculation + REAL(kind_real), ALLOCATABLE :: dref_dp(:, :) ! Partial derivative of refractivity wrt. pressure + REAL(kind_real), ALLOCATABLE :: dref_dq(:, :) ! Partial derivative of refractivity wrt. specific humidity + REAL(kind_real) :: dnr_dref(nRefLevels, nRefLevels) ! Partial derivative of impact parameter wrt. refractivity + REAL(kind_real) :: dalpha_dref(nobs, nRefLevels) ! Partial derivative of bending angle wrt. refractivity + REAL(kind_real) :: dalpha_dnr(nobs, nRefLevels) ! Partial derivative of bending angle wrt. impact parameter ! 1. Calculate the gradient of ref wrt p (on rho levels) and q (on theta levels) - CALL Ops_GPSRO_refracK (nstate, & - nlevP, & - nb, & - nlevq, & - za, & - zb, & - x, & - pseudo_ops, & - vert_interp_ops, & - dref_dp, & !out - dref_dq) !out - - IF (pseudo_ops) THEN - ! 2. Calculate the gradient of nr wrt ref - CALL Ops_GPSROcalc_nrK (zb_pseudo, & ! geopotential heights of pseudo levels - nb, & ! number of levels in zb - RO_Rad_Curv, & ! radius of curvature of earth at observation - Latitude, & ! latitude at observation - RO_geoid_und, & ! geoid undulation above WGS-84 - ref_model, & ! refractivity of model on model levels - dnr_dref) ! out - ELSE - ! 2. Calculate the gradient of nr wrt ref - CALL Ops_GPSROcalc_nrK (zb, & ! geopotential heights of model levels - nb, & ! number of levels in zb - RO_Rad_Curv, & ! radius of curvature of earth at observation - Latitude, & ! latitude at observation - RO_geoid_und, & ! geoid undulation above WGS-84 - ref_model, & ! refractivity of model on model levels - dnr_dref) ! out - END IF + CALL ufo_refractivity_kmat(nlevp, & + nlevq, & + nRefLevels, & + za, & + zb, & + pressure, & + humidity, & + pseudo_ops, & + vert_interp_ops, & + min_temp_grad, & + dref_dp, & !out + dref_dq) !out + + ! 2. Calculate the gradient of nr wrt ref + CALL Ops_GPSROcalc_nrK (model_heights, & ! geopotential heights of pseudo levels + nRefLevels, & ! number of refractivity levels + RO_Rad_Curv, & ! radius of curvature of earth at observation + Latitude, & ! latitude at observation + RO_geoid_und, & ! geoid undulation above WGS-84 + ref_model, & ! refractivity of model on model levels + dnr_dref) ! out ! 3. Calculate the gradient of bending angle wrt ref and nr CALL Ops_GPSROcalc_alphaK (nobs, & ! size of ob. vector - nb, & ! no. of refractivity levels + nRefLevels, & ! no. of refractivity levels zobs, & ! obs impact parameters ref_model, & ! refractivity values on model levels nr, & ! index * radius product @@ -575,8 +549,8 @@ SUBROUTINE Ops_GPSRO_GetK(nstate, & ! Calculate overall gradient of bending angle wrt p and q m1 = MATMUL (dalpha_dnr,dnr_dref) - K(1:nobs, 1:nlevP) = MATMUL (dalpha_dref,dref_dp) + MATMUL (m1,dref_dp) !P part - K(1:nobs, nlevP + 1:nstate) = MATMUL (dalpha_dref,dref_dq) + MATMUL (m1,dref_dq) !q part + K(1:nobs, 1:nlevp) = MATMUL (dalpha_dref,dref_dp) + MATMUL (m1,dref_dp) !P part + K(1:nobs, nlevp+1:nlevp+nlevq) = MATMUL (dalpha_dref,dref_dq) + MATMUL (m1,dref_dq) !q part IF (ALLOCATED(dref_dp)) DEALLOCATE(dref_dp) IF (ALLOCATED(dref_dq)) DEALLOCATE(dref_dq) diff --git a/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_tlad_utils_mod.F90 b/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_tlad_utils_mod.F90 index 982e9c8cd..2c78e4e24 100644 --- a/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_tlad_utils_mod.F90 +++ b/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_tlad_utils_mod.F90 @@ -33,448 +33,10 @@ module ufo_gnssro_bendmetoffice_tlad_utils_mod implicit none public :: Ops_GPSROcalc_alphaK public :: Ops_GPSROcalc_nrK -public :: Ops_GPSRO_refracK private contains -!------------------------------------------------------------------------------- -! (C) Crown copyright Met Office. All rights reserved. -! Refer to COPYRIGHT.txt of this distribution for details. -!------------------------------------------------------------------------------- -! Calculate GPSRO refractivity K matrix. -!------------------------------------------------------------------------------- - -SUBROUTINE Ops_GPSRO_refracK (nstate, & - nlevP, & - nb, & - nlevq, & - za, & - zb, & - x, & - GPSRO_pseudo_ops, & - GPSRO_vert_interp_ops, & - dref_dP, & - dref_dq) - -IMPLICIT NONE - -! Subroutine arguments: -INTEGER, INTENT(IN) :: nstate ! Number of variables in full model state -INTEGER, INTENT(IN) :: nlevP ! Number of p levels in state vector -INTEGER, INTENT(IN) :: nb ! Number of theta levels -INTEGER, INTENT(IN) :: nlevq ! Number of specific humidity levels (=nb) -REAL(kind_real), INTENT(IN) :: za(:) ! Heights of the pressure levels -REAL(kind_real), INTENT(IN) :: zb(:) ! Heights of the theta (specific humidity) levels -REAL(kind_real), INTENT(IN) :: x(:) ! Input model state -LOGICAL, INTENT(IN) :: GPSRO_pseudo_ops ! Whether to use pseudo-levels in the calculation -LOGICAL, INTENT(IN) :: GPSRO_vert_interp_ops ! Whether to use log(p) for the vertical interpolation -REAL(kind_real), ALLOCATABLE, INTENT(OUT) :: dref_dP(:,:) ! K-matrix for p -REAL(kind_real), ALLOCATABLE, INTENT(OUT) :: dref_dq(:,:) ! K-matrix for q - -! Local declarations: -CHARACTER(len=*), PARAMETER :: RoutineName = "Ops_GPSRO_refracK" -INTEGER :: i -INTEGER :: counter -REAL(kind_real) :: P(nlevP) -REAL(kind_real) :: Exner(nlevP) -REAL(kind_real) :: q(nlevq) -REAL(kind_real) :: Pb(nlevq) -REAL(kind_real) :: Tv(nlevq) -REAL(kind_real) :: T(nlevq) -REAL(kind_real) :: Extheta -REAL(kind_real) :: pwt1 -REAL(kind_real) :: pwt2 -REAL(kind_real) :: Ndry -REAL(kind_real) :: Nwet -REAL(kind_real) :: refrac(nlevq) -REAL(kind_real) :: dEx_dP(nlevP,nlevP) -REAL(kind_real) :: dPb_dP(nlevq,nlevP) -REAL(kind_real) :: dExtheta_dPb(nlevq,nlevq) -REAL(kind_real) :: dTv_dExtheta(nlevq,nlevq) -REAL(kind_real) :: dTv_dEx(nlevq,nlevP) -REAL(kind_real) :: dT_dTv(nlevq,nlevq) -REAL(kind_real) :: dT_dq(nlevq,nlevq) -REAL(kind_real) :: dref_dPb(nlevq,nlevq) -REAL(kind_real) :: dref_dT(nlevq,nlevq) -REAL(kind_real) :: m1(nb,nlevq) -REAL(kind_real) :: m2(nb,nlevP) -REAL(kind_real) :: m3(nb,nlevq) -REAL(kind_real) :: m4(nb,nlevq) -REAL(kind_real), ALLOCATABLE :: P_pseudo(:) -REAL(kind_real), ALLOCATABLE :: q_pseudo(:) -REAL(kind_real), ALLOCATABLE :: T_pseudo(:) -REAL(kind_real), ALLOCATABLE :: z_pseudo(:) -REAL(kind_real), ALLOCATABLE :: N_pseudo(:) -REAL(kind_real) :: gamma -REAL(kind_real) :: beta -REAL(kind_real) :: c !continuity constant for hydrostatic pressure -REAL(kind_real) :: g_RB ! Frequently used term -REAL(kind_real) :: c_ZZ ! Frequently used term -REAL(kind_real) :: dPp_dT1 ! dP_pseudo / dT_below -REAL(kind_real) :: dPp_dTp ! dP_pseudo / dT_pseudo -REAL(kind_real) :: dTp_dT1 ! dT_pseudo / dT_below -REAL(kind_real) :: dPp_dbeta ! dP_pseudo / dbeta -REAL(kind_real) :: dbeta_dT1 ! dbeta / dT_below -REAL(kind_real) :: dTp_dbeta ! dT_pseudo / dbeta -REAL(kind_real) :: dbeta_dT2 ! dbeta / dT_above -REAL(kind_real) :: dPp_dc ! dP_pseudo / dc -REAL(kind_real) :: dc_dT1 ! dc / dT_below -REAL(kind_real) :: dc_dbeta ! dc / dbeta -REAL(kind_real) :: dc_dT2 ! dc / dT_above -REAL(kind_real) :: dPp_dP1 ! dP_pseudo / dP_below -REAL(kind_real) :: dc_dP1 ! dc / dP_below -REAL(kind_real) :: dc_dP2 ! dc / dP_above -REAL(kind_real), ALLOCATABLE :: dref_dPpseudo(:,:) -REAL(kind_real), ALLOCATABLE :: dref_dTpseudo(:,:) -REAL(kind_real), ALLOCATABLE :: dref_dqpseudo(:,:) -REAL(kind_real), ALLOCATABLE :: dPpseudo_dPb(:,:) -REAL(kind_real), ALLOCATABLE :: dTpseudo_dTb(:,:) -REAL(kind_real), ALLOCATABLE :: dqpseudo_dqb(:,:) -REAL(kind_real), ALLOCATABLE :: dPpseudo_dT(:,:) -REAL(kind_real), ALLOCATABLE :: dTb_dP(:,:) -REAL(kind_real), ALLOCATABLE :: dPpseudo_dP(:,:) -REAL(kind_real), ALLOCATABLE :: dTpseudo_dP(:,:) - -IF (GPSRO_pseudo_ops) THEN - ALLOCATE (dref_dP(nb,nlevP)) - ALLOCATE (dref_dq(nb,nlevq)) - - ALLOCATE (P_pseudo(nb)) - ALLOCATE (q_pseudo(nb)) - ALLOCATE (T_pseudo(nb)) - ALLOCATE (z_pseudo(nb)) - ALLOCATE (N_pseudo(nb)) - - ALLOCATE (dref_dPpseudo(nb,nb)) - ALLOCATE (dref_dTpseudo(nb,nb)) - ALLOCATE (dref_dqpseudo(nb,nb)) - ALLOCATE (dPpseudo_dPb(nb,nlevq)) - ALLOCATE (dPpseudo_dT(nb,nlevq)) - ALLOCATE (dTpseudo_dTb(nb,nlevq)) - ALLOCATE (dqpseudo_dqb(nb,nlevq)) - - ALLOCATE (dTb_dP(nlevq,nlevP)) - ALLOCATE (dPpseudo_dP(nb,nlevP)) - ALLOCATE (dTpseudo_dP(nb,nlevP)) - - dref_dPpseudo(:,:) = 0.0 - dref_dTpseudo(:,:) = 0.0 - dref_dqpseudo(:,:) = 0.0 - dPpseudo_dPb(:,:) = 0.0 - dPpseudo_dT(:,:) = 0.0 - dTpseudo_dTb(:,:) = 0.0 - dqpseudo_dqb(:,:) = 0.0 - dPpseudo_dP(:,:) = 0.0 - dTpseudo_dP(:,:) = 0.0 -ELSE - ALLOCATE (dref_dP(nlevq,nlevP)) - ALLOCATE (dref_dq(nlevq,nlevq)) -END IF - -!----------------------- -! 1. Initialise matrices -!----------------------- - -dPb_dP(:,:) = 0.0 -dExtheta_dPb(:,:) = 0.0 -dEx_dP(:,:) = 0.0 -dTv_dExtheta(:,:) = 0.0 -dTv_dEx(:,:) = 0.0 -dT_dTv(:,:) = 0.0 -dT_dq(:,:) = 0.0 -dref_dpb(:,:) = 0.0 -dref_dT(:,:) = 0.0 -dref_dq(:,:) = 0.0 -dref_dp(:,:) = 0.0 - -! Set up the P and q vectors from x - -P(:) = 1.0E2 * x(1:nlevP) -q(:) = 1.0E-3 * x(nlevP + 1:nstate) - -! Calculate exner on rho levels. - -Exner(:) = (P(:) / Pref) ** rd_over_cp - -DO i = 1,nlevp - - dEx_dP(i,i) = rd_over_cp / Pref * (P(i) / Pref) ** (rd_over_cp - 1.0) - -END DO - -!---------------------------------------------- -! 2. Calculate the refractivity on the b levels -!---------------------------------------------- - -DO i = 1, nlevq - - ! Calc. pressure on b levels - - pwt1 = (za(i + 1) - zb(i)) / (za(i + 1) - za(i)) - - pwt2 = 1.0 - pwt1 - - ! calculate the pressure on the theta level. - IF (GPSRO_vert_interp_ops) THEN - Pb(i) = EXP (pwt1 * LOG (P(i)) + pwt2 * LOG (P(i + 1))) - - dPb_dP(i,i) = Pb(i) * pwt1 / P(i) - dPb_dP(i,i + 1) = Pb(i) * pwt2 / P(i + 1) - ELSE - ! Assume Exner varies linearly with height - Pb(i) = Pref * (pwt1 * (P(i) / Pref) ** rd_over_cp + pwt2 * (P(i + 1) / Pref) ** rd_over_cp) ** (1.0 / rd_over_cp) - - dPb_dP(i,i) = pwt1 * (pwt1 * (P(i) / Pref) ** rd_over_cp + pwt2 * & - (P(i + 1) / Pref) ** rd_over_cp) ** (1.0 / rd_over_cp - 1.0) * (P(i) / Pref) ** (rd_over_cp - 1.0) - dPb_dP(i,i + 1) = pwt2 * (pwt1 * (P(i) / Pref) ** rd_over_cp + pwt2 * & - (P(i + 1) / Pref) ** rd_over_cp) ** (1.0 / rd_over_cp - 1.0) * (P(i + 1) / Pref) ** (rd_over_cp-1.0) - END IF - - ! calculate Exner on the theta level. - - Extheta = (Pb(i) / Pref) ** rd_over_cp - - dExtheta_dPb(i,i) = rd_over_cp * (Pb(i) ** (rd_over_cp - 1.0)) / (Pref ** rd_over_cp) - - ! Calculate mean layer Tv using ND definition - - Tv(i) = grav * (za(i + 1) - za(i)) * Extheta / (Cp * (Exner(i) - Exner(i + 1))) - - dTv_dExtheta(i,i) = Tv(i) / Extheta - - dTv_dEx(i,i) = -Tv(i) / (Exner(i) - Exner(i + 1)) - - dTv_dEx(i,i + 1) = Tv(i) / (Exner(i) - Exner(i + 1)) - - IF (i > nlevq) THEN - - T(i) = Tv(i) - - dT_dTv(i,i) = 1.0 - - ! no wet component - - Nwet = 0.0 - - ELSE - - T(i) = Tv(i) / (1.0 + C_virtual * q(i)) - - dT_dTv(i,i) = 1.0 / (1.0 + C_virtual * q(i)) - - dT_dq(i,i) = -C_virtual * T(i) / (1.0 + C_virtual * q(i)) - - ! wet compontent - - Nwet = n_beta * Pb(i) * q(i) / (T(i) ** 2 * (mw_ratio + (1.0 - mw_ratio) * q(i))) - - dref_dq(i,i) = n_beta * Pb(i) * mw_ratio / (T(i) * (mw_ratio + (1.0 - mw_ratio) * q(i))) ** 2 - - END IF - - Ndry = n_alpha * Pb(i) / T(i) - - refrac(i) = Ndry + Nwet - - dref_dPb(i,i) = refrac(i) / Pb(i) - - dref_dT(i,i) = -(Ndry + 2.0 * Nwet) / T(i) - -END DO - -IF (GPSRO_pseudo_ops) THEN - !----------------------------------! - !- Add intermediate pseudo-levels -! - !----------------------------------! - dTb_dP = MATMUL (dT_dTv, MATMUL (MATMUL (dTv_dExtheta, dExtheta_dPb), dPb_dP) + MATMUL (dTv_dEx, dEx_dP)) - counter = 1 - DO i = 1, nb - ! Odd 'i' (i.e. copies of actual model level values) - IF (MOD (i, 2) > 0) THEN - z_pseudo(i) = zb(counter) - P_pseudo(i) = Pb(counter) - q_pseudo(i) = q(counter) - T_pseudo(i) = T(counter) - N_pseudo(i) = n_alpha * P_pseudo(i) / T_pseudo(i) + n_beta * P_pseudo(i) * q_pseudo(i) / & - (T_pseudo(i) ** 2 * (mw_ratio + (1.0 - mw_ratio) * q_pseudo(i))) - - dref_dPpseudo(i,i) = dref_dPb(counter,counter) - dref_dTpseudo(i,i) = dref_dT(counter,counter) - dref_dqpseudo(i,i) = dref_dq(counter,counter) - - dPpseudo_dPb(i,counter) = 1.0 - dTpseudo_dTb(i,counter) = 1.0 - dqpseudo_dqb(i,counter) = 1.0 - - counter = counter + 1 - - ! Even 'i' (i.e. intermediate pseudo-levels) - ELSE - z_pseudo(i) = (zb(counter - 1) + zb(counter)) / 2.0 - IF (MIN (q(counter - 1), q(counter)) > 0.0) THEN - gamma = LOG (q(counter - 1) / q(counter)) / (zb(counter) - zb(counter - 1)) - q_pseudo(i) = q(counter - 1) * EXP (-gamma * (z_pseudo(i) - z_pseudo(i - 1))) - ELSE - q_pseudo(i) = q(counter - 1) + (q(counter) - q(counter - 1)) / & - (zb(counter) - zb(counter - 1)) * (z_pseudo(i) - zb(counter - 1)) - END IF - - beta = (T(counter) - T(counter - 1)) / (zb(counter) - zb(counter - 1)) - T_pseudo(i) = T(counter - 1) + beta * (z_pseudo(i) - zb(counter - 1)) - IF (ABS (T(counter) - T(counter - 1)) > 1.0e-10) THEN - c = ((Pb(counter) / Pb(counter - 1)) * (T(counter) / T(counter - 1)) ** (grav / (rd * beta)) - 1.0) / & - (zb(counter) - zb(counter - 1)) - P_pseudo(i) = (Pb(counter - 1) * (T_pseudo(i) / T(counter - 1)) ** & - (-grav / (rd * beta))) * (1.0 + c * (z_pseudo(i) - zb(counter - 1))) - ELSE - P_pseudo(i) = Pb(counter - 1) * EXP (LOG (Pb(counter) / Pb(counter - 1)) * & - ((z_pseudo(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1)))) - END IF - - Ndry = n_alpha * P_pseudo(i) / T_pseudo(i) - Nwet = n_beta * P_pseudo(i) * q_pseudo(i) / (T_pseudo(i) ** 2 & - *(mw_ratio + (1.0 - mw_ratio) * q_pseudo(i))) - - N_pseudo(i) = Ndry + Nwet - - dref_dPpseudo(i,i) = N_pseudo(i) / P_pseudo(i) - dref_dTpseudo(i,i) = -(Ndry + 2.0 * Nwet) / T_pseudo(i) - dref_dqpseudo(i,i) = n_beta * P_pseudo(i) * mw_ratio / (T_pseudo(i) * (mw_ratio + & - (1.0 - mw_ratio) * q_pseudo(i))) ** 2 - - ! Transform P, q and T to pseudo-levels - IF (MIN (q(counter - 1), q(counter)) > 0.0) THEN - dqpseudo_dqb(i,counter - 1) = (q_pseudo(i) / q(counter - 1)) * (1.0 - & - (z_pseudo(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1))) - - dqpseudo_dqb(i,counter) = (q_pseudo(i) / q(counter)) * ((z_pseudo(i) - zb(counter - 1)) / & - (zb(counter) - zb(counter - 1))) - ELSE - dqpseudo_dqb(i,counter - 1) = (zb(counter) - z_pseudo(i)) / (zb(counter) - zb(counter - 1)) - - dqpseudo_dqb(i,counter) = (z_pseudo(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1)) - END IF - - dTpseudo_dTb(i,counter - 1) = (zb(counter) - z_pseudo(i)) / (zb(counter) - zb(counter - 1)) - - dTpseudo_dTb(i,counter) = (z_pseudo(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1)) - - ! Items that need changing for dPpseudo/dTb: - - g_RB = grav / (rd * beta) - c_ZZ = c + 1.0 / (zb(counter) - zb(counter - 1)) - - dPp_dT1 = (g_RB / T(counter - 1)) * P_pseudo(i) - dPp_dTp = -(g_RB / T_pseudo(i)) * P_pseudo(i) - dTp_dT1 = dTpseudo_dTb(i,counter - 1) - dPp_dbeta = (g_RB / beta) * LOG (T_pseudo(i) / T(counter - 1)) * P_pseudo(i) - dbeta_dT1 = -1.0 / (zb(counter) - zb(counter - 1)) - dTp_dbeta = z_pseudo(i) - zb(counter - 1) - dbeta_dT2 = 1.0 / (zb(counter) - zb(counter - 1)) - - IF (ABS (T(counter) - T(counter - 1)) > 1.0e-10) THEN - ! Incomplete computation of dPpseudo_dPb. Temperature derivatives done below - dPp_dc = (z_pseudo(i) - zb(counter - 1)) * Pb(counter - 1) * (T_pseudo(i) / T(counter - 1)) ** (-g_RB) - dc_dT1 = -(g_RB / (T(counter - 1))) * c_ZZ - dc_dbeta = -(g_RB / beta) * LOG (T(counter) / T(counter - 1)) * c_ZZ - dc_dT2 = (g_RB / T(counter)) * c_ZZ - dPp_dP1 = P_pseudo(i) / Pb(counter - 1) - dPpseudo_dT(i,counter - 1) = dPp_dT1 + dPp_dTp * dTp_dT1 + dPp_dbeta * dbeta_dT1 + dPp_dc * & - (dc_dT1 + dc_dbeta * dbeta_dT1) - dPpseudo_dT(i,counter) = (dPp_dbeta + dPp_dTp * dTp_dbeta + dPp_dc * dc_dbeta) * & - dbeta_dT2 + dPp_dc * dc_dT2 - - dc_dP1 = -(1.0 / Pb(counter - 1)) * c_ZZ - dc_dP2 = (1.0 / Pb(counter)) * c_ZZ - - dPpseudo_dPb(i,counter - 1) = dPp_dP1 + dPp_dc * dc_dP1 - dPpseudo_dPb(i,counter) = dPp_dc * dc_dP2 - ELSE - dPpseudo_dPb(i,counter - 1) = EXP (LOG (Pb(counter) / Pb(counter - 1)) * ((z_pseudo(i) - zb(counter - 1)) / & - (zb(counter) - zb(counter - 1)))) * (1.0 - ((z_pseudo(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1)))) - dPpseudo_dPb(i,counter) = (Pb(counter - 1) / Pb(counter)) * ((z_pseudo(i) - zb(counter - 1)) / & - (zb(counter) - zb(counter - 1))) * EXP (LOG (Pb(counter) / Pb(counter - 1)) * ((z_pseudo(i) - zb(counter - 1)) / & - (zb(counter) - zb(counter - 1)))) - END IF - - END IF - - END DO - - ! temperature derivatives: - dPpseudo_dP = MATMUL (dPpseudo_dPb, dPb_dp) + MATMUL (dPpseudo_dT, dTb_dP) - dTpseudo_dP = MATMUL (dTpseudo_dTb, dTb_dP) - - !------------------------------------------------- - ! 3. Evaluate the Kmatrix by matrix multiplication - !------------------------------------------------- - - ! calc K matrix for P on rho levels - dref_dP(:,:) = MATMUL (dref_dPpseudo, dPpseudo_dP) + MATMUL (dref_dTpseudo, dTpseudo_dP) - - ! calc Kmatrix for q on theta levels - dref_dq(:,:) = MATMUL (dref_dqpseudo, dqpseudo_dqb) + MATMUL (MATMUL (dref_dTpseudo, dTpseudo_dTb), dT_dq) + & - MATMUL (MATMUL (dref_dPpseudo, dPpseudo_dT), dT_dq) - - ! Put the K matrices in correct units - - dref_dP(:,:) = 1.0E2 * dref_dP(:,:) ! hPa - - dref_dq(:,:) = 1.0E-3 * dref_dq(:,:) ! g/kg - -! Normal model levels -ELSE - - !------------------------------------------------- - ! 3. Evaluate the Kmatrix by matrix multiplication - !------------------------------------------------- - - ! calc K matrix for P on rho levels - ! dNmod/dP = (dNmod/dPb * dPb/dP) + .... - dref_dP(:,:) = MATMUL (dref_dPb, dPb_dP) - - ! .... (dNmod/dT * dT/dTv * dTv/dEx *dEx/dP) + ..... - m1(:,:) = MATMUL (dref_dT, dT_dTv) - m2(:,:) = MATMUL (m1, dTv_dEx) - dref_dP(:,:) = dref_dP(:,:) + MATMUL (m2, dEx_dP) - - ! .... (dNmod/dT * dT/dTv * dTv/dExtheta *dExtheta/dPb*dPb/dP) - m3(:,:) = MATMUL (m1, dTv_dExtheta) - m4(:,:) = MATMUL (m3, dExtheta_dPb) - dref_dP(:,:) = dref_dP(:,:) + MATMUL (m4, dPb_dp) - - ! calc Kmatrix for q on theta levels - ! dNmod/dq = (dNmod/dq) + (dNmod/dT*dT/dq) - dref_dq(:,:) = dref_dq(:,:) + MATMUL (dref_dT, dT_dq) - - ! Put the K matrices in correct units - - dref_dP(:,:) = 1.0E2 * dref_dP(:,:) ! hPa - - dref_dq(:,:) = 1.0E-3 * dref_dq(:,:) ! g/kg - -END IF - -IF (ALLOCATED (P_pseudo)) DEALLOCATE (P_pseudo) -IF (ALLOCATED (q_pseudo)) DEALLOCATE (q_pseudo) -IF (ALLOCATED (T_pseudo)) DEALLOCATE (T_pseudo) -IF (ALLOCATED (z_pseudo)) DEALLOCATE (z_pseudo) -IF (ALLOCATED (N_pseudo)) DEALLOCATE (N_pseudo) -IF (ALLOCATED (dref_dPpseudo)) DEALLOCATE (dref_dPpseudo) -IF (ALLOCATED (dref_dTpseudo)) DEALLOCATE (dref_dTpseudo) -IF (ALLOCATED (dref_dqpseudo)) DEALLOCATE (dref_dqpseudo) -IF (ALLOCATED (dPpseudo_dPb)) DEALLOCATE (dPpseudo_dPb) -IF (ALLOCATED (dTpseudo_dTb)) DEALLOCATE (dTpseudo_dTb) -IF (ALLOCATED (dqpseudo_dqb)) DEALLOCATE (dqpseudo_dqb) -IF (ALLOCATED (dPpseudo_dT)) DEALLOCATE (dPpseudo_dT) -IF (ALLOCATED (dTb_dP)) DEALLOCATE (dTb_dP) -IF (ALLOCATED (dPpseudo_dP)) DEALLOCATE (dPpseudo_dP) -IF (ALLOCATED (dTpseudo_dP)) DEALLOCATE (dTpseudo_dP) - -END SUBROUTINE Ops_GPSRO_refracK - - SUBROUTINE Ops_GPSROcalc_nrK (zb, & ! geopotential heights of model levels nb, & ! number of levels in zb Rad, & ! radius of curvature of earth at observation diff --git a/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_utils_mod.F90 b/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_utils_mod.F90 index dac2d2d43..b21428a3d 100644 --- a/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_utils_mod.F90 +++ b/src/ufo/gnssro/BendMetOffice/ufo_gnssro_bendmetoffice_utils_mod.F90 @@ -33,7 +33,6 @@ module ufo_gnssro_ukmo1d_utils_mod implicit none public :: Ops_GPSROcalc_alpha public :: Ops_GPSROcalc_nr -public :: Ops_GPSRO_refrac public :: Ops_GPSRO_geop_geom private @@ -216,12 +215,12 @@ END SUBROUTINE Ops_GPSROcalc_alpha ! (C) Crown copyright Met Office. All rights reserved. ! Refer to COPYRIGHT.txt of this distribution for details. !------------------------------------------------------------------------------- -SUBROUTINE Ops_GPSROcalc_nr (zb, & ! geopotential heights of model levels - nb, & ! number of levels in zb +SUBROUTINE Ops_GPSROcalc_nr (nb, & ! number of levels in zb + zb, & ! geopotential heights of model levels + refrac, & ! refractivity of model on model levels Rad, & ! radius of curvature of earth at observation lat, & ! latitude at observation und, & ! geoid undulation above WGS-84 - refrac, & ! refractivity of model on model levels nr) ! Calculated model impact parameters IMPLICIT NONE @@ -307,267 +306,5 @@ SUBROUTINE Ops_GPSRO_geop_geom (lat, & END SUBROUTINE Ops_GPSRO_geop_geom - -!------------------------------------------------------------------------------- -! (C) Crown copyright Met Office. All rights reserved. -! Refer to COPYRIGHT.txt of this distribution for details. -!------------------------------------------------------------------------------- -! GPSRO refractivity forward operator -!------------------------------------------------------------------------------- - -SUBROUTINE Ops_GPSRO_refrac (nstate, & - nlevP, & - nb, & - nlevq, & - za, & - zb, & - x, & - GPSRO_pseudo_ops, & - GPSRO_vert_interp_ops, & - refracerr, & - refrac, & - T, & - z_pseudo, & - N_pseudo, & - nb_pseudo) - -IMPLICIT NONE - -! Subroutine arguments: -INTEGER, INTENT(IN) :: nstate ! no. of levels in state vec. -INTEGER, INTENT(IN) :: nlevP ! no. of p levels in state vec. -INTEGER, INTENT(IN) :: nb ! no. of non-pseudo levs (=nlevq) -INTEGER, INTENT(IN) :: nlevq ! no. of theta levels -REAL(kind_real), INTENT(IN) :: za(:) ! heights of rho levs -REAL(kind_real), INTENT(IN) :: zb(:) ! heights of theta levs -REAL(kind_real), INTENT(IN) :: x(:) ! state vector -LOGICAL, INTENT(IN) :: GPSRO_pseudo_ops -LOGICAL, INTENT(IN) :: GPSRO_vert_interp_ops -LOGICAL, INTENT(OUT) :: refracerr ! refractivity error -REAL(kind_real), INTENT(OUT) :: refrac(nb) ! refrac on theta levs -REAL(kind_real), INTENT(OUT) :: T(nb) ! Temp. on theta levs -REAL(kind_real), ALLOCATABLE, INTENT(OUT), OPTIONAL :: z_pseudo(:) ! height of pseudo levs -REAL(kind_real), ALLOCATABLE, INTENT(OUT), OPTIONAL :: N_pseudo(:) ! Ref. on pseudo levs -INTEGER, INTENT(OUT), OPTIONAL :: nb_pseudo ! no. of pseudo levs - -! Local declarations: -integer, parameter :: max_string = 800 -CHARACTER(len=*), PARAMETER :: RoutineName = "Ops_GPSRO_refrac" -CHARACTER(len=max_string) :: message -INTEGER :: i -INTEGER :: counter -INTEGER :: search_lev ! The vertical level to start searching from to - ! find matching temperature-level heights -INTEGER :: this_lev ! Matching level to temperature-level height -REAL(kind_real) :: P(nlevP) -REAL(kind_real) :: Exner(nlevP) -REAL(kind_real) :: q(nlevq) -REAL(kind_real) :: Pb(nb) -REAL(kind_real) :: Tv -REAL(kind_real) :: Ex_theta -REAL(kind_real) :: pwt1 -REAL(kind_real) :: pwt2 -REAL(kind_real) :: Ndry -REAL(kind_real) :: Nwet -REAL(kind_real), ALLOCATABLE :: P_pseudo(:) -REAL(kind_real), ALLOCATABLE :: q_pseudo(:) -REAL(kind_real), ALLOCATABLE :: T_pseudo(:) -REAL(kind_real) :: gamma -REAL(kind_real) :: beta -REAL(kind_real) :: c ! continuity constant for hydrostatic pressure -LOGICAL :: nonmon -LOGICAL :: unphys - -! Allocate arrays for pseudo-level processing -IF (GPSRO_pseudo_ops) THEN - nb_pseudo = 2 * nb - 1 - ALLOCATE (P_pseudo(nb_pseudo)) - ALLOCATE (q_pseudo(nb_pseudo)) - ALLOCATE (T_pseudo(nb_pseudo)) - ALLOCATE (z_pseudo(nb_pseudo)) - ALLOCATE (N_pseudo(nb_pseudo)) -END IF - -! Set up the P and q vectors from x -P(:) = x(1:nlevP) -q(:) = x(nlevP + 1:nstate) - -! Initialise refractivity arrays to missing Data -refrac(:) = missing_value(refrac(1)) -T(:) = missing_value(T(1)) -nonmon = .FALSE. -unphys = .FALSE. -refracerr = .FALSE. - -DO i = 1, nlevP - IF (P(i) == missing_value(P(i))) THEN !pressure missing - refracerr = .TRUE. - WRITE(message, *) RoutineName, "Missing value P", i - CALL fckit_log % warning(message) - EXIT - END IF -END DO - -DO i = 1, nlevP-1 - IF (P(i) - P(i + 1) < 0.0) THEN !or non-monotonic pressure - refracerr = .TRUE. - nonmon = .TRUE. - WRITE(message,*) "Non monotonic", i, P(i), P(i+1) - CALL fckit_log % warning(message) - EXIT - END IF -END DO - -IF (ANY (P(:) <= 0.0)) THEN !pressure zero or negative - refracerr = .TRUE. - unphys = .TRUE. -END IF - -IF (ANY (q(:) < 0.0)) THEN !humidity negative - refracerr = .TRUE. - unphys = .TRUE. -END IF - -! only proceed if pressure is valid -IF (refracerr) THEN - IF (nonmon) THEN - CALL fckit_log%warning (RoutineName // ": GPSRO Pressure non-monotonic") - ELSE IF (unphys) THEN - CALL fckit_log%warning (RoutineName // ": GPSRO Pressure <= zero") - ELSE - CALL fckit_log%warning (RoutineName // ": GPSRO Pressure missing") - END IF -ELSE - - ! Calculate exner on rho levels. - Exner(:) = (P(:) / Pref) ** rd_over_cp - -! PRINT*, 'Exner' -! WRITE(*, '(7E18.10)') exner - - ! Calculate the refractivity on the b levels - search_lev = 1 - DO i = 1, nb - ! Search for the first pressure (rho) level which has a height greater than - ! the temperature (theta) level being considered. Interpolate the pressure - ! to the temperature levels using the level which has been found. - DO this_lev = search_lev, nlevP - IF (za(this_lev) > zb(i)) THEN - EXIT - END IF - END DO - IF (this_lev == 1) THEN - ! Calc. pressure pb - Pb(i) = P(i) - CALL fckit_log%warning (RoutineName // ": Bottom temperature level is below bottom pressure level! Extrapolating!") - CALL fckit_log%warning (RoutineName // ": Results could be very inaccurate") - ELSE - ! Calc. pressure pb - pwt1 = (za(this_lev) - zb(i)) / (za(this_lev) - za(this_lev-1)) - search_lev = this_lev - pwt2 = 1.0 - pwt1 - - ! Calculate the pressure on the theta level. - IF (GPSRO_vert_interp_ops) THEN - ! Assume ln(P) linear with height - Pb(i) = EXP (pwt1 * LOG (P(this_lev-1)) + pwt2 * LOG (P(this_lev))) - ELSE - ! Assume Exner varies linearly with height - Pb(i) = Pref * (pwt1 * (P(this_lev-1) / Pref) ** rd_over_cp + pwt2 * & - (P(this_lev) / Pref) ** rd_over_cp) ** (1.0 / rd_over_cp) - END IF - END IF - - ! Calculate the Exner on the theta level. - Ex_theta = (Pb(i) / Pref) ** rd_over_cp - - ! Calculate mean layer Tv (virtual temperature) using ND definition - Tv = grav * (za(this_lev) - za(this_lev-1)) * Ex_theta / & - (Cp * (Exner(this_lev-1) - Exner(this_lev))) - - IF (i > nlevq) THEN - - T(i) = Tv - - ! No wet component - - Nwet = 0.0 - - ELSE - - T(i) = Tv / (1.0 + C_virtual * q(i)) - - ! Wet component - - Nwet = n_beta * Pb(i) * q(i) / (T(i) ** 2 * (mw_ratio + (1.0 - mw_ratio) * q(i))) - - END IF - - Ndry = n_alpha * Pb(i) / T(i) - - refrac(i) = Ndry + Nwet - - END DO - - ! Do pseudo-level processing - IF (GPSRO_pseudo_ops) THEN - counter = 1 - DO i = 1, nb_pseudo - - ! Odd 'i' (i.e. copies of actual model level values) - IF (MOD (i, 2) > 0) THEN - z_pseudo(i) = zb(counter) - P_pseudo(i) = Pb(counter) - q_pseudo(i) = q(counter) - T_pseudo(i) = T(counter) - counter = counter + 1 - - ! Even 'i' (i.e. intermediate pseudo-levels) - ELSE - z_pseudo(i) = (zb(counter - 1) + zb(counter)) / 2.0 - - ! Assume exponential variation when humidities are positive - IF (MIN (q(counter - 1), q(counter)) > 0.0) THEN - gamma = LOG (q(counter - 1) / q(counter)) / (zb(counter) - zb(counter - 1)) - q_pseudo(i) = q(counter - 1) * EXP (-gamma * (z_pseudo(i) - z_pseudo(i - 1))) - - ! Assume linear variation if humidities are -ve - ELSE - q_pseudo(i) = q(counter - 1) + (q(counter) - q(counter - 1)) / (zb(counter) - & - zb(counter - 1)) * (z_pseudo(i) - zb(counter - 1)) - END IF - - ! T varies linearly with height - beta = (T(counter) - T(counter - 1)) / (zb(counter) - zb(counter - 1)) - T_pseudo(i) = T(counter - 1) + beta * (z_pseudo(i) - zb(counter - 1)) - - ! Pressure varies to maintain hydrostatic balance - IF (ABS (T(counter) - T(counter - 1)) > 1.0E-10) THEN - c = ((Pb(counter) / Pb(counter - 1)) * (T(counter) / T(counter - 1)) ** (grav / (rd * beta)) - & - 1.0) / (zb(counter) - zb(counter - 1)) - P_pseudo(i) = (Pb(counter - 1) * (T_pseudo(i) / T(counter - 1)) ** & - (-grav / (rd * beta))) * (1.0 + c * (z_pseudo(i) - zb(counter - 1))) - ELSE - ! If layer is isothermal, explicitly force P to vary exponentially to avoid singularity - P_pseudo(i) = Pb(counter - 1) * EXP (LOG (Pb(counter) / Pb(counter - 1)) * & - ((z_pseudo(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1)))) - END IF - END IF - END DO - - N_pseudo = n_alpha * P_pseudo / T_pseudo + n_beta * P_pseudo * q_pseudo / & - (T_pseudo ** 2 * (mw_ratio + (1.0 - mw_ratio) * q_pseudo)) - END IF - -END IF - -IF (GPSRO_pseudo_ops) THEN - IF (ALLOCATED (P_pseudo)) DEALLOCATE (P_pseudo) - IF (ALLOCATED (q_pseudo)) DEALLOCATE (q_pseudo) - IF (ALLOCATED (T_pseudo)) DEALLOCATE (T_pseudo) -END IF - -END SUBROUTINE Ops_GPSRO_refrac - !------------------------------------------------------------------------- end module ufo_gnssro_ukmo1d_utils_mod diff --git a/src/ufo/gnssro/BndNBAM/ObsGnssroBndNBAMTLAD.cc b/src/ufo/gnssro/BndNBAM/ObsGnssroBndNBAMTLAD.cc index f10d1ff2a..e89974b64 100644 --- a/src/ufo/gnssro/BndNBAM/ObsGnssroBndNBAMTLAD.cc +++ b/src/ufo/gnssro/BndNBAM/ObsGnssroBndNBAMTLAD.cc @@ -28,7 +28,7 @@ static LinearObsOperatorMaker makerGnssroBndNBAMTL_("Gnssr ObsGnssroBndNBAMTLAD::ObsGnssroBndNBAMTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOperGnssroBndNBAM_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOperGnssroBndNBAM_(0), varin_() { std::vector vv{"air_temperature", "specific_humidity"}; @@ -64,20 +64,20 @@ ObsGnssroBndNBAMTLAD::~ObsGnssroBndNBAMTLAD() { void ObsGnssroBndNBAMTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_gnssro_bndnbam_tlad_settraj_f90(keyOperGnssroBndNBAM_, geovals.toFortran(), odb_); + ufo_gnssro_bndnbam_tlad_settraj_f90(keyOperGnssroBndNBAM_, geovals.toFortran(), obsspace()); } // ----------------------------------------------------------------------------- void ObsGnssroBndNBAMTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_gnssro_bndnbam_simobs_tl_f90(keyOperGnssroBndNBAM_, geovals.toFortran(), odb_, + ufo_gnssro_bndnbam_simobs_tl_f90(keyOperGnssroBndNBAM_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); } // ----------------------------------------------------------------------------- void ObsGnssroBndNBAMTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_gnssro_bndnbam_simobs_ad_f90(keyOperGnssroBndNBAM_, geovals.toFortran(), odb_, + ufo_gnssro_bndnbam_simobs_ad_f90(keyOperGnssroBndNBAM_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); } diff --git a/src/ufo/gnssro/BndNBAM/ObsGnssroBndNBAMTLAD.h b/src/ufo/gnssro/BndNBAM/ObsGnssroBndNBAMTLAD.h index 639ac5651..ea7968bb8 100644 --- a/src/ufo/gnssro/BndNBAM/ObsGnssroBndNBAMTLAD.h +++ b/src/ufo/gnssro/BndNBAM/ObsGnssroBndNBAMTLAD.h @@ -57,7 +57,6 @@ class ObsGnssroBndNBAMTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOperGnssroBndNBAM_; - const ioda::ObsSpace& odb_; std::unique_ptr varin_; }; diff --git a/src/ufo/gnssro/BndNBAM/ufo_gnssro_bndnbam_mod.F90 b/src/ufo/gnssro/BndNBAM/ufo_gnssro_bndnbam_mod.F90 index 0a7e6b451..24c80b0a3 100644 --- a/src/ufo/gnssro/BndNBAM/ufo_gnssro_bndnbam_mod.F90 +++ b/src/ufo/gnssro/BndNBAM/ufo_gnssro_bndnbam_mod.F90 @@ -19,6 +19,7 @@ module ufo_gnssro_bndnbam_mod use gnssro_mod_grids, only : get_coordinate_value use fckit_log_module, only : fckit_log use ufo_gnssro_bndnbam_util_mod + use ufo_utils_mod, only: cmp_strings implicit none public :: ufo_gnssro_BndNBAM @@ -63,6 +64,8 @@ subroutine ufo_gnssro_bndnbam_simobs(self, geovals, hofx, obss) real(kind_real), allocatable :: obsLat(:), obsImpP(:),obsLocR(:), obsGeoid(:), obsValue(:) integer(c_size_t), allocatable :: obsRecnum(:) real(kind_real), allocatable :: temperature(:) + real(kind_real), allocatable :: humidity(:),refractivity(:),pressure(:) + real(kind_real) :: temp, geop real(kind_real) :: wf integer :: wi, wi2 real(kind_real) :: grids(ngrd) @@ -91,6 +94,14 @@ subroutine ufo_gnssro_bndnbam_simobs(self, geovals, hofx, obss) allocate(temperature(nlocs)) temperature = missing + if (trim(self%roconf%output_diags) .eq. "true") then + allocate(humidity(nlocs)) ! at obs location + allocate(pressure(nlocs)) ! at obs location + allocate(refractivity(nlocs)) ! at obs location + humidity = missing + pressure = missing + refractivity = missing + endif allocate(super_refraction_flag(nlocs)) super_refraction_flag = 0 allocate(LayerIdx(nlocs)) @@ -260,17 +271,25 @@ subroutine ufo_gnssro_bndnbam_simobs(self, geovals, hofx, obss) ! save the obs vertical location index (unit: model layer) LayerIdx(iobs) = min(max(1, int(sIndx)), nlev) -! calculating temperature at obs location to obs space for BackgroundCheck RONBAM +! calculating virtual temperature at obs location to obs space for BackgroundCheck RONBAM indx=sIndx wi=min(max(1,indx),nlev) wi2=max(1,min(indx+1,nlev)) wf=sIndx-float(wi) wf=max(zero,min(wf,one)) temperature(iobs)=gesTv(wi,iobs)*(one-wf)+gesTv(wi2,iobs)*wf + if (trim(self%roconf%output_diags) .eq. "true") then + humidity(iobs)= gesQ(wi,iobs)*(one-wf)+gesQ(wi2,iobs)*wf + temp = gesT(wi,iobs)*(one-wf)+gesT(wi2,iobs)*wf + geop = gesZ(wi,iobs)*(one-wf)+gesZ(wi2,iobs)*wf + pressure(iobs)= gesP(wi,iobs)/exp(two*grav*(geop-gesZ(wi,iobs))/(rd*(temperature(iobs)+gesTv(wi,iobs)))) + call compute_refractivity(temp, humidity(iobs), pressure(iobs), & + refractivity(iobs), self%roconf%use_compress) + end if ! (2) super-refaction ! (2.1) GSI style super refraction check - if(trim(self%roconf%super_ref_qc) == "NBAM") then + if(cmp_strings(self%roconf%super_ref_qc, "NBAM")) then obsImpH = (obsImpP(iobs) - obsLocR(iobs)) * r1em3 !impact heigt: a-r_earth @@ -306,7 +325,7 @@ subroutine ufo_gnssro_bndnbam_simobs(self, geovals, hofx, obss) end if ! obsImpH <= six ! ROPP style super refraction check - else if(trim(self%roconf%super_ref_qc) == "ECMWF") then + else if(cmp_strings(self%roconf%super_ref_qc, "ECMWF")) then sr_hgt_idx = 1 do k = nlev, 2, -1 @@ -338,7 +357,7 @@ subroutine ufo_gnssro_bndnbam_simobs(self, geovals, hofx, obss) end do obs_loop end do rec_loop - if (trim(self%roconf%super_ref_qc) == "NBAM" .and. self%roconf%sr_steps > 1 ) then + if (cmp_strings(self%roconf%super_ref_qc, "NBAM") .and. self%roconf%sr_steps > 1 ) then rec_loop2: do irec = 1, nrecs if (obs_max(irec) > 0 ) then @@ -382,12 +401,20 @@ subroutine ufo_gnssro_bndnbam_simobs(self, geovals, hofx, obss) call fckit_log%info(err_msg) end if ! end check if ZERO OBS -! putting temeprature at obs location to obs space for BackgroundCheck RONBAM - call obsspace_put_db(obss, "MetaData", "temperature", temperature) +! putting virtual temeprature at obs location to obs space for BackgroundCheck RONBAM + call obsspace_put_db(obss, "MetaData", "virtual_temperature", temperature) ! putting super refraction flag to obs space call obsspace_put_db(obss, "SRflag", "bending_angle", super_refraction_flag) ! saving obs vertical model layer postion for later call obsspace_put_db(obss, "LayerIdx", "bending_angle", LayerIdx) + if (trim(self%roconf%output_diags) .eq. "true") then + call obsspace_put_db(obss, "ObsDiag", "specific_humidity", humidity) + call obsspace_put_db(obss, "ObsDiag", "refractivity", refractivity) + call obsspace_put_db(obss, "ObsDiag", "pressure", pressure) + deallocate(humidity) + deallocate(pressure) + deallocate(refractivity) + end if deallocate(super_refraction_flag) deallocate(temperature) deallocate(LayerIdx) diff --git a/src/ufo/gnssro/BndROPP1D/CMakeLists.txt b/src/ufo/gnssro/BndROPP1D/CMakeLists.txt index 405ad9950..dae854d91 100644 --- a/src/ufo/gnssro/BndROPP1D/CMakeLists.txt +++ b/src/ufo/gnssro/BndROPP1D/CMakeLists.txt @@ -2,7 +2,7 @@ # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -if( ${ROPP-UFO_FOUND} ) +if( ${ropp-ufo_FOUND} ) set ( bndropp1d_src_files ObsGnssroBndROPP1D.h ObsGnssroBndROPP1D.cc @@ -17,7 +17,7 @@ set ( bndropp1d_src_files ufo_gnssro_ropp1d_utils_mod.F90 PARENT_SCOPE ) -else( ${ROPP-UFO_FOUND} ) +else( ${ropp-ufo_FOUND} ) set ( bndropp1d_src_files ObsGnssroBndROPP1D.h ObsGnssroBndROPP1D.cc @@ -31,4 +31,4 @@ set ( bndropp1d_src_files ufo_gnssro_bndropp1d_tlad_mod_stub.F90 PARENT_SCOPE ) -endif( ${ROPP-UFO_FOUND} ) +endif( ${ropp-ufo_FOUND} ) diff --git a/src/ufo/gnssro/BndROPP1D/ObsGnssroBndROPP1DTLAD.cc b/src/ufo/gnssro/BndROPP1D/ObsGnssroBndROPP1DTLAD.cc index 37912efe6..2e0621f7c 100644 --- a/src/ufo/gnssro/BndROPP1D/ObsGnssroBndROPP1DTLAD.cc +++ b/src/ufo/gnssro/BndROPP1D/ObsGnssroBndROPP1DTLAD.cc @@ -28,7 +28,7 @@ static LinearObsOperatorMaker makerGnssroBndROPP1DTL_("G ObsGnssroBndROPP1DTLAD::ObsGnssroBndROPP1DTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOperGnssroBndROPP1D_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOperGnssroBndROPP1D_(0), varin_() { const eckit::LocalConfiguration obsOptions(config, "obs options"); @@ -51,20 +51,20 @@ ObsGnssroBndROPP1DTLAD::~ObsGnssroBndROPP1DTLAD() { void ObsGnssroBndROPP1DTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_gnssro_bndropp1d_tlad_settraj_f90(keyOperGnssroBndROPP1D_, geovals.toFortran(), odb_); + ufo_gnssro_bndropp1d_tlad_settraj_f90(keyOperGnssroBndROPP1D_, geovals.toFortran(), obsspace()); } // ----------------------------------------------------------------------------- void ObsGnssroBndROPP1DTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_gnssro_bndropp1d_simobs_tl_f90(keyOperGnssroBndROPP1D_, geovals.toFortran(), odb_, + ufo_gnssro_bndropp1d_simobs_tl_f90(keyOperGnssroBndROPP1D_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); } // ----------------------------------------------------------------------------- void ObsGnssroBndROPP1DTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_gnssro_bndropp1d_simobs_ad_f90(keyOperGnssroBndROPP1D_, geovals.toFortran(), odb_, + ufo_gnssro_bndropp1d_simobs_ad_f90(keyOperGnssroBndROPP1D_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); } diff --git a/src/ufo/gnssro/BndROPP1D/ObsGnssroBndROPP1DTLAD.h b/src/ufo/gnssro/BndROPP1D/ObsGnssroBndROPP1DTLAD.h index 5bf554bb9..fff42f694 100644 --- a/src/ufo/gnssro/BndROPP1D/ObsGnssroBndROPP1DTLAD.h +++ b/src/ufo/gnssro/BndROPP1D/ObsGnssroBndROPP1DTLAD.h @@ -56,7 +56,6 @@ class ObsGnssroBndROPP1DTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOperGnssroBndROPP1D_; - const ioda::ObsSpace& odb_; std::unique_ptr varin_; }; diff --git a/src/ufo/gnssro/BndROPP2D/CMakeLists.txt b/src/ufo/gnssro/BndROPP2D/CMakeLists.txt index 41bf3c26a..2c0353121 100644 --- a/src/ufo/gnssro/BndROPP2D/CMakeLists.txt +++ b/src/ufo/gnssro/BndROPP2D/CMakeLists.txt @@ -2,7 +2,7 @@ # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -if( ${ROPP-UFO_FOUND} ) +if( ${ropp-ufo_FOUND} ) set ( bndropp2d_src_files ObsGnssroBndROPP2D.h ObsGnssroBndROPP2D.cc @@ -18,7 +18,7 @@ set ( bndropp2d_src_files ufo_gnssro_2d_locs_mod.F90 PARENT_SCOPE ) -else( ${ROPP-UFO_FOUND} ) +else( ${ropp-ufo_FOUND} ) set ( bndropp2d_src_files ObsGnssroBndROPP2D.h ObsGnssroBndROPP2D.cc @@ -33,4 +33,4 @@ set ( bndropp2d_src_files ufo_gnssro_2d_locs_mod_stub.F90 PARENT_SCOPE ) -endif( ${ROPP-UFO_FOUND} ) +endif( ${ropp-ufo_FOUND} ) diff --git a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.cc b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.cc index b500d8ee1..441f029b9 100644 --- a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.cc +++ b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.cc @@ -29,7 +29,8 @@ static ObsOperatorMaker makerGnssroBndROPP2D_("GnssroBndROPP ObsGnssroBndROPP2D::ObsGnssroBndROPP2D(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : ObsOperatorBase(odb, config), keyOperGnssroBndROPP2D_(0), odb_(odb), varin_() + : ObsOperatorBase(odb, config), keyOperGnssroBndROPP2D_(0), odb_(odb), varin_(), + nhoriz_(config.getInt("obs options.n_horiz")) { const std::vector vv{"air_temperature", "specific_humidity", "air_pressure", "geopotential_height", "surface_altitude"}; @@ -57,13 +58,20 @@ void ObsGnssroBndROPP2D::simulateObs(const GeoVaLs & gom, ioda::ObsVector & ovec } // ----------------------------------------------------------------------------- -std::unique_ptr ObsGnssroBndROPP2D::locations(const util::DateTime & t1, - const util::DateTime & t2) const { - std::unique_ptr locs(new Locations(odb_.comm())); - - int keylocs = locs->toFortran(); - - ufo_gnssro_2d_locs_init_f90(keyOperGnssroBndROPP2D_, keylocs, odb_, t1, t2); +std::unique_ptr ObsGnssroBndROPP2D::locations() const { + std::vector lons(odb_.nlocs()*nhoriz_); + std::vector lats(odb_.nlocs()*nhoriz_); + std::vector times(odb_.nlocs()*nhoriz_); + + std::vector times_notduplicated(odb_.nlocs()); + odb_.get_db("MetaData", "datetime", times_notduplicated); + for (size_t jloc = 0; jloc < odb_.nlocs(); ++jloc) { + for (size_t jhoriz = 0; jhoriz < nhoriz_; ++jhoriz) { + times[jloc*nhoriz_ + jhoriz] = times_notduplicated[jloc]; + } + } + ufo_gnssro_2d_locs_init_f90(keyOperGnssroBndROPP2D_, odb_, lons.size(), lons[0], lats[0]); + std::unique_ptr locs(new Locations(lons, lats, times, odb_.distribution())); return locs; } diff --git a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.h b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.h index 3a509ad33..8742eb4b5 100644 --- a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.h +++ b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.h @@ -48,8 +48,7 @@ class ObsGnssroBndROPP2D : public ObsOperatorBase, // Other const oops::Variables & requiredVars() const override {return *varin_;} - std::unique_ptr locations(const util::DateTime &, - const util::DateTime &) const override; + std::unique_ptr locations() const override; int & toFortran() {return keyOperGnssroBndROPP2D_;} const int & toFortran() const {return keyOperGnssroBndROPP2D_;} @@ -59,6 +58,7 @@ class ObsGnssroBndROPP2D : public ObsOperatorBase, F90hop keyOperGnssroBndROPP2D_; const ioda::ObsSpace& odb_; std::unique_ptr varin_; + const size_t nhoriz_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.interface.F90 b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.interface.F90 index 2882b829d..f1da6f4f6 100644 --- a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.interface.F90 +++ b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.interface.F90 @@ -9,8 +9,6 @@ module ufo_gnssro_bndropp2d_mod_c use fckit_configuration_module, only: fckit_configuration use ufo_gnssro_bndropp2d_mod - use ufo_locs_mod - use ufo_locs_mod_c use ufo_gnssro_2d_locs_mod implicit none @@ -81,26 +79,19 @@ subroutine ufo_gnssro_bndropp2d_simobs_c(c_key_self, c_key_geovals, c_obsspace, end subroutine ufo_gnssro_bndropp2d_simobs_c ! ------------------------------------------------------------------------------ -subroutine ufo_gnssro_2d_locs_init_c(c_key_self, c_key_locs, c_obsspace, c_t1, c_t2) bind(c,name='ufo_gnssro_2d_locs_init_f90') -use datetime_mod +subroutine ufo_gnssro_2d_locs_init_c(c_key_self, c_obsspace, c_nlocs, c_lons, c_lats) & + bind(c,name='ufo_gnssro_2d_locs_init_f90') implicit none -integer(c_int), intent(in) :: c_key_self ! operator key -integer(c_int), intent(inout) :: c_key_locs ! location key +integer(c_int), intent(in) :: c_key_self type(c_ptr), value, intent(in) :: c_obsspace -type(c_ptr), value, intent(in) :: c_t1, c_t2 +integer(c_int), intent(in) :: c_nlocs +real(c_float), intent(inout) :: c_lons(c_nlocs) +real(c_float), intent(inout) :: c_lats(c_nlocs) -type(ufo_locs), pointer :: locs type(ufo_gnssro_BndROPP2D), pointer :: self -integer, parameter :: max_string = 800 -type(datetime) :: t1, t2 - -call c_f_datetime(c_t1, t1) -call c_f_datetime(c_t2, t2) - -call ufo_locs_registry%get(c_key_locs, locs) call ufo_gnssro_BndROPP2D_registry%get(c_key_self, self) -call ufo_gnssro_2d_locs_init(self,locs, c_obsspace, t1, t2) +call ufo_gnssro_2d_locs_init(self, c_obsspace, c_nlocs, c_lons, c_lats) end subroutine ufo_gnssro_2d_locs_init_c diff --git a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.interface.h b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.interface.h index 039dfb284..a5bb9a03e 100644 --- a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.interface.h +++ b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2D.interface.h @@ -10,7 +10,6 @@ #include "ioda/ObsSpace.h" #include "ufo/Fortran.h" -#include "ufo/Locations.h" namespace ufo { @@ -24,8 +23,8 @@ extern "C" { // ----------------------------------------------------------------------------- // Gnssro bending angle observation operators - (ROPP2D) // ----------------------------------------------------------------------------- - void ufo_gnssro_2d_locs_init_f90(const F90hop &, F90locs &, const ioda::ObsSpace &, - const util::DateTime &, const util::DateTime &); + void ufo_gnssro_2d_locs_init_f90(const F90hop &, const ioda::ObsSpace &, const int &, + float &, float &); void ufo_gnssro_bndropp2d_setup_f90(F90hop &, const eckit::Configuration &, const int &); void ufo_gnssro_bndropp2d_delete_f90(F90hop &); void ufo_gnssro_bndropp2d_simobs_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, diff --git a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2DTLAD.cc b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2DTLAD.cc index 604f645c0..20644315f 100644 --- a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2DTLAD.cc +++ b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2DTLAD.cc @@ -28,7 +28,7 @@ static LinearObsOperatorMaker makerGnssroBndROPP2DTL_("G ObsGnssroBndROPP2DTLAD::ObsGnssroBndROPP2DTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOperGnssroBndROPP2D_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOperGnssroBndROPP2D_(0), varin_() { const eckit::LocalConfiguration obsOptions(config, "obs options"); @@ -51,20 +51,20 @@ ObsGnssroBndROPP2DTLAD::~ObsGnssroBndROPP2DTLAD() { void ObsGnssroBndROPP2DTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_gnssro_bndropp2d_tlad_settraj_f90(keyOperGnssroBndROPP2D_, geovals.toFortran(), odb_); + ufo_gnssro_bndropp2d_tlad_settraj_f90(keyOperGnssroBndROPP2D_, geovals.toFortran(), obsspace()); } // ----------------------------------------------------------------------------- void ObsGnssroBndROPP2DTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_gnssro_bndropp2d_simobs_tl_f90(keyOperGnssroBndROPP2D_, geovals.toFortran(), odb_, + ufo_gnssro_bndropp2d_simobs_tl_f90(keyOperGnssroBndROPP2D_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); } // ----------------------------------------------------------------------------- void ObsGnssroBndROPP2DTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_gnssro_bndropp2d_simobs_ad_f90(keyOperGnssroBndROPP2D_, geovals.toFortran(), odb_, + ufo_gnssro_bndropp2d_simobs_ad_f90(keyOperGnssroBndROPP2D_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); } diff --git a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2DTLAD.h b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2DTLAD.h index 3394ccc74..005998826 100644 --- a/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2DTLAD.h +++ b/src/ufo/gnssro/BndROPP2D/ObsGnssroBndROPP2DTLAD.h @@ -56,7 +56,6 @@ class ObsGnssroBndROPP2DTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOperGnssroBndROPP2D_; - const ioda::ObsSpace& odb_; std::unique_ptr varin_; }; diff --git a/src/ufo/gnssro/BndROPP2D/ufo_gnssro_2d_locs_mod.F90 b/src/ufo/gnssro/BndROPP2D/ufo_gnssro_2d_locs_mod.F90 index 7220e2105..376856508 100644 --- a/src/ufo/gnssro/BndROPP2D/ufo_gnssro_2d_locs_mod.F90 +++ b/src/ufo/gnssro/BndROPP2D/ufo_gnssro_2d_locs_mod.F90 @@ -3,14 +3,13 @@ module ufo_gnssro_2d_locs_mod use iso_c_binding use fckit_log_module, only : fckit_log use kinds, only : kind_real -use ufo_locs_mod public:: ufo_gnssro_2d_locs_init contains !------------------------------------------------------------------------- !------------------------------------------------------------------------- -subroutine ufo_gnssro_2d_locs_init(self, locs, obss, t1, t2) +subroutine ufo_gnssro_2d_locs_init(self, obss, nlocs_ext, lons, lats) use kinds use datetime_mod use obsspace_mod @@ -18,19 +17,17 @@ subroutine ufo_gnssro_2d_locs_init(self, locs, obss, t1, t2) implicit none - type(ufo_locs), intent(inout) :: locs class(ufo_gnssro_BndROPP2D), intent(inout) :: self - type(c_ptr), value, intent(in) :: obss - type(datetime), intent(in) :: t1, t2 + type(c_ptr), value, intent(in) :: obss + integer, intent(in) :: nlocs_ext + real(c_float), dimension(nlocs_ext), intent(inout) :: lons, lats character(len=*),parameter :: myname = "ufo_gnssro_2d_locs_init" integer, parameter :: max_string = 800 character(max_string) :: err_msg - integer :: i, j, tw_nlocs,nlocs - integer, dimension(:), allocatable :: tw_indx + integer :: i, j,nlocs real(kind_real), dimension(:), allocatable :: lon, lat - type(datetime), dimension(:), allocatable :: date_time ! gnss ro data 2d location real(kind_real), dimension(:), allocatable :: obsAzim @@ -40,54 +37,26 @@ subroutine ufo_gnssro_2d_locs_init(self, locs, obss, t1, t2) dtheta = self%roconf%dtheta n_horiz = self%roconf%n_horiz - ! Local copies pre binning - nlocs = obsspace_get_nlocs(obss) + nlocs = nlocs_ext / n_horiz - allocate(date_time(nlocs), lon(nlocs), lat(nlocs)) - - call obsspace_get_db(obss, "MetaData", "datetime", date_time) + allocate(lon(nlocs), lat(nlocs)) call obsspace_get_db(obss, "MetaData", "longitude", lon) call obsspace_get_db(obss, "MetaData", "latitude", lat) - ! Generate the timing window indices - allocate(tw_indx(nlocs)) - tw_nlocs = 0 - do i = 1, nlocs - if (date_time(i) > t1 .and. date_time(i) <= t2) then - tw_nlocs = tw_nlocs + 1 - tw_indx(tw_nlocs) = i - endif - enddo - allocate(obsAzim(nlocs)) - if (obsspace_has(obss,"ObsValue","bending_angle")) then - if (obsspace_has(obss, "MetaData", "sensor_azimuth_angle")) then - call obsspace_get_db(obss, "MetaData", "sensor_azimuth_angle", obsAzim) - else - write(err_msg,*) myname, ' error: sensor_azimuth_angle not found' - call abor1_ftn(err_msg) - endif - endif + call obsspace_get_db(obss, "MetaData", "sensor_azimuth_angle", obsAzim) !Setup ufo 2d locations - call ufo_locs_setup(locs, tw_nlocs*n_horiz) - do i = 1, tw_nlocs - call ropp_fm_2d_plane(lat(tw_indx(i)),lon(tw_indx(i)),obsAzim(tw_indx(i)),dtheta,n_horiz,plat_2d,plon_2d,kerror) - locs%lon( (i-1)*n_horiz+1 : i*n_horiz) = plon_2d - locs%lat( (i-1)*n_horiz+1 : i*n_horiz) = plat_2d + do i = 1, nlocs + call ropp_fm_2d_plane(lat(i),lon(i),obsAzim(i),dtheta,n_horiz,plat_2d,plon_2d,kerror) + lons( (i-1)*n_horiz+1 : i*n_horiz) = plon_2d + lats( (i-1)*n_horiz+1 : i*n_horiz) = plat_2d ! save ufo_locs to self - self%obsLat2d( (i-1)*n_horiz+1 : i*n_horiz) = locs%lat( (i-1)*n_horiz+1 : i*n_horiz) - self%obsLon2d( (i-1)*n_horiz+1 : i*n_horiz) = locs%lon( (i-1)*n_horiz+1 : i*n_horiz) - do j = 1, n_horiz - locs%indx((i-1)*n_horiz+j) = (tw_indx(i)-1)*n_horiz+j - locs%time((i-1)*n_horiz+j) = date_time(tw_indx(i)) - end do + self%obsLat2d( (i-1)*n_horiz+1 : i*n_horiz) = lats( (i-1)*n_horiz+1 : i*n_horiz) + self%obsLon2d( (i-1)*n_horiz+1 : i*n_horiz) = lons( (i-1)*n_horiz+1 : i*n_horiz) end do - do i = 1, nlocs - call datetime_delete(date_time(i)) - enddo - deallocate(date_time, lon, lat, tw_indx, obsAzim) + deallocate(lon, lat, obsAzim) end subroutine ufo_gnssro_2d_locs_init diff --git a/src/ufo/gnssro/BndROPP2D/ufo_gnssro_2d_locs_mod_stub.F90 b/src/ufo/gnssro/BndROPP2D/ufo_gnssro_2d_locs_mod_stub.F90 index 387c4521f..785a09f5e 100644 --- a/src/ufo/gnssro/BndROPP2D/ufo_gnssro_2d_locs_mod_stub.F90 +++ b/src/ufo/gnssro/BndROPP2D/ufo_gnssro_2d_locs_mod_stub.F90 @@ -3,35 +3,31 @@ module ufo_gnssro_2d_locs_mod use iso_c_binding use fckit_log_module, only : fckit_log use kinds, only : kind_real -use ufo_locs_mod public:: ufo_gnssro_2d_locs_init contains !------------------------------------------------------------------------- !------------------------------------------------------------------------- -subroutine ufo_gnssro_2d_locs_init(self, locs, obss, t1, t2) +subroutine ufo_gnssro_2d_locs_init(self, obss, nlocs_ext, lons, lats) use kinds use datetime_mod - use fckit_log_module, only : fckit_log use obsspace_mod use ufo_gnssro_bndropp2d_mod implicit none - type(ufo_locs), intent(inout) :: locs class(ufo_gnssro_BndROPP2D), intent(inout) :: self - type(c_ptr), value, intent(in) :: obss - type(datetime), intent(in) :: t1, t2 + type(c_ptr), value, intent(in) :: obss + integer, intent(in) :: nlocs_ext + real(c_float), dimension(nlocs_ext), intent(inout) :: lons, lats character(len=*),parameter :: myname = "ufo_gnssro_2d_locs_init" integer, parameter :: max_string = 800 character(max_string) :: err_msg - integer :: i, j, tw_nlocs,nlocs - integer, dimension(:), allocatable :: tw_indx + integer :: i, j, nlocs real(kind_real), dimension(:), allocatable :: lon, lat - type(datetime), dimension(:), allocatable :: date_time ! gnss ro data 2d location real(kind_real), dimension(:), allocatable :: obsAzim @@ -41,54 +37,26 @@ subroutine ufo_gnssro_2d_locs_init(self, locs, obss, t1, t2) dtheta = self%roconf%dtheta n_horiz = self%roconf%n_horiz - ! Local copies pre binning - nlocs = obsspace_get_nlocs(obss) + nlocs = nlocs_ext / n_horiz - allocate(date_time(nlocs), lon(nlocs), lat(nlocs)) - - call obsspace_get_db(obss, "MetaData", "datetime", date_time) + allocate(lon(nlocs), lat(nlocs)) call obsspace_get_db(obss, "MetaData", "longitude", lon) call obsspace_get_db(obss, "MetaData", "latitude", lat) - ! Generate the timing window indices - allocate(tw_indx(nlocs)) - tw_nlocs = 0 - do i = 1, nlocs - if (date_time(i) > t1 .and. date_time(i) <= t2) then - tw_nlocs = tw_nlocs + 1 - tw_indx(tw_nlocs) = i - endif - enddo - allocate(obsAzim(nlocs)) - if (obsspace_has(obss,"ObsValue","bending_angle")) then - if (obsspace_has(obss, "MetaData", "sensor_azimuth_angle")) then - call obsspace_get_db(obss, "MetaData", "sensor_azimuth_angle", obsAzim) - else - write(err_msg,*) myname, ' error: sensor_azimuth_angle not found' - call abor1_ftn(err_msg) - endif - endif + call obsspace_get_db(obss, "MetaData", "sensor_azimuth_angle", obsAzim) !Setup ufo 2d locations - call ufo_locs_setup(locs, tw_nlocs*n_horiz) - do i = 1, tw_nlocs - locs%lon( (i-1)*n_horiz+1 : i*n_horiz) = lon(tw_indx(i)) - locs%lat( (i-1)*n_horiz+1 : i*n_horiz) = lat(tw_indx(i)) - do j = 1, n_horiz - locs%indx((i-1)*n_horiz+j) = (tw_indx(i)-1)*n_horiz+j - locs%time((i-1)*n_horiz+j) = date_time(tw_indx(i)) - end do + do i = 1, nlocs + lons( (i-1)*n_horiz+1 : i*n_horiz) = lon(i) + lats( (i-1)*n_horiz+1 : i*n_horiz) = lat(i) end do ! save ufo_locs to self - self%obsLat2d = locs%lat - self%obsLon2d = locs%lon + self%obsLat2d = lats + self%obsLon2d = lons - do i = 1, nlocs - call datetime_delete(date_time(i)) - enddo - deallocate(date_time, lon, lat, tw_indx, obsAzim) + deallocate(lon, lat, obsAzim) end subroutine ufo_gnssro_2d_locs_init diff --git a/src/ufo/gnssro/BndROPP2D/ufo_gnssro_bndropp2d_mod.F90 b/src/ufo/gnssro/BndROPP2D/ufo_gnssro_bndropp2d_mod.F90 index 21227ed43..60d139577 100755 --- a/src/ufo/gnssro/BndROPP2D/ufo_gnssro_bndropp2d_mod.F90 +++ b/src/ufo/gnssro/BndROPP2D/ufo_gnssro_bndropp2d_mod.F90 @@ -23,7 +23,6 @@ module ufo_gnssro_bndropp2d_mod use ufo_gnssro_ropp1d_utils_mod use gnssro_mod_conf -use ufo_locs_mod use fckit_log_module, only : fckit_log implicit none diff --git a/src/ufo/gnssro/QC/BackgroundCheckRONBAM.cc b/src/ufo/gnssro/QC/BackgroundCheckRONBAM.cc index 6396f9155..82f871677 100644 --- a/src/ufo/gnssro/QC/BackgroundCheckRONBAM.cc +++ b/src/ufo/gnssro/QC/BackgroundCheckRONBAM.cc @@ -21,7 +21,6 @@ #include "oops/interface/ObsFilter.h" #include "oops/util/Logger.h" -#include "ufo/filters/processWhere.h" #include "ufo/filters/QCflags.h" #include "ufo/GeoVaLs.h" @@ -64,9 +63,9 @@ void BackgroundCheckRONBAM::applyFilter(const std::vector & apply, ioda::ObsDataVector impactparameter(obsdb_, "impact_parameter", "MetaData"); ioda::ObsDataVector latitude(obsdb_, "latitude", "MetaData"); ioda::ObsDataVector earthradius(obsdb_, "earth_radius_of_curvature", - "MetaData"); - ioda::ObsDataVector temperature(obsdb_, "temperature", - "MetaData"); // background temperature at obs location + "MetaData"); + ioda::ObsDataVector temperature(obsdb_, "virtual_temperature", + "MetaData"); // background virtual temperature at obs location Variables varhofx(filtervars, "HofX"); diff --git a/src/ufo/gnssro/QC/CMakeLists.txt b/src/ufo/gnssro/QC/CMakeLists.txt index 096f1d7db..809e7296d 100644 --- a/src/ufo/gnssro/QC/CMakeLists.txt +++ b/src/ufo/gnssro/QC/CMakeLists.txt @@ -9,6 +9,7 @@ set ( qc_src_files ROobserror.interface.F90 ROobserror.interface.h ufo_roobserror_mod.F90 + ufo_roobserror_utils_mod.F90 BackgroundCheckRONBAM.h BackgroundCheckRONBAM.cc actions/ROobserrInflation.cc diff --git a/src/ufo/gnssro/QC/ROobserror.cc b/src/ufo/gnssro/QC/ROobserror.cc index 64dfa33d8..6272ab540 100644 --- a/src/ufo/gnssro/QC/ROobserror.cc +++ b/src/ufo/gnssro/QC/ROobserror.cc @@ -32,6 +32,15 @@ ROobserror::ROobserror(ioda::ObsSpace & obsdb, oops::Log::trace() << "ROobserror contructor = "<< filvar << std::endl; ufo_roobserror_create_f90(key_, obsdb, config, filvar); oops::Log::trace() << "ROobserror contructor key = " << key_ << std::endl; + + // Declare the geovals that are needed by the fortran + allvars_ += Variable("air_temperature@GeoVaLs"); + allvars_ += Variable("geopotential_height@GeoVaLs"); + + // Get the number of horizontal geovals (used by ROPP-2D) + // Default to 1 + n_horiz = config_.getInt("n_horiz", 1); + oops::Log::debug() << "constructor: n_horiz = " << n_horiz << std::endl; } // ----------------------------------------------------------------------------- @@ -47,13 +56,46 @@ void ROobserror::applyFilter(const std::vector & apply, const Variables & filtervars, std::vector> & flagged) const { oops::Log::trace() << "ROobserror using priorFilter" << std::endl; - flags_->save("FortranQC"); // should pass values to fortran properly - obserr_->save("FortranERR"); // should pass values to fortran properly - ufo_roobserror_prior_f90(key_); - flags_->read("FortranQC"); // should get values from fortran properly - obserr_->read("FortranERR"); // should get values from fortran properly + // Check that we have valid data to apply the filter to + if (obsdb_.nlocs() > 0 && data_.getGeoVaLs()->nlocs() > 0) { + // Get the geovals + Eigen::ArrayXXf air_temperature = get_geovals("air_temperature@GeoVaLs"); + Eigen::ArrayXXf geopot_height = get_geovals("geopotential_height@GeoVaLs"); + oops::Log::debug() << "Shape geopotential height " << geopot_height.rows() << + " " << geopot_height.cols() << std::endl; + + // Call the fortran routines to do the processing + flags_->save("FortranQC"); // should pass values to fortran properly + obserr_->save("FortranERR"); // should pass values to fortran properly + ufo_roobserror_prior_f90(key_, + air_temperature.rows(), air_temperature.cols(), air_temperature.data(), + geopot_height.rows(), geopot_height.cols(), geopot_height.data()); + flags_->read("FortranQC"); // should get values from fortran properly + obserr_->read("FortranERR"); // should get values from fortran properly + } } + +Eigen::ArrayXXf ROobserror::get_geovals(const std::string& var_name) const { + // Get the geovals + oops::Log::debug() << "Running get_geovals for " << var_name << std::endl; + // Note that ROPP has more geovals than observation locations, and this converts to + // the correct number (there are n_horiz geovals for every observation) + size_t nlocs = obsdb_.nlocs() * static_cast(n_horiz); + ASSERT(nlocs == data_.getGeoVaLs()->nlocs()); + size_t nlevs = data_.nlevs(Variable(var_name)); + oops::Log::debug() << "nlocs = " << nlocs << " nlevs = " << nlevs << std::endl; + Eigen::ArrayXXf all_geovals(nlocs, nlevs); + std::vector single_geoval(nlocs); + for (int ilev=0; ilev < static_cast(nlevs); ilev++) { + oops::Log::debug() << "Getting data for level " << ilev+1 << std::endl; + data_.getGeoVaLs()->get(single_geoval, Variable(var_name).variable(), ilev+1); + all_geovals.col(ilev) = Eigen::VectorXf::Map(single_geoval.data(), single_geoval.size()); + } + return all_geovals; +} + + // ----------------------------------------------------------------------------- void ROobserror::print(std::ostream & os) const { diff --git a/src/ufo/gnssro/QC/ROobserror.h b/src/ufo/gnssro/QC/ROobserror.h index e6ccc1c48..00016ff3d 100644 --- a/src/ufo/gnssro/QC/ROobserror.h +++ b/src/ufo/gnssro/QC/ROobserror.h @@ -8,6 +8,7 @@ #ifndef UFO_GNSSRO_QC_ROOBSERROR_H_ #define UFO_GNSSRO_QC_ROOBSERROR_H_ +#include #include #include #include @@ -46,6 +47,8 @@ class ROobserror : public FilterBase, void applyFilter(const std::vector &, const Variables &, std::vector> &) const override; int qcFlag() const override {return 76;} + Eigen::ArrayXXf get_geovals(const std::string&) const; + int n_horiz = 1; F90roerr key_; }; diff --git a/src/ufo/gnssro/QC/ROobserror.interface.F90 b/src/ufo/gnssro/QC/ROobserror.interface.F90 index cdf77062c..0c1792648 100644 --- a/src/ufo/gnssro/QC/ROobserror.interface.F90 +++ b/src/ufo/gnssro/QC/ROobserror.interface.F90 @@ -9,6 +9,7 @@ module ufo_roobserror_mod_c use fckit_configuration_module, only: fckit_configuration use iso_c_binding use ufo_roobserror_mod +use kinds use ufo_geovals_mod use ufo_geovals_mod_c, only: ufo_geovals_registry @@ -67,13 +68,40 @@ end subroutine ufo_roobserror_delete_c ! ------------------------------------------------------------------------------ -subroutine ufo_roobserror_prior_c(c_self) bind(c,name='ufo_roobserror_prior_f90') +subroutine ufo_roobserror_prior_c(c_self, air_nobs, air_nlevs, air_temperature, & + height_nobs, height_nlevs, geopotential_height) bind(c,name='ufo_roobserror_prior_f90') + implicit none -integer(c_int), intent(in) :: c_self + +integer(c_int), intent(in) :: c_self ! The object containing configuration info +integer(c_int), intent(in) :: air_nlevs ! The number of vertical levels in air_temperature +integer(c_int), intent(in) :: air_nobs ! The number of observations in air_temperature +real(c_float), intent(in) :: air_temperature(1:air_nobs,1:air_nlevs) + ! The geovals with air temperature +integer(c_int), intent(in) :: height_nlevs ! The number of vertical levels in geopotential_height +integer(c_int), intent(in) :: height_nobs ! The number of observations in geopotential_height +real(c_float), intent(in) :: geopotential_height(1:height_nobs,1:height_nlevs) + ! The geovals with geopotential height + type(ufo_roobserror), pointer :: self +character(len=200) :: ErrorMessage ! Error message to be output call ufo_roobserror_registry%get(c_self, self) -call ufo_roobserror_prior(self) + +if (height_nlevs /= air_nlevs) then + write(ErrorMessage, '(2A,2I7)') "air_temperature and geopotential_height must have the same number of levels", & + " I received: ", air_nlevs, height_nlevs + call abor1_ftn(ErrorMessage) +end if + +if (height_nobs /= air_nobs) then + write(ErrorMessage, '(2A,2I7)') "air_temperature and geopotential_height must have the same number of locations", & + " I received: ", air_nobs, height_nobs + call abor1_ftn(ErrorMessage) +end if + +call ufo_roobserror_prior(self, air_nobs, air_nlevs, air_temperature, & + geopotential_height) end subroutine ufo_roobserror_prior_c diff --git a/src/ufo/gnssro/QC/ROobserror.interface.h b/src/ufo/gnssro/QC/ROobserror.interface.h index 6a59e90aa..491c60fc3 100644 --- a/src/ufo/gnssro/QC/ROobserror.interface.h +++ b/src/ufo/gnssro/QC/ROobserror.interface.h @@ -29,7 +29,8 @@ extern "C" { void ufo_roobserror_create_f90(F90roerr &, const ioda::ObsSpace &, const eckit::Configuration &, const oops::Variables &); void ufo_roobserror_delete_f90(F90roerr &); - void ufo_roobserror_prior_f90(const F90roerr &); + void ufo_roobserror_prior_f90(const F90roerr &, const int &, const int &, const float*, + const int &, const int &, const float*); } // extern C } // namespace ufo diff --git a/src/ufo/gnssro/QC/actions/ROobserrInflation.cc b/src/ufo/gnssro/QC/actions/ROobserrInflation.cc index b9ab096f3..40bff6354 100644 --- a/src/ufo/gnssro/QC/actions/ROobserrInflation.cc +++ b/src/ufo/gnssro/QC/actions/ROobserrInflation.cc @@ -10,7 +10,6 @@ #include #include "ioda/ObsDataVector.h" -#include "oops/base/Variables.h" #include "oops/util/IntSetParser.h" #include "oops/util/missingValues.h" #include "ufo/filters/ObsFilterData.h" @@ -24,16 +23,17 @@ static FilterActionMaker makerInflateErr_("RONBAMErrInflate") // ----------------------------------------------------------------------------- -ROobserrInflation::ROobserrInflation(const eckit::Configuration & conf) +ROobserrInflation::ROobserrInflation(const Parameters_ &) : allvars_() { } // ----------------------------------------------------------------------------- void ROobserrInflation::apply(const Variables & vars, - const std::vector> & flagged, - const ObsFilterData & data, - ioda::ObsDataVector & flags, - ioda::ObsDataVector & obserr) const { + const std::vector> & flagged, + const ObsFilterData & data, + int filterQCflag, + ioda::ObsDataVector & flags, + ioda::ObsDataVector & obserr) const { const float missing = util::missingValue(missing); oops::Log::debug() << " input obserr: " << obserr << std::endl; size_t nlocs = data.nlocs(); diff --git a/src/ufo/gnssro/QC/actions/ROobserrInflation.h b/src/ufo/gnssro/QC/actions/ROobserrInflation.h index e61670b9a..6ad011636 100644 --- a/src/ufo/gnssro/QC/actions/ROobserrInflation.h +++ b/src/ufo/gnssro/QC/actions/ROobserrInflation.h @@ -11,8 +11,8 @@ #include #include -#include "ioda/ObsDataVector.h" #include "ufo/filters/actions/FilterActionBase.h" +#include "ufo/filters/Variables.h" namespace ufo { @@ -20,19 +20,29 @@ class ObsFilterData; // ----------------------------------------------------------------------------- +class ROobserrInflationParameters : public FilterActionParametersBase { + OOPS_CONCRETE_PARAMETERS(ROobserrInflationParameters, FilterActionParametersBase); + + // No extra parameters needed +}; + +// ----------------------------------------------------------------------------- + class ROobserrInflation : public FilterActionBase { public: - explicit ROobserrInflation(const eckit::Configuration &); + /// The type of parameters accepted by the constructor of this action. + /// This typedef is used by the FilterActionFactory. + typedef ROobserrInflationParameters Parameters_; + + explicit ROobserrInflation(const Parameters_ &); ~ROobserrInflation() {} void apply(const Variables &, const std::vector> &, - const ObsFilterData &, + const ObsFilterData &, int, ioda::ObsDataVector &, ioda::ObsDataVector &) const override; const ufo::Variables & requiredVariables() const override {return allvars_;} private: Variables allvars_; - const std::string strfactor_; - const eckit::LocalConfiguration conf_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/gnssro/QC/ufo_roobserror_mod.F90 b/src/ufo/gnssro/QC/ufo_roobserror_mod.F90 index db20e5881..09084a8f3 100644 --- a/src/ufo/gnssro/QC/ufo_roobserror_mod.F90 +++ b/src/ufo/gnssro/QC/ufo_roobserror_mod.F90 @@ -25,8 +25,11 @@ module ufo_roobserror_mod type :: ufo_roobserror character(len=max_string) :: variable character(len=max_string) :: errmodel + character(len=max_string) :: rmatrix_filename + character(len=max_string) :: err_variable type(oops_variables), public :: obsvar type(c_ptr) :: obsdb + integer :: n_horiz ! For ROPP-2D multiplier of the number of geovals end type ufo_roobserror ! ------------------------------------------------------------------------------ @@ -47,6 +50,25 @@ subroutine ufo_roobserror_create(self, obspace, f_conf) call f_conf%get_or_die("errmodel",str) self%errmodel = str end if + +self % rmatrix_filename = "" +if (f_conf % has("rmatrix_filename")) then + call f_conf % get_or_die("rmatrix_filename", str) + self % rmatrix_filename = str +end if + +self % err_variable = "" +if (f_conf % has("err_variable")) then + call f_conf % get_or_die("err_variable", str) + self % err_variable = str +end if + +! Get the number of extra geovals as a multiplier, default to 1 +self % n_horiz = 1 +if (f_conf % has("n_horiz")) then + call f_conf % get_or_die("n_horiz", self % n_horiz) +end if + self%obsdb = obspace end subroutine ufo_roobserror_create @@ -60,19 +82,35 @@ end subroutine ufo_roobserror_delete ! ------------------------------------------------------------------------------ -subroutine ufo_roobserror_prior(self) +subroutine ufo_roobserror_prior(self, model_nobs, model_nlevs, air_temperature, & + geopotential_height) + use fckit_log_module, only : fckit_log +use ufo_utils_mod, only: cmp_strings + implicit none + type(ufo_roobserror), intent(in) :: self -integer :: nobs -real(kind_real), allocatable :: obsZ(:), obsLat(:) -real(kind_real), allocatable :: obsImpH(:),obsImpP(:),obsGeoid(:),obsLocR(:) -real(kind_real), allocatable :: obsValue(:) -real(kind_real), allocatable :: obsErr(:) -integer(c_int), allocatable :: obsSaid(:) -integer(c_int), allocatable :: QCflags(:) -real(kind_real) :: missing -character(max_string) :: err_msg +integer, intent(in) :: model_nobs +integer, intent(in) :: model_nlevs +real, intent(in) :: air_temperature(:,:) +real, intent(in) :: geopotential_height(:,:) + +integer :: nobs +real(kind_real), allocatable :: obsZ(:), obsLat(:) +integer, allocatable :: obsSatid(:) ! Satellite identifier +integer, allocatable :: obsOrigC(:) ! Originating centre number for each ob +real(kind_real), allocatable :: obsImpA(:) ! The observation impact alitude +real(kind_real), allocatable :: obsImpH(:) ! The observation impact height +real(kind_real), allocatable :: obsImpP(:) ! The observation impact parameter +real(kind_real), allocatable :: obsGeoid(:) ! The geoid undulation at the observation location +real(kind_real), allocatable :: obsLocR(:) ! The local radius of curvature at the observation location +real(kind_real), allocatable :: obsValue(:) +real(kind_real), allocatable :: obsErr(:) +integer(c_int), allocatable :: obsSaid(:) +integer(c_int), allocatable :: QCflags(:) +real(kind_real) :: missing +character(max_string) :: err_msg missing = missing_value(missing) nobs = obsspace_get_nlocs(self%obsdb) @@ -80,6 +118,11 @@ subroutine ufo_roobserror_prior(self) allocate(obsErr(nobs)) QCflags(:) = 0 +if (model_nobs /= nobs * self % n_horiz) then + write(err_msg, '(A,2I8)') 'nobs from model and observations must be equal', nobs, model_nobs + call abor1_ftn(err_msg) +end if + ! read QC flags call obsspace_get_db(self%obsdb, "FortranQC", trim(self%variable),QCflags ) @@ -93,10 +136,12 @@ subroutine ufo_roobserror_prior(self) allocate(obsGeoid(nobs)) allocate(obsLocR(nobs)) allocate(obsImpH(nobs)) + allocate(obsImpA(nobs)) call obsspace_get_db(self%obsdb, "MetaData", "impact_parameter", obsImpP) call obsspace_get_db(self%obsdb, "MetaData", "geoid_height_above_reference_ellipsoid",obsGeoid) call obsspace_get_db(self%obsdb, "MetaData", "earth_radius_of_curvature", obsLocR) - obsImpH(:) = obsImpP(:) - obsGeoid(:) - obsLocR(:) + obsImpH(:) = obsImpP(:) - obsLocR(:) + obsImpA(:) = obsImpP(:) - obsGeoid(:) - obsLocR(:) select case (trim(self%errmodel)) case ("NBAM") @@ -104,7 +149,7 @@ subroutine ufo_roobserror_prior(self) allocate(obsLat(nobs)) call obsspace_get_db(self%obsdb, "MetaData", "occulting_sat_id", obsSaid) call obsspace_get_db(self%obsdb, "MetaData", "latitude", obsLat) - call bending_angle_obserr_NBAM(obsLat, obsImpH, obsSaid, nobs, obsErr, QCflags, missing) + call bending_angle_obserr_NBAM(obsLat, obsImpA, obsSaid, nobs, obsErr, QCflags, missing) write(err_msg,*) "ufo_roobserror_mod: setting up bending_angle obs error with NBAM method" call fckit_log%info(err_msg) deallocate(obsSaid) @@ -115,7 +160,7 @@ subroutine ufo_roobserror_prior(self) case ("ECMWF") allocate(obsValue(nobs)) call obsspace_get_db(self%obsdb, "ObsValue", "bending_angle", obsValue) - call bending_angle_obserr_ECMWF(obsImpH, obsValue, nobs, obsErr, QCflags, missing) + call bending_angle_obserr_ECMWF(obsImpA, obsValue, nobs, obsErr, QCflags, missing) write(err_msg,*) "ufo_roobserror_mod: setting up bending_angle obs error with ECMWF method" call fckit_log%info(err_msg) deallocate(obsValue) @@ -126,7 +171,7 @@ subroutine ufo_roobserror_prior(self) allocate(obsLat(nobs)) call obsspace_get_db(self%obsdb, "ObsValue", "bending_angle", obsValue) call obsspace_get_db(self%obsdb, "MetaData", "latitude", obsLat) - call bending_angle_obserr_NRL(obsLat, obsImpH, obsValue, nobs, obsErr, QCflags, missing) + call bending_angle_obserr_NRL(obsLat, obsImpA, obsValue, nobs, obsErr, QCflags, missing) write(err_msg,*) "ufo_roobserror_mod: setting up bending_angle obs error with NRL method" call fckit_log%info(err_msg) deallocate(obsValue) @@ -134,15 +179,50 @@ subroutine ufo_roobserror_prior(self) ! update obs error call obsspace_put_db(self%obsdb, "FortranERR", trim(self%variable), obsErr) + case ("MetOffice") + write(err_msg,*) "ufo_roobserror_mod: setting up bending_angle obs error with the Met Office method" + call fckit_log%info(err_msg) + if (cmp_strings(self % rmatrix_filename, "")) then + err_msg = "If you choose the Met Office method, then you must specify rmatrix_filename" + call abor1_ftn(err_msg) + end if + allocate(obsSatid(nobs)) + allocate(obsOrigC(nobs)) + allocate(obsValue(nobs)) + call obsspace_get_db(self%obsdb, "MetaData", "occulting_sat_id", obsSatid) + call obsspace_get_db(self%obsdb, "MetaData", "originating_center", obsOrigC) + call obsspace_get_db(self%obsdb, "ObsValue", "bending_angle", obsValue) + call obsspace_get_db(self%obsdb, "ObsError", trim(self%variable), obsErr) + if (self % err_variable == "latitude") then + allocate(obsLat(nobs)) + call obsspace_get_db(self%obsdb, "MetaData", "latitude", obsLat) + call gnssro_obserr_latitude(nobs, self % rmatrix_filename, obsSatid, obsOrigC, obsLat, obsImpH, & + obsValue, obsErr, QCflags, missing) + deallocate(obsLat) + else if (self % err_variable == "average_temperature") then + call gnssro_obserr_avtemp(nobs, self % n_horiz, self % rmatrix_filename, obsSatid, obsOrigC, & + model_nlevs, air_temperature, geopotential_height, obsImpH, obsValue, & + obsErr, QCflags, missing) + else + err_msg = "The error variable should be either 'latitude' or 'average_temperature', but you gave " // & + trim(self % err_variable) + call fckit_log%info(err_msg) + end if + deallocate(obsValue) + deallocate(obsSatid) + deallocate(obsOrigC) + ! up date obs error + call obsspace_put_db(self%obsdb, "FortranERR", trim(self%variable), obsErr) + case default - write(err_msg,*) "ufo_roobserror_mod: bending_angle error model must be NBAM, ECMWF, or NRL" - call fckit_log%info(err_msg) + write(err_msg,*) "ufo_roobserror_mod: bending_angle error model must be NBAM, ECMWF, NRL or MetOffice" call fckit_log%info(err_msg) end select deallocate(obsImpP) deallocate(obsGeoid) deallocate(obsLocR) deallocate(obsImpH) + deallocate(obsImpA) !------------------------------- case ("refractivity") diff --git a/src/ufo/gnssro/QC/ufo_roobserror_utils_mod.F90 b/src/ufo/gnssro/QC/ufo_roobserror_utils_mod.F90 new file mode 100644 index 000000000..9617c5023 --- /dev/null +++ b/src/ufo/gnssro/QC/ufo_roobserror_utils_mod.F90 @@ -0,0 +1,486 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +!> Fortran module for utility routines used in calculating the observation +!> uncertainties, using the Met Office calculations + +module ufo_roobserror_utils_mod + +use kinds +use fckit_log_module, only: fckit_log +use ufo_utils_mod, only: ufo_utils_iogetfreeunit +use missing_values_mod + +implicit none + +public :: ufo_roobserror_getrmatrix + +type Rmatrix_type + INTEGER :: satid = 0 ! Sat ID for each R matrix + INTEGER :: origctr = 0 ! Originating centre for each R matrix + REAL :: av_temp ! Average temperature of the troposphere (if used) + INTEGER :: max_height ! The maximum height used in calculating the average temperature + REAL :: latitude ! The latitude of this R matrix (if used) + INTEGER :: num_heights ! Number of heights in the R matrix specification (i.e. the size of the matrix) + REAL :: clen ! Correlation length-scale for the R matrix + REAL :: min_error ! Minimum value of the observation error (in radians) + REAL, POINTER :: height(:) ! The height for each specified observation error + REAL, POINTER :: frac_err(:) ! The fractional error at this height (obs error / background measurement) +end type + +contains + +!------------------------------------------------------------------------------- +! Read data needed to set up the Rmatrix. +!------------------------------------------------------------------------------- + +SUBROUTINE ufo_roobserror_getrmatrix(max_mats, & ! The maximum number of matrices + filename, & ! The name of the file to be read in + Rmatrix, & ! The R matrices read from the files + R_num_mats) ! Number of R matrices read in + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: max_mats ! The maximum number of matrices possible +CHARACTER(LEN=*), INTENT(IN) :: filename ! The name of the file to be read in +TYPE (Rmatrix_type), ALLOCATABLE, INTENT(OUT) :: Rmatrix(:) ! The R matrices read from the files +INTEGER, INTENT(OUT) :: R_num_mats ! Number of sats/processing R matrices in these files + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_roobserror_getrmatrix" +TYPE (Rmatrix_type) :: temp_rmat(max_mats) ! Temporary store of the matrices read in +INTEGER :: i ! Loop variable when copying matrices +INTEGER :: fileunit ! The unit number of the input file +CHARACTER(len=200) :: ErrorMessage ! The error message to be output +INTEGER :: return_code ! Return code from routines called + +!----------------------------------------------- +! 0. Open the R-matrix file +!----------------------------------------------- + +R_num_mats = 0 + +fileunit = ufo_utils_iogetfreeunit() +OPEN(UNIT=fileunit, FILE=filename, ACTION='READ', STATUS='OLD', IOSTAT=return_code) +if (return_code /= 0) then + WRITE(ErrorMessage, '(3A,I0)') "Error opening ", TRIM(filename), & + ", return code = ", return_code + call abor1_ftn(ErrorMessage) +end if + +!----------------------------------------------- +! 1. Read all the namelists that are in the file - read these into a temporary +! array to store the data +!----------------------------------------------- + +DO + IF (R_num_mats >= max_mats) THEN + WRITE (ErrorMessage, '(A,I0,A,I0)') & + "Trying to read too many R-matrices ", & + R_num_mats, " out of ", max_mats + CALL abor1_ftn(ErrorMessage) + END IF + + CALL ufo_roobserror_read_rmatrix (fileunit, & + temp_rmat(R_num_mats + 1), & + return_code) + IF (return_code < 0) THEN + ! End of file reached + EXIT + ELSE IF (return_code > 0) THEN + WRITE(ErrorMessage, '(A,I0)') & + "Trouble reading R-matrix namelist, error number ", & + return_code + CALL abor1_ftn(ErrorMessage) + ELSE + R_num_mats = R_num_mats + 1 + END IF +END DO + +!----------------------------------------------- +! 2. Copy the R-matrices from the temporary array to the output variable +!----------------------------------------------- + +ALLOCATE (RMatrix(R_num_mats)) +DO i = 1, R_num_mats + CALL ufo_roobserror_copy_rmatrix (temp_rmat(i), & + RMatrix(i)) +END DO + +CLOSE(fileunit) + +!----------------------------------------------- +! 3. Check that all the matrices have the same max_height value +!----------------------------------------------- + +DO i = 2, R_num_mats + IF (RMatrix(i) % max_height /= RMatrix(1) % max_height) THEN + WRITE (ErrorMessage, '(A,I0,A,I0,A,I0)') & + "All the R-matrices should have the same value for max_height Rmatrix(", & + i, ") % max_height = ", RMatrix(i) % max_height, & + ", RMatrix(1) % max_height = ", RMatrix(1) % max_height + CALL abor1_ftn(ErrorMessage) + END IF +END DO + +END SUBROUTINE ufo_roobserror_getrmatrix + + +!------------------------------------------------------------------------------- +! (C) Crown copyright Met Office. All rights reserved. +! Refer to COPYRIGHT.txt of this distribution for details. +!------------------------------------------------------------------------------- +! Read data needed to set up the Rmatrix. +!------------------------------------------------------------------------------- + +SUBROUTINE ufo_roobserror_read_rmatrix (fileunit, & ! The maximum number of matrices + Rmatrix, & ! The R matrices read from the files + return_code) ! Number of R matrices read in + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: fileunit ! The unit number of the open file +TYPE (Rmatrix_type), INTENT(OUT) :: Rmatrix ! The R matrix read from the file +INTEGER, INTENT(OUT) :: return_code ! Return code from this routine + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_roobserror_read_rmatrix" +CHARACTER(len=200) :: ErrorMessage +REAL :: av_temp ! Average troposphere temperature +INTEGER :: max_height ! The maximum height for calculating the average temperature +REAL :: latitude ! Latitude +REAL :: heights(600) ! Heights of the observation errors +REAL :: obs_errors(600) ! Observation errors (on vertical levels) +INTEGER :: satid ! Satellite identifier +INTEGER :: origc ! Originating centre for the RO data +REAL :: clen ! Correlation length-scale for the R-matrix +REAL :: min_error ! Minimum observation error + +NAMELIST / GPSRO_ob_error / & + av_temp, & + heights, & + obs_errors, & + satid, & + origc, & + clen, & + min_error, & + latitude, & + max_height + +! Initialise values to blank / sensible values + +satid = missing_value(satid) +origc = missing_value(origc) +min_error = 0 +heights = missing_value(heights(1)) +obs_errors = missing_value(obs_errors(1)) +clen = 1.0E10 +av_temp = missing_value(av_temp) +latitude = missing_value(latitude) +max_height = 0 + +! Read the namelist, and decode this into the R-matrix structure + +READ (fileunit, NML = GPSRO_ob_error, IOSTAT = return_code) +IF (return_code == 0) THEN + + RMatrix % num_heights = COUNT (heights /= missing_value(heights(1))) + IF (COUNT(obs_errors /= missing_value(obs_errors(1))) /= RMatrix % num_heights) THEN + WRITE (ErrorMessage, '(A,I0,1X,I0)') "Counts do not match ", & + COUNT(obs_errors /= missing_value(obs_errors(1))), RMatrix % num_heights + CALL abor1_ftn(ErrorMessage) + ELSE + ALLOCATE (RMatrix % height(1:RMatrix % num_heights)) + ALLOCATE (RMatrix % frac_err(1:RMatrix % num_heights)) + RMatrix % height(:) = PACK (heights, heights /= missing_value(heights(1))) + RMatrix % frac_err(:) = PACK (obs_errors, obs_errors /= missing_value(obs_errors(1))) + RMatrix % frac_err(:) = RMatrix % frac_err(:) + END IF + + RMatrix % av_temp = av_temp + RMatrix % max_height = max_height + RMatrix % latitude = latitude + RMatrix % satid = satid + RMatrix % origctr = origc + RMatrix % clen = clen + RMatrix % min_error = min_error +END IF + +END SUBROUTINE ufo_roobserror_read_rmatrix + + +!------------------------------------------------------------------------------- +! (C) Crown copyright Met Office. All rights reserved. +! Refer to COPYRIGHT.txt of this distribution for details. +!------------------------------------------------------------------------------- +! Choose the R matrix to apply for this observation. The matrix will be +! be selected to have the same satellite identifier and originating centre as +! requested. The matrices will be interpolated from those with the nearest +! average temperature value. +!------------------------------------------------------------------------------- + +SUBROUTINE ufo_roobserror_interpolate_rmatrix (satid, & + origc, & + av_temp, & + R_num_sats, & + Rmatrix_list, & + out_matrix) + + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: satid ! The satellite identifier | These three entries will +INTEGER, INTENT(IN) :: origc ! The originating centre | be used to find the +REAL, INTENT(IN) :: av_temp ! The average temperature | R matrix from the list +INTEGER, INTENT(IN) :: R_num_sats ! The number of matrices in the list +TYPE (Rmatrix_type), INTENT(IN) :: RMatrix_list(R_num_sats) ! The list of R matrices to select from +TYPE (Rmatrix_type), INTENT(OUT) :: out_matrix ! Output R matrix + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_roobserror_interpolate_rmatrix" +INTEGER :: i ! Loop variable +INTEGER :: lower_match ! The largest av_temp which is smaller than the one being searched for +INTEGER :: upper_match ! The smallest av_temp which is larger than the one being searched for +REAL :: weight ! Weight to give to the upper matched av_temp +CHARACTER(len=200) :: ErrorMessage ! Error message to be output +REAL, ALLOCATABLE :: interp_errors(:) ! Observation errors, interpolated to the required av_temp + +lower_match = 0 +upper_match = 0 +DO i = 1, R_num_sats + IF (satid == RMatrix_list(i) % satid .AND. & + origc == RMatrix_list(i) % origctr) THEN + IF (RMatrix_list(i) % av_temp /= missing_value(RMatrix_list(i) % av_temp)) THEN + IF (RMatrix_list(i) % av_temp > av_temp) THEN + IF (upper_match == 0) THEN + upper_match = i + ELSE IF (RMatrix_list(i) % av_temp < RMatrix_list(upper_match) % av_temp) THEN + upper_match = i + END IF + ELSE + IF (lower_match == 0) THEN + lower_match = i + ELSE IF (RMatrix_list(i) % av_temp > RMatrix_list(lower_match) % av_temp) THEN + lower_match = i + END IF + END IF + END IF + END IF +END DO + +IF (upper_match > 0) THEN + IF (lower_match > 0) THEN + WRITE (ErrorMessage, '(A,3F10.2)') "Interpolating between locations.. ", & + RMatrix_list(lower_match) % av_temp, & + av_temp, RMatrix_list(upper_match) % av_temp + call fckit_log % debug(ErrorMessage) + weight = (av_temp - RMatrix_list(lower_match) % av_temp) / & + (RMatrix_list(upper_match) % av_temp - RMatrix_list(lower_match) % av_temp) + + out_matrix % num_heights = RMatrix_list(lower_match) % num_heights + ALLOCATE (out_matrix % height(1:out_matrix % num_heights)) + ALLOCATE (out_matrix % frac_err(1:out_matrix % num_heights)) + ALLOCATE (interp_errors(1:out_matrix % num_heights)) + CALL ufo_roobserror_interpolate_heights(RMatrix_list(lower_match) % num_heights, & + RMatrix_list(lower_match) % height, & + RMatrix_list(upper_match) % num_heights, & + RMatrix_list(upper_match) % height, & + RMatrix_list(upper_match) % frac_err, & + interp_errors) + out_matrix % height(:) = RMatrix_list(lower_match) % height(:) + out_matrix % frac_err(:) = weight * interp_errors + & + (1 - weight) * RMatrix_list(lower_match) % frac_err + + out_matrix % av_temp = av_temp + + ! max_height should be the same for every matrix, so do not interpolate + out_matrix % max_height = RMatrix_list(lower_match) % max_height + out_matrix % satid = satid + out_matrix % origctr = origc + out_matrix % clen = weight * RMatrix_list(upper_match) % clen + & + (1 - weight) * RMatrix_list(lower_match) % clen + out_matrix % min_error = weight * RMatrix_list(upper_match) % min_error + & + (1 - weight) * RMatrix_list(lower_match) % min_error + out_matrix % latitude = missing_value(out_matrix % latitude) + ELSE + CALL ufo_roobserror_copy_rmatrix(RMatrix_list(upper_match), & + out_matrix) + END IF +ELSE + IF (lower_match > 0) THEN + CALL ufo_roobserror_copy_rmatrix(RMatrix_list(lower_match), & + out_matrix) + ELSE + WRITE (ErrorMessage, '(A,I0,1X,I0,1X)') "Did not match any matrices ", satid, origc + CALL fckit_log % warning(ErrorMessage) + CALL ufo_roobserror_copy_rmatrix(RMatrix_list(1), & + out_matrix) + END IF +END IF + +END SUBROUTINE ufo_roobserror_interpolate_rmatrix + + +!------------------------------------------------------------------------------- +! (C) Crown copyright Met Office. All rights reserved. +! Refer to COPYRIGHT.txt of this distribution for details. +!------------------------------------------------------------------------------- +! Interpolate the R-matrix values between the source and target heights +!------------------------------------------------------------------------------- + +SUBROUTINE ufo_roobserror_interpolate_heights(target_nheights, & + target_heights, & + source_nheights, & + source_heights, & + source_values, & + output_values) + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: target_nheights +REAL, INTENT(IN) :: target_heights(1:target_nheights) +INTEGER, INTENT(IN) :: source_nheights +REAL, INTENT(IN) :: source_heights(1:source_nheights) +REAL, INTENT(IN) :: source_values(1:source_nheights) +REAL, INTENT(OUT) :: output_values(1:target_nheights) + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_roobserror_interpolate_heights" +INTEGER :: i +INTEGER :: j +LOGICAL :: MatchFound +REAL :: weight ! Weight to give to the upper level value + +DO i = 1, target_nheights + IF (ALL (target_heights(i) < source_heights)) THEN + ! The target height is less than all in the source, so take the bottom value + output_values(i) = source_values(1) + ELSE + ! Search for a match, where the target height is between two adjacent values + MatchFound = .FALSE. + DO j = 1, source_nheights - 1 + IF (target_heights(i) >= source_heights(j) .AND. & + target_heights(i) < source_heights(j + 1)) THEN + MatchFound = .TRUE. + EXIT + END IF + END DO + IF (MatchFound) THEN + ! If we have found a match, then interpolate between the heights in + ! question + weight = (target_heights(i) - source_heights(j)) / & + (source_heights(j + 1) - source_heights(j)) + output_values(i) = weight * source_values(j + 1) + & + (1 - weight) * source_values(j) + ELSE + ! If no match has been found, then the target is greater than all the + ! source heights + output_values(i) = source_values(source_nheights) + END IF + END IF +END DO + +END SUBROUTINE ufo_roobserror_interpolate_heights + + +!------------------------------------------------------------------------------- +! (C) Crown copyright Met Office. All rights reserved. +! Refer to COPYRIGHT.txt of this distribution for details. +!------------------------------------------------------------------------------- +! Read data needed to set up the Rmatrix. +!------------------------------------------------------------------------------- + +SUBROUTINE ufo_roobserror_copy_rmatrix(in_mat, & ! The RMatrix to copy + out_mat) ! The target for the copy + +IMPLICIT NONE + +! Subroutine arguments: +TYPE (Rmatrix_type), INTENT(IN) :: in_mat ! The R matrix to copy +TYPE (Rmatrix_type), INTENT(OUT) :: out_mat ! The target for the copy + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_roobserror_copy_rmatrix" + +out_mat % num_heights = in_mat % num_heights +ALLOCATE (out_mat % height(out_mat % num_heights)) +ALLOCATE (out_mat % frac_err(out_mat % num_heights)) +out_mat % height(:) = in_mat % height(:) +out_mat % frac_err(:) = in_mat % frac_err(:) + +out_mat % av_temp = in_mat % av_temp +out_mat % max_height = in_mat % max_height +out_mat % latitude = in_mat % latitude +out_mat % satid = in_mat % satid +out_mat % origctr = in_mat % origctr +out_mat % clen = in_mat % clen +out_mat % min_error = in_mat % min_error + +END SUBROUTINE ufo_roobserror_copy_rmatrix + +!------------------------------------------------------------------------------- +! Choose the R matrix to apply for this observation. The matrix will be +! be selected to have the same satellite identifier and originating centre as +! requested. The matrix with the nearest latitude to that specified will be +! selected (without interpolation). +!------------------------------------------------------------------------------- + +SUBROUTINE ufo_roobserror_findnearest_rmatrix(satid, & + origc, & + latitude, & + R_num_sats, & + Rmatrix_list, & + out_matrix) + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: satid ! The satellite identifier | These three entries will +INTEGER, INTENT(IN) :: origc ! The originating centre | be used to find the +REAL(kind_real), INTENT(IN) :: latitude ! The latitude | R matrix from the list +INTEGER, INTENT(IN) :: R_num_sats ! The number of matrices in the list +TYPE (Rmatrix_type), INTENT(IN) :: RMatrix_list(1:R_num_sats) ! The list of R matrices to select from +TYPE (Rmatrix_type), INTENT(OUT) :: out_matrix ! Output R matrix + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_roobserror_findnearest_rmatrix" +INTEGER :: i ! Loop variable +INTEGER :: closest_match ! The R matrix whose latitude is closest to the specification +CHARACTER(len=200) :: ErrorMessage ! Error message to be output + +closest_match = -1 +DO i = 1, R_num_sats + IF (satid == RMatrix_list(i) % satid .AND. & + origc == RMatrix_list(i) % origctr) THEN + IF (RMatrix_list(i) % latitude /= missing_value(RMatrix_list(i) % latitude)) THEN + IF (closest_match < 1) THEN + closest_match = i + ELSE IF (ABS (latitude - RMatrix_list(i) % latitude) < & + ABS (latitude - RMatrix_list(closest_match) % latitude)) THEN + closest_match = i + END IF + END IF + END IF +END DO + +IF (closest_match > 0) THEN + CALL ufo_roobserror_copy_rmatrix(RMatrix_list(closest_match), & + out_matrix) +ELSE + WRITE (ErrorMessage, '(A,I0,1X,I0)') "Did not match any matrices ", satid, origc + CALL fckit_log % warning(ErrorMessage) + CALL ufo_roobserror_copy_rmatrix(RMatrix_list(1), & + out_matrix) +END IF + +END SUBROUTINE ufo_roobserror_findnearest_rmatrix + +end module ufo_roobserror_utils_mod diff --git a/src/ufo/gnssro/Ref/ObsGnssroRefTLAD.cc b/src/ufo/gnssro/Ref/ObsGnssroRefTLAD.cc index ce24ec31d..8196e6166 100644 --- a/src/ufo/gnssro/Ref/ObsGnssroRefTLAD.cc +++ b/src/ufo/gnssro/Ref/ObsGnssroRefTLAD.cc @@ -27,7 +27,7 @@ static LinearObsOperatorMaker makerGnssroRefTL_("GnssroRef"); // ----------------------------------------------------------------------------- ObsGnssroRefTLAD::ObsGnssroRefTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOperGnssroRef_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOperGnssroRef_(0), varin_() { const eckit::LocalConfiguration obsOptions(config, "obs options"); @@ -50,20 +50,20 @@ ObsGnssroRefTLAD::~ObsGnssroRefTLAD() { void ObsGnssroRefTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_gnssro_ref_tlad_settraj_f90(keyOperGnssroRef_, geovals.toFortran(), odb_); + ufo_gnssro_ref_tlad_settraj_f90(keyOperGnssroRef_, geovals.toFortran(), obsspace()); } // ----------------------------------------------------------------------------- void ObsGnssroRefTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_gnssro_ref_simobs_tl_f90(keyOperGnssroRef_, geovals.toFortran(), odb_, + ufo_gnssro_ref_simobs_tl_f90(keyOperGnssroRef_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); } // ----------------------------------------------------------------------------- void ObsGnssroRefTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_gnssro_ref_simobs_ad_f90(keyOperGnssroRef_, geovals.toFortran(), odb_, + ufo_gnssro_ref_simobs_ad_f90(keyOperGnssroRef_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); } diff --git a/src/ufo/gnssro/Ref/ObsGnssroRefTLAD.h b/src/ufo/gnssro/Ref/ObsGnssroRefTLAD.h index ea964c9f5..ce03eccce 100644 --- a/src/ufo/gnssro/Ref/ObsGnssroRefTLAD.h +++ b/src/ufo/gnssro/Ref/ObsGnssroRefTLAD.h @@ -56,7 +56,6 @@ class ObsGnssroRefTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOperGnssroRef_; - const ioda::ObsSpace& odb_; std::unique_ptr varin_; }; diff --git a/src/ufo/gnssro/utils/gnssro_mod_conf.F90 b/src/ufo/gnssro/utils/gnssro_mod_conf.F90 index c98c8f734..9980fa57b 100644 --- a/src/ufo/gnssro/utils/gnssro_mod_conf.F90 +++ b/src/ufo/gnssro/utils/gnssro_mod_conf.F90 @@ -22,6 +22,7 @@ module gnssro_mod_conf real(kind_real) :: top_2d real(kind_real) :: dtheta character(len=20) :: vertlayer + character(len=20) :: output_diags end type gnssro_conf !--------- ropp2d location default parameters----------------- @@ -62,7 +63,11 @@ subroutine gnssro_conf_setup(roconf, f_conf) endif roconf%sr_steps = 2 if (f_conf%has("sr_steps")) call f_conf%get_or_die("sr_steps",roconf%sr_steps) - +roconf%output_diags="false" +if (f_conf%has("output_diags")) then + call f_conf%get_or_die("output_diags",str) + roconf%output_diags=trim(str) +endif end subroutine gnssro_conf_setup diff --git a/src/ufo/gnssro/utils/gnssro_mod_obserror.F90 b/src/ufo/gnssro/utils/gnssro_mod_obserror.F90 index eaea0de5d..4798d2f03 100644 --- a/src/ufo/gnssro/utils/gnssro_mod_obserror.F90 +++ b/src/ufo/gnssro/utils/gnssro_mod_obserror.F90 @@ -4,6 +4,8 @@ module gnssro_mod_obserror use kinds use gnssro_mod_constants +use ufo_roobserror_utils_mod +use fckit_log_module, only: fckit_log contains subroutine bending_angle_obserr_ECMWF(obsImpH, obsValue, nobs, obsErr, QCflags, missing) @@ -164,5 +166,212 @@ subroutine refractivity_obserr_NBAM(obsLat, obsZ, nobs, obsErr, QCflags,missing) end subroutine refractivity_obserr_NBAM + +subroutine gnssro_obserr_avtemp(nobs, n_horiz, rmatrix_filename, obsSatid, obsOrigC, nlevs, & + air_temperature, geopotential_height, obsZ, obsValue, obsErr, & + QCflags, missing) + +implicit none + +! Subroutine arguments +integer, intent(in) :: nobs ! Number of observations +integer, intent(in) :: n_horiz ! Number of geovals per observation +character(len=*), intent(in) :: rmatrix_filename ! Name of the R-matrix file +integer, intent(in) :: obsSatid(:) ! Satellite identifier +integer, intent(in) :: obsOrigC(:) ! Originating centre number +integer, intent(in) :: nlevs ! Number of model levels +real, intent(in) :: air_temperature(:,:) ! Temperature of the model background +real, intent(in) :: geopotential_height(:,:) ! Geopotential height of the model levels +real(kind_real), intent(in) :: obsZ(:) ! Height of the observation +real(kind_real), intent(in) :: obsValue(:) ! The observed value +real(kind_real), intent(out) :: obsErr(:) ! The calculated observation error (uncertainty) +integer(c_int), intent(in) :: QCflags(:) ! Quality control flags for the observations +real(kind_real), intent(in) :: missing ! Missing value indicator + +! Local parameters +integer, parameter :: Rmax_num = 1000 ! Max number of R matrices to be read in + +! Local variables +type(rmatrix_type), allocatable :: Rmatrix_list(:) ! List of all the R matrices to use +type(rmatrix_type) :: Rmatrix ! The chosen R matrix +real(kind_real) :: frac_err ! Fractional observation error +real :: av_temp +integer :: npoints +integer :: ilev +integer :: R_num_sats ! Actual number of R-matrices read in +character(len=200) :: Message ! Message to be output +integer :: iob ! Loop variable, observation number +integer :: igeoval ! Loop variable, geoval number +integer :: iheight ! Loop variable, height in profile + +! Read in R matrix data +CALL ufo_roobserror_getrmatrix(Rmax_num, & ! Max number of R matrices to read in + rmatrix_filename, & ! The name of the file to be read in + Rmatrix_list, & ! List of all R matrices to use + R_num_sats) ! Number of R matrices read in +!-------------------------------------------------------- +! Choose the R-matrix values. We use different code if we are passed +! a matrix which depends on latitude or average temperature +!-------------------------------------------------------- + +do iob = 1, nobs + if (QCflags(iob) .eq. 0) then + + IF (RMatrix_list(1) % av_temp > 0) THEN + !-------------------------------------------------------- + ! Choose R matrix depending on satid, origctr and the average + ! background temperature between the surface and 20km + !-------------------------------------------------------- + + ! Calculate the average troposphere temperature for this profile + + av_temp = 0 + npoints = 0 + igeoval = (iob-1) * n_horiz + (n_horiz + 1) / 2 + DO ilev = 1, nlevs + IF (geopotential_height(igeoval, ilev) < RMatrix_list(1) % max_height) THEN + av_temp = av_temp + air_temperature(igeoval, ilev) + npoints = npoints + 1 + END IF + END DO + + IF (npoints > 0) THEN + av_temp = av_temp / npoints + ELSE + av_temp = missing + END IF + + ! Find the observation error matrix which best matches the average + ! temperature we found + + CALL ufo_roobserror_interpolate_rmatrix(obsSatid(iob), & + obsOrigC(iob), & + av_temp, & + R_num_sats, & + RMatrix_list, & + RMatrix) + + ELSE + WRITE (Message, '(2A)') "RMatrices must have positive average ", & + "temperature" + CALL abor1_ftn(Message) + END IF + + do iheight = 1, Rmatrix % num_heights - 1 + if (obsZ(iob) < Rmatrix % height(iheight + 1)) then + exit + end if + end do + + ! Fractional error + frac_err = Rmatrix % frac_err(iheight) + & + (Rmatrix % frac_err(iheight + 1) - Rmatrix % frac_err(iheight)) * & + (obsZ(iob) - Rmatrix % height(iheight)) / & + (Rmatrix % height(iheight + 1) - Rmatrix % height(iheight)) + + WRITE(Message,'(A,I8,2F16.4,2E26.8)') 'Result', iob, obsZ(iob), frac_err, & + ObsErr(iob), MAX(frac_err * obsValue(iob), Rmatrix % min_error) + CALL fckit_log % debug(Message) + + ! Standard deviation + ObsErr(iob) = MAX(frac_err * obsValue(iob), Rmatrix % min_error) + + else + obsErr(iob) = missing + end if +end do + +end subroutine gnssro_obserr_avtemp + + +subroutine gnssro_obserr_latitude(nobs, rmatrix_filename, obsSatid, obsOrigC, obsLat, obsZ, obsValue, obsErr, QCflags, missing) + +implicit none + +! Subroutine arguments +integer, intent(in) :: nobs ! Number of observations +character(len=*), intent(in) :: rmatrix_filename ! Name of the R-matrix file +integer, intent(in) :: obsSatid(:) ! Satellite identifier +integer, intent(in) :: obsOrigC(:) ! Originating centre number +real(kind_real), intent(in) :: obsLat(:) ! Latitude of the observation +real(kind_real), intent(in) :: obsZ(:) ! Height of the observation +real(kind_real), intent(in) :: obsValue(:) ! The observed value +real(kind_real), intent(out) :: obsErr(:) ! The calculated observation error (uncertainty) +integer(c_int), intent(in) :: QCflags(:) ! Quality control flags for the observations +real(kind_real), intent(in) :: missing ! Missing value indicator + +! Local parameters +integer, parameter :: Rmax_num = 1000 ! Max number of R matrices to be read in + +! Local variables +type(rmatrix_type), allocatable :: Rmatrix_list(:) ! List of all the R matrices to use +type(rmatrix_type) :: Rmatrix ! The chosen R matrix +real(kind_real) :: frac_err ! Fractional observation error +integer :: R_num_sats ! Actual number of R-matrices read in +character(len=200) :: Message ! Message to be output +integer :: iob ! Loop variable, observation number +integer :: iheight ! Loop variable, height in profile + +! Read in R matrix data +CALL ufo_roobserror_getrmatrix(Rmax_num, & ! Max number of R matrices to read in + rmatrix_filename, & ! The name of the file to be read in + Rmatrix_list, & ! List of all R matrices to use + R_num_sats) ! Number of R matrices read in + +!-------------------------------------------------------- +! Choose R matrix depending on satid, origctr and latitude +! No interpolation between matrices to match old code +!-------------------------------------------------------- + +do iob = 1, nobs + if (QCflags(iob) .eq. 0) then + IF (RMatrix_list(1) % latitude > 0) THEN + CALL ufo_roobserror_findnearest_rmatrix(obsSatid(iob), & + obsOrigC(iob), & + obsLat(iob), & + R_num_sats, & + RMatrix_list, & + RMatrix) + ELSE + WRITE (Message, '(2A)') "RMatrices must have positive average ", & + "temperature or latitude" + CALL abor1_ftn(Message) + END IF + + do iheight = 1, Rmatrix % num_heights + if (obsZ(iob) < Rmatrix % height(iheight)) then + exit + end if + end do + + ! Calculate fractional error + if (iheight == 1) then + frac_err = Rmatrix % frac_err(iheight) + else if (iheight > Rmatrix % num_heights) then + frac_err = Rmatrix % frac_err(RMatrix % num_heights) + else + frac_err = Rmatrix % frac_err(iheight - 1) + & + (Rmatrix % frac_err(iheight) - Rmatrix % frac_err(iheight - 1)) * & + (obsZ(iob) - Rmatrix % height(iheight - 1)) / & + (Rmatrix % height(iheight) - Rmatrix % height(iheight - 1)) + end if + + WRITE(Message,'(A,I8,2F16.4,2E21.8,F12.4)') 'Result', iob, obsZ(iob), & + frac_err, ObsErr(iob), MAX(frac_err * obsValue(iob), Rmatrix % min_error), & + obsLat(iob) + CALL fckit_log % debug(Message) + + ! Standard deviation + ObsErr(iob) = MAX (frac_err * obsValue(iob), Rmatrix % min_error) + + else + WRITE(Message,'(A,I8,2F16.4)') 'Missing', iob, obsZ(iob), obsLat(iob) + CALL fckit_log % debug(Message) + obsErr(iob) = missing + end if +end do + +end subroutine gnssro_obserr_latitude + end module gnssro_mod_obserror diff --git a/src/ufo/groundgnss/CMakeLists.txt b/src/ufo/groundgnss/CMakeLists.txt index 54c3ba823..41e2fb07a 100644 --- a/src/ufo/groundgnss/CMakeLists.txt +++ b/src/ufo/groundgnss/CMakeLists.txt @@ -3,19 +3,14 @@ # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -set ( groundgnss_files - ObsGroundgnssMetOffice.h - ObsGroundgnssMetOffice.cc - ObsGroundgnssMetOffice.interface.h - ObsGroundgnssMetOffice.interface.F90 - ufo_groundgnss_metoffice_mod.F90 - ufo_groundgnss_metoffice_utils_mod.F90 - ufo_refractivity_utils_mod.F90 -) +add_subdirectory( ZenithTotalDelayMetOffice ) +add_subdirectory( ZenithTotalDelayROPP ) -PREPEND( _p_groundgnss_files "groundgnss" ${groundgnss_files} ) +PREPEND( _p_metoffice_files "groundgnss/ZenithTotalDelayMetOffice" ${groundgnssmetoffice_src_files} ) +PREPEND( _p_groundgnssropp_files "groundgnss/ZenithTotalDelayROPP" ${groundgnssropp_src_files} ) set ( groundgnss_src_files - ${_p_groundgnss_files} + ${_p_metoffice_files} + ${_p_groundgnssropp_files} PARENT_SCOPE ) diff --git a/src/ufo/groundgnss/RefROPP1D/CMakeLists.txt b/src/ufo/groundgnss/RefROPP1D/CMakeLists.txt new file mode 100644 index 000000000..b138e839f --- /dev/null +++ b/src/ufo/groundgnss/RefROPP1D/CMakeLists.txt @@ -0,0 +1,24 @@ +# (C) Copyright 2017-2018 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +if( ${ropp-ufo_FOUND} ) +set ( refropp1d_src_files + ObsGnssGBRefROPP1D.h + ObsGnssGBRefROPP1D.cc + ObsGnssGBRefROPP1D.interface.h + ObsGnssGBRefROPP1D.interface.F90 + ufo_gnssgb_refropp1d_mod.F90 + ufo_gnssgb_ropp1d_utils_mod.F90 +PARENT_SCOPE +) +else( ${ropp-ufo_FOUND} ) +set ( refropp1d_src_files + ObsGnssGBRefROPP1D.h + ObsGnssGBRefROPP1D.cc + ObsGnssGBRefROPP1D.interface.h + ObsGnssGBRefROPP1D.interface.F90 + ufo_gnssgb_refropp1d_mod_stub.F90 +PARENT_SCOPE +) +endif( ${ropp-ufo_FOUND} ) diff --git a/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.cc b/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.cc new file mode 100644 index 000000000..5c6edc457 --- /dev/null +++ b/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.cc @@ -0,0 +1,63 @@ +/* + * (C) Copyright 2017-2018 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.h" + +#include +#include +#include + +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsDiagnostics.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static ObsOperatorMaker makerGnssGBRefROPP1D_("GnssGBRefROPP1D"); +// ----------------------------------------------------------------------------- + +ObsGnssGBRefROPP1D::ObsGnssGBRefROPP1D(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : ObsOperatorBase(odb, config), keyOperGnssGBRefROPP1D_(0), odb_(odb), varin_() +{ + const std::vector vv{"air_temperature", "specific_humidity", "air_pressure", + "geopotential_height", "surface_altitude"}; + varin_.reset(new oops::Variables(vv)); + + ufo_gnssgb_refropp1d_setup_f90(keyOperGnssGBRefROPP1D_, config); + oops::Log::trace() << "ObsGnssGBRefROPP1D created." << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsGnssGBRefROPP1D::~ObsGnssGBRefROPP1D() { + ufo_gnssgb_refropp1d_delete_f90(keyOperGnssGBRefROPP1D_); + oops::Log::trace() << "ObsGnssGBRefROPP1D destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsGnssGBRefROPP1D::simulateObs(const GeoVaLs & gom, ioda::ObsVector & ovec, + ObsDiagnostics &) const { + ufo_gnssgb_refropp1d_simobs_f90(keyOperGnssGBRefROPP1D_, gom.toFortran(), odb_, + ovec.size(), ovec.toFortran()); +} + +// ----------------------------------------------------------------------------- + +void ObsGnssGBRefROPP1D::print(std::ostream & os) const { + os << "ObsGnssGBRefROPP1D::print not implemented"; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.h b/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.h new file mode 100644 index 000000000..0fc86c612 --- /dev/null +++ b/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.h @@ -0,0 +1,64 @@ +/* + * (C) Copyright 2017-2018 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_GROUNDGNSS_REFROPP1D_OBSGNSSGBREFROPP1D_H_ +#define UFO_GROUNDGNSS_REFROPP1D_OBSGNSSGBREFROPP1D_H_ + +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.interface.h" +#include "ufo/ObsOperatorBase.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsDiagnostics; + +// ----------------------------------------------------------------------------- + +/// GnssGBRefROPP1D observation operator +class ObsGnssGBRefROPP1D : public ObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsGnssGBRefROPP1D";} + + ObsGnssGBRefROPP1D(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsGnssGBRefROPP1D(); + +// Obs Operator + void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; + +// Other + const oops::Variables & requiredVars() const override {return *varin_;} + + int & toFortran() {return keyOperGnssGBRefROPP1D_;} + const int & toFortran() const {return keyOperGnssGBRefROPP1D_;} + + private: + void print(std::ostream &) const override; + F90hop keyOperGnssGBRefROPP1D_; + const ioda::ObsSpace& odb_; + std::unique_ptr varin_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_GROUNDGNSS_REFROPP1D_OBSGNSSGBREFROPP1D_H_ diff --git a/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.interface.F90 b/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.interface.F90 new file mode 100644 index 000000000..365191768 --- /dev/null +++ b/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.interface.F90 @@ -0,0 +1,80 @@ +! (C) Copyright 2017 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to handle ground-based gnss observations ROPP 1d operator + +module ufo_gnssgb_refropp1d_mod_c + + use fckit_configuration_module, only: fckit_configuration + use iso_c_binding + use ufo_gnssgb_refropp1d_mod + + implicit none + private + +#define LISTED_TYPE ufo_gnssgb_RefROPP1D + + !> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + + !> Global registry + type(registry_t) :: ufo_gnssgb_RefROPP1D_registry + + ! ------------------------------------------------------------------------------ +contains + ! ------------------------------------------------------------------------------ + !> Linked list implementation +#include "oops/util/linkedList_c.f" + +! ------------------------------------------------------------------------------ + +subroutine ufo_gnssgb_refropp1d_setup_c(c_key_self, c_conf) bind(c,name='ufo_gnssgb_refropp1d_setup_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), value, intent(in) :: c_conf + +type(ufo_gnssgb_RefROPP1D), pointer :: self +type(fckit_configuration) :: f_conf + +call ufo_gnssgb_RefROPP1D_registry%setup(c_key_self, self) +f_conf = fckit_configuration(c_conf) + +end subroutine ufo_gnssgb_RefROPP1D_setup_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_gnssgb_refropp1d_delete_c(c_key_self) bind(c,name='ufo_gnssgb_refropp1d_delete_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self + +type(ufo_gnssgb_RefROPP1D), pointer :: self + +call ufo_gnssgb_RefROPP1D_registry%delete(c_key_self,self) + +end subroutine ufo_gnssgb_refropp1d_delete_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_gnssgb_refropp1d_simobs_c(c_key_self, c_key_geovals, c_obsspace, c_nobs, c_hofx) & + bind(c,name='ufo_gnssgb_refropp1d_simobs_f90') + +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nobs +real(c_double), intent(inout) :: c_hofx(c_nobs) + +type(ufo_gnssgb_RefROPP1D), pointer :: self + +character(len=*), parameter :: myname_="ufo_gnssgb_refropp1d_simobs_c" +call ufo_gnssgb_RefROPP1D_registry%get(c_key_self, self) +call self%opr_simobs(c_key_geovals, c_obsspace, c_hofx) + +end subroutine ufo_gnssgb_refropp1d_simobs_c + +! ------------------------------------------------------------------------------ + +end module ufo_gnssgb_refropp1d_mod_c diff --git a/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.interface.h b/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.interface.h new file mode 100644 index 000000000..8d6ab04c2 --- /dev/null +++ b/src/ufo/groundgnss/RefROPP1D/ObsGnssGBRefROPP1D.interface.h @@ -0,0 +1,35 @@ +/* + * (C) Copyright 2017 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_GROUNDGNSS_REFROPP1D_OBSGNSSGBREFROPP1D_INTERFACE_H_ +#define UFO_GROUNDGNSS_REFROPP1D_OBSGNSSGBREFROPP1D_INTERFACE_H_ + +#include "ioda/ObsSpace.h" +#include "ufo/Fortran.h" + +namespace ufo { + +/// Interface to Fortran UFO routines +/*! + * The core of the UFO is coded in Fortran. + * Here we define the interfaces to the Fortran code. + */ + +extern "C" { +// ----------------------------------------------------------------------------- +// Gnssro bending angle observation operators - (ROPP1D) +// ----------------------------------------------------------------------------- + void ufo_gnssgb_refropp1d_setup_f90(F90hop &, const eckit::Configuration &); + void ufo_gnssgb_refropp1d_delete_f90(F90hop &); + void ufo_gnssgb_refropp1d_simobs_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + const int &, double &); +// ----------------------------------------------------------------------------- + +} // extern C + +} // namespace ufo +#endif // UFO_GROUNDGNSS_REFROPP1D_OBSGNSSGBREFROPP1D_INTERFACE_H_ diff --git a/src/ufo/groundgnss/RefROPP1D/ufo_gnssgb_refropp1d_mod.F90 b/src/ufo/groundgnss/RefROPP1D/ufo_gnssgb_refropp1d_mod.F90 new file mode 100644 index 000000000..f6ffa22f2 --- /dev/null +++ b/src/ufo/groundgnss/RefROPP1D/ufo_gnssgb_refropp1d_mod.F90 @@ -0,0 +1,179 @@ +! (C) Copyright 2017-2018 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module for ground-based gnss refractivity ropp1d forward operator +!> following the ROPP (2018 Aug) implementation + +module ufo_gnssgb_refropp1d_mod + +use iso_c_binding +use kinds +use ufo_vars_mod +use ufo_geovals_mod +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_basis_mod, only: ufo_basis +use vert_interp_mod +use lag_interp_mod, only: lag_interp_const, lag_interp_smthWeights +use obsspace_mod +use missing_values_mod +use ufo_gnssgb_ropp1d_utils_mod +use fckit_log_module, only : fckit_log + +implicit none +public :: ufo_gnssgb_refropp1d +private + + !> Fortran derived type for ground based gnss trajectory +type, extends(ufo_basis) :: ufo_gnssgb_refROPP1D + contains + procedure :: simobs => ufo_gnssgb_refropp1d_simobs +end type ufo_gnssgb_RefROPP1D + +contains + +! ------------------------------------------------------------------------------ +! ------------------------------------------------------------------------------ +subroutine ufo_gnssgb_refropp1d_simobs(self, geovals, hofx, obss) + use ropp_fm_types, only: State1dFM + use ropp_fm_types, only: Obs1dRefrac + use typesizes, only: wp => EightByteReal + use datetimetypes, only: dp + + implicit none + class(ufo_gnssgb_RefROPP1D), intent(in) :: self + type(ufo_geovals), intent(in) :: geovals + real(kind_real), intent(inout) :: hofx(:) + type(c_ptr), value, intent(in) :: obss + real(c_double) :: missing + + type(State1dFM) :: x + type(Obs1dRefrac) :: y + + character(len=*), parameter :: myname_="ufo_gnssgb_refropp1d_simobs" + real(kind=dp) :: ob_time + integer, parameter :: max_string = 800 + + character(max_string) :: err_msg + integer :: nlev, nobs, iobs + integer, allocatable, dimension(:) :: ichk + type(ufo_geoval), pointer :: t, q, prs, gph, gph_sfc + real(kind_real), allocatable :: obsLat(:), obsLon(:), obsHeight(:), obsValue(:) + real(kind_real), allocatable :: model_z(:) + real(kind_real) :: station_phi, model_ztd + integer :: iflip + logical :: l_linear + write(err_msg,*) "TRACE: ufo_gnssgb_refropp1d_simobs: begin" + call fckit_log%info(err_msg) + +! check if nlocs is consistent in geovals & hofx + if (geovals%nlocs /= size(hofx)) then + write(err_msg,*) myname_, ' error: nlocs inconsistent!' + call abor1_ftn(err_msg) + endif + +! get variables from geovals + call ufo_geovals_get_var(geovals, var_ts, t) ! temperature + call ufo_geovals_get_var(geovals, var_q, q) ! specific humidity + call ufo_geovals_get_var(geovals, var_prs, prs) ! pressure + call ufo_geovals_get_var(geovals, var_z, gph) ! geopotential height + call ufo_geovals_get_var(geovals, var_sfc_geomz, gph_sfc) ! surface geopotential height + + missing = missing_value(missing) + + nlev = t%nval ! number of model levels + nobs = obsspace_get_nlocs(obss) + + if (nobs > 0) then + iflip = 0 + if (prs%vals(1,1) .lt. prs%vals(prs%nval,1) ) then + iflip = 1 + write(err_msg,'(a)') ' ufo_gnssgb_refropp1d_simobs:'//new_line('a')// & + ' Model vertical height profile is in descending order,'//new_line('a')// & + ' but ROPP requires it to be ascending order, need flip' + call fckit_log%info(err_msg) + end if + + ! set obs space struture + allocate(obsLon(nobs)) + allocate(obsLat(nobs)) + allocate(obsHeight(nobs)) + allocate(obsValue(nobs)) + + ! create array for model geometric heights + allocate(model_z(nlev)) + model_z(:) = 0.0 + + call obsspace_get_db(obss, "MetaData", "longitude", obsLon) + call obsspace_get_db(obss, "MetaData", "latitude", obsLat) + call obsspace_get_db(obss, "MetaData", "station_height", obsHeight) + call obsspace_get_db(obss, "ObsValue", "ZTD", obsValue) +! obsValue = 0.0 + + allocate(ichk(nlev)) + ichk(:) = 0 ! this will hold QC values for observation from QC flags + + write(err_msg,*) "TRACE: ufo_gnssgb_refropp1d_simobs: begin observation loop, nobs = ", nobs + call fckit_log%info(err_msg) ! always print + + obs_loop: do iobs = 1, nobs + + ob_time = 0.0 + l_linear = .False. + call init_ropp_1d_statevec(ob_time, & + obsLon(iobs), & + obsLat(iobs), & + t%vals(:,iobs), & + q%vals(:,iobs), & + prs%vals(:,iobs), & + gph%vals(:,iobs), & + nlev, & + gph_sfc%vals(1,iobs), & + x, iflip) + + call calc_station_phi(obsLat(iobs), obsHeight(iobs), station_phi) + call init_ropp_1d_obvec(nlev, & + ichk, ob_time, & + obsLat(iobs), & + obsLon(iobs), & + station_phi, & + x, y) + + call ropp_fm_refrac_1d(x,y) + + call calc_model_z(nlev, obsLat(iobs), y%geop, model_z) + ! note the scaling of the refractivity by 1.e-6 is done in subroutine after integral + call gnss_ztd_integral(nlev, y%refrac, model_z, obsHeight(iobs), model_ztd, l_linear) + ! add error trap ! model_ztd initialized to 0 in integral if 0 is returned something very wrong + if ( model_ztd == 0.0 ) then + model_ztd = missing + end if + + ! matching a print format used in initialization of obvec + write(err_msg,'(a9,2a11,2a15)') 'ROPPsim: ', 'ob', 'bk', 'station_hgt', 'model_terr' + call fckit_log%debug(err_msg) ! print when MAIN_DEBUG=1 + write(err_msg,'(9x,2f11.3,2f15.3)') obsValue(iobs), model_ztd, obsHeight(iobs), model_z(1) + call fckit_log%debug(err_msg) ! print when MAIN_DEBUG=1 + + hofx(iobs) = model_ztd + + ! hack -- handling ropp missing value + call ropp_tidy_up_1d(x,y) + + end do obs_loop + + deallocate(ichk) + deallocate(obsLat) + deallocate(obsLon) + deallocate(obsHeight) + deallocate(obsValue) + end if ! nobs > 0 + + write(err_msg,*) "TRACE: ufo_gnssgb_refropp1d_simobs: completed" + call fckit_log%info(err_msg) + +end subroutine ufo_gnssgb_refropp1d_simobs +! ------------------------------------------------------------------------------ + +end module ufo_gnssgb_refropp1d_mod diff --git a/src/ufo/groundgnss/RefROPP1D/ufo_gnssgb_refropp1d_mod_stub.F90 b/src/ufo/groundgnss/RefROPP1D/ufo_gnssgb_refropp1d_mod_stub.F90 new file mode 100644 index 000000000..3f8a58d5c --- /dev/null +++ b/src/ufo/groundgnss/RefROPP1D/ufo_gnssgb_refropp1d_mod_stub.F90 @@ -0,0 +1,94 @@ +! (C) Copyright 2017-2018 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!>Stubbed Fortran module for ground based gnss ropp1d forward operator +!> following the ROPP (2018 Aug) implementation + +module ufo_gnssgb_refropp1d_mod + +use iso_c_binding +use kinds +use ufo_vars_mod +use ufo_geovals_mod +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_basis_mod, only: ufo_basis +use vert_interp_mod +use lag_interp_mod, only: lag_interp_const, lag_interp_smthWeights +use obsspace_mod +use missing_values_mod +use fckit_log_module, only : fckit_log + +implicit none +public :: ufo_gnssgb_refropp1d +private + + !> Fortran derived type for ground based gnss trajectory +type, extends(ufo_basis) :: ufo_gnssgb_RefROPP1D + contains + procedure :: simobs => ufo_gnssgb_refropp1d_simobs +end type ufo_gnssgb_RefROPP1D + +contains + +! ------------------------------------------------------------------------------ +! ------------------------------------------------------------------------------ +subroutine ufo_gnssgb_refropp1d_simobs(self, geovals, hofx, obss) + + implicit none + class(ufo_gnssgb_RefROPP1D), intent(in) :: self + type(ufo_geovals), intent(in) :: geovals + real(kind_real), intent(inout) :: hofx(:) + type(c_ptr), value, intent(in) :: obss + real(c_double) :: missing + + character(len=*), parameter :: myname_="ufo_gnssgb_refropp1d_simobs" + integer, parameter :: max_string = 800 + + character(max_string) :: err_msg + integer :: nlev, nobs, iobs,nvprof, obss_nobs + type(ufo_geoval), pointer :: t, q, prs, gph, gph_sfc + real(kind_real), allocatable :: obsLat(:), obsLon(:) + + write(err_msg,*) "TRACE: ufo_gnssgb_refropp1d_simobs_stub: begin" + call fckit_log%info(err_msg) + +! check if nobs is consistent in geovals & hofx + if (geovals%nlocs /= size(hofx)) then + write(err_msg,*) myname_, ' error: nlocs inconsistent!' + call abor1_ftn(err_msg) + endif + +! get variables from geovals + call ufo_geovals_get_var(geovals, var_ts, t) ! temperature + call ufo_geovals_get_var(geovals, var_q, q) ! specific humidity + call ufo_geovals_get_var(geovals, var_prs, prs) ! pressure + call ufo_geovals_get_var(geovals, var_z, gph) ! geopotential height + call ufo_geovals_get_var(geovals, var_sfc_geomz, gph_sfc) ! surface geopotential height + + missing = missing_value(missing) + + nlev = t%nval ! number of model levels + nobs = obsspace_get_nlocs(obss) + +! initialize HofX in the stub + hofx(:) = missing + +! set obs space struture + allocate(obsLon(nobs)) + allocate(obsLat(nobs)) + + call obsspace_get_db(obss, "MetaData", "longitude", obsLon) + call obsspace_get_db(obss, "MetaData", "latitude", obsLat) + + deallocate(obsLat) + deallocate(obsLon) + + write(err_msg,*) "TRACE: ufo_gnssgb_refropp1d_simobs_stub: completed" + call fckit_log%info(err_msg) + +end subroutine ufo_gnssgb_refropp1d_simobs +! ------------------------------------------------------------------------------ + +end module ufo_gnssgb_refropp1d_mod diff --git a/src/ufo/groundgnss/RefROPP1D/ufo_gnssgb_ropp1d_utils_mod.F90 b/src/ufo/groundgnss/RefROPP1D/ufo_gnssgb_ropp1d_utils_mod.F90 new file mode 100644 index 000000000..2b25005f1 --- /dev/null +++ b/src/ufo/groundgnss/RefROPP1D/ufo_gnssgb_ropp1d_utils_mod.F90 @@ -0,0 +1,562 @@ +! (C) Copyright 2017-2018 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to handle ground-based gnss observations following +!> the ROPP (2018 Aug) implementation + +module ufo_gnssgb_ropp1d_utils_mod + +!use iso_c_binding +use fckit_log_module, only: fckit_log +use kinds, only: kind_real + +! ROPP data type and library subroutines +use typesizes, only: wp => EightByteReal +use datetimetypes, only: dp +use ropp_fm_types, only: State1dFM, obs1dRefrac +use geodesy, only: gravity, R_eff, geometric2geopotential, geopotential2geometric +use arrays, only: callocate + +implicit none +public :: init_ropp_1d_statevec +public :: init_ropp_1d_statevec_ad +public :: init_ropp_1d_obvec +public :: init_ropp_1d_obvec_tlad +public :: ropp_tidy_up_1d +public :: ropp_tidy_up_tlad_1d +public :: calc_model_z +public :: calc_station_phi +public :: gnss_ztd_integral +private + +contains + +! ------------------------------------------------------------------------------------ +! ------------------------------------------------------------------------------------ + +subroutine init_ropp_1d_statevec(step_time,rlon,rlat, temp,shum,pres,phi,lm,phi_sfc,x, iflip) +! Description: +! subroutine to fill a ROPP state vector structure with +! model background fields: Temperature, pressure, specific +! humidity at full-levels, and surface geopotential height. +! +! Inputs: +! temp background temperature +! shum background specific humidity +! pres background pressure +! phi geopotential height +! phi_sfc terrain geopotential of background field +! lm number of vertical levels in the background +! +! Outputs: +! x Forward model state vector +! ------------------------------------------------------------------------------------ + implicit none + + type(State1dFM), intent(out) :: x + real(kind=dp), intent(in) :: step_time + real(kind=kind_real), intent(in) :: rlat, rlon + real(kind=kind_real), intent(in) :: phi_sfc + integer, intent(in) :: lm + real(kind=kind_real), dimension(lm), intent(in) :: temp,shum,pres,phi + +! Local variables + character(len=250) :: err_msg + real(kind=kind_real) :: rlon_local + integer :: n,i,j,k + integer, optional, intent(in) :: iflip + x%non_ideal = .FALSE. + x%direct_ion = .FALSE. + x%state_ok = .TRUE. + +! ROPP Longitude value is -180.0 to 180.0 + x%lat = real(rlat,kind=wp) + rlon_local = rlon + if (rlon_local .gt. 180) rlon_local = rlon_local - 360. + x%lon = real(rlon_local,kind=wp) + x%time = real(step_time, kind=wp) + + +! Number of levels in background profile. What about (lm+1) field ? + x%n_lev = lm + +!-------------------------------------------------------------- +! allocate arrays for temperature, specific humidity, pressure +! and geopotential height data +!-------------------------------------------------------------- + if (associated(x%temp)) deallocate(x%temp) + if (associated(x%shum)) deallocate(x%shum) + if (associated(x%pres)) deallocate(x%pres) + if (associated(x%geop)) deallocate(x%geop) + + allocate(x%temp(x%n_lev)) + allocate(x%shum(x%n_lev)) + allocate(x%pres(x%n_lev)) + allocate(x%geop(x%n_lev)) +!---------------------------------------------------- +! ROPP FM requires vertical height profile to be of the ascending order. +! (see ropp_io_ascend ( ROdata )). So we need to flip the data. +!---------------------------------------------------- + write(err_msg,'(4a9,a11)') 'lvl','temp','shum','pres','geop' + + n = lm + if ( present(iflip) .and. iflip .eq. 1) then + do k = 1, lm + x%temp(n) = real(temp(k),kind=wp) + x%shum(n) = real(shum(k),kind=wp) + x%pres(n) = real(pres(k),kind=wp) + x%geop(n) = real(phi(k),kind=wp) + n = n - 1 + end do +else + do k = 1, lm + x%temp(k) = real(temp(k),kind=wp) + x%shum(k) = real(shum(k),kind=wp) + x%pres(k) = real(pres(k),kind=wp) + x%geop(k) = real(phi(k),kind=wp) + end do +end if + +! sufrace geopotential height value + x%geop_sfc = real(phi_sfc,kind=wp) + write(err_msg,'("geop_sfc",f15.2)') x%geop_sfc +!------------------------------------------------ +! covariance matrix, is this used by ROPP FM? +!------------------------------------------------ + x%cov_ok = .TRUE. + +! Allocate memory +! For ECMWF example, Covariance matrix for temperature sigma and +! specific humidity sigma, and surface pressure. There the +! size of covariance matrix is 2 * nlevel + 1. + + n = (2*x%n_lev)+1 ! Number of elements in the state vector + + if (associated(x%cov%d)) deallocate(x%cov%d) + call callocate(x%cov%d, n*(n+1)/2) ! From ROPP utility library + + do i = 1, x%n_lev + x%cov%d(i + i*(i-1)/2) = 1.0_wp + end do + + do i = 1, x%n_lev + j = x%n_lev + i + x%cov%d(j + j*(j-1)/2) = 1.0_wp + enddo + + x%cov%d(n + n*(n-1)/2) = 1.0_wp + +!------------------------------ +! Rest of the covariance marix +!------------------------------ + if (associated(x%cov%e)) deallocate(x%cov%e) + if (associated(x%cov%f)) deallocate(x%cov%f) + if (associated(x%cov%s)) deallocate(x%cov%s) + + x%cov%fact_chol = .FALSE. + x%cov%equi_chol = 'N' + + return +end subroutine init_ropp_1d_statevec + +!------------------------------------------------------------------------------------ +!------------------------------------------------------------------------------------ + +subroutine init_ropp_1d_statevec_ad(temp_d,shum_d,pres_d,phi_d,lm,x_ad, iflip) + +! Description: +! subroutine to fill a ROPP state vector structure with +! model background fields: Temperature, pressure, specific +! humidity at full-levels, and surface geopotential height. +! +! Inputs: +! temp background temperature +! shum background specific humidity +! pres background pressure +! phi geopotential height +! phi_sfc terrain geopotential of background field +! lm number of vertical levels in the background +! +! Outputs: +! x Forward model state vector +! +! ############################################################### + type(State1dFM), intent(inout) :: x_ad + integer, intent(in) :: lm + real(kind=kind_real), dimension(lm), intent(inout) :: temp_d,shum_d,pres_d,phi_d + +! Local variables + integer :: n,k + integer, optional, intent(in) :: iflip +!------------------------------------------------------------------------- + + n = lm + + if ( present(iflip) .and. iflip .eq. 1) then + do k = 1, lm + +!! x_tl%temp(n,:) = real(temp_d(k),kind=wp) + temp_d(k) = temp_d(k) + real(x_ad%temp(n),kind=kind_real) + x_ad%temp(n) = 0.0_wp + +!! x_tl%shum(n,:) = real(shum_d(k),kind=wp) + shum_d(k) = shum_d(k) + real(x_ad%shum(n),kind=kind_real) + x_ad%shum(n) = 0.0_wp + +!! x_tl%pres(n,:) = real(pres_d(k),kind=wp) + pres_d(k) = pres_d(k) + real(x_ad%pres(n),kind=kind_real) + x_ad%pres(n) = 0.0_wp + +!! x_tl%geop(n,:) = real(phi_d(k),kind=wp) + phi_d(k) = phi_d(k) + real(x_ad%geop(n),kind=kind_real) + x_ad%geop(n) = 0.0_wp + + n = n -1 + end do + else + do k = 1, lm + temp_d(k) = temp_d(k) + real(x_ad%temp(k),kind=kind_real) + x_ad%temp(k) = 0.0_wp + shum_d(k) = shum_d(k) + real(x_ad%shum(k),kind=kind_real) + x_ad%shum(k) = 0.0_wp + pres_d(k) = pres_d(k) + real(x_ad%pres(k),kind=kind_real) + x_ad%pres(k) = 0.0_wp + phi_d(k) = phi_d(k) + real(x_ad%geop(k),kind=kind_real) + x_ad%geop(k) = 0.0_wp + end do + + end if + return + +end subroutine init_ropp_1d_statevec_ad + +!------------------------------------------------------------------------------------ +!------------------------------------------------------------------------------------ + +subroutine calc_station_phi(rlat, station_height, station_phi) + +! Description: +! convert station geometric height to geopotential height +! provide inputs of: +! lat, station_height +! +! Inputs: +! rlat latitude +! station_height station geometric height +! +! Outputs: +! station_phi: station geopotential height +!----------------------------------------------------------------------------------- + implicit none + + real(kind=kind_real), intent(in) :: rlat, station_height + real(kind=kind_real), intent(out) :: station_phi + + ! not used in forward operator at this time -- simulate at model levels only + station_phi = geometric2geopotential(real(rlat,kind=wp), real(station_height,kind=wp)) + +end subroutine calc_station_phi + +!------------------------------------------------------------------------------------ +!------------------------------------------------------------------------------------ + +subroutine calc_model_z(nlev, rlat, model_phi, model_z) + +! Description: +! convert model geopotential height to geometric height +! provide inputs of: +! number_of_model_levels, lat, model_geopotential +! +! Inputs: +! nlev number of model levels +! rlat latitude +! model_phi model geopotential height +! +! Outputs: +! model_z: model geometric height +!----------------------------------------------------------------------------------- + implicit none + + integer, intent(in) :: nlev + real(kind=kind_real), intent(in) :: rlat + real(kind=kind_real), dimension(nlev), intent(in) :: model_phi + real(kind=kind_real), dimension(nlev), intent(out) :: model_z + + integer ilev + + ! not used in forward operator at this time -- simulate at model levels only + do ilev = 1, nlev + model_z(ilev) = geopotential2geometric(real(rlat,kind=wp), real(model_phi(ilev),kind=wp)) + end do + +end subroutine calc_model_z + +!------------------------------------------------------------------------------------ +!------------------------------------------------------------------------------------ + +subroutine init_ropp_1d_obvec(nlev, ichk, ob_time, rlat, rlon, station_phi, x, y) + +! Description: +! subroutine to fill a ROPP observation vector structure +! observation provides the inputs of: +! lat, lon, time +! +! forward model will provide the simulation of refractivity +! +! Inputs: +! ob_time time of the observation +! rlat latitude +! rlon longitude +! +! Outputs: +! y: Partially filled Forward model observation vector +!----------------------------------------------------------------------------------- + implicit none +! Input model state vector + type(State1dFM), intent(in) :: x +! Output observation vector + type(Obs1dRefrac), intent(out) :: y + + integer, intent(in) :: nlev + integer, dimension(nlev), intent(in) :: ichk + real(kind=kind_real), intent(in) :: rlat, rlon, station_phi + real(kind=dp), intent(in) :: ob_time +! Local variables + real(kind=wp) :: r8lat + real(kind=kind_real) :: rlon_local + character(len=250) :: err_msg + integer :: i + + y%time = real(ob_time,kind=wp) + r8lat = real(rlat,kind=wp) + y%lat = r8lat + rlon_local = rlon + if (rlon_local .gt. 180) rlon_local = rlon_local - 360. + y%lon = real(rlon_local,kind=wp) + +!---------------------------------------------------- +! allocate refractivity & weights +!---------------------------------------------------- + if (associated(y%refrac)) then + deallocate(y%refrac) + deallocate(y%weights) + deallocate(y%geop) + nullify(y%refrac) + nullify(y%weights) + nullify(y%geop) + end if + + allocate(y%refrac(1:nlev)) ! value computed in fwd model + allocate(y%weights(1:nlev)) ! value set in fwd model + allocate(y%geop(1:nlev)) ! value set in fwd model + + do i=1,nlev + if (ichk(i) .le. 0) then + y%weights(i) = 1.0_wp ! following t_fascod example + else + y%weights(i) = 0.0_wp + end if + y%geop(i) = x%geop(i) + end do + + y%refrac(:) = 0.0_wp + + write(err_msg,'(a9,2a11,2a15)') 'ROPPyvec:','lat', 'lon', 'geop(1)', 'station_phi' + call fckit_log%debug(err_msg) + write(err_msg,'(9x,2f11.2,2f15.2)') y%lat, y%lon, y%geop(1), station_phi + call fckit_log%debug(err_msg) + +!--------------------------------------------- +! covariance matrix, is this used by ROPP FM? +!-------------------------------------------- + y%obs_ok = .TRUE. + return + +end subroutine init_ropp_1d_obvec + +!------------------------------------------------------------------------- +!------------------------------------------------------------------------- +subroutine init_ropp_1d_obvec_tlad(nlev, & + rlat,rlon,x,y,y_p) + implicit none +! Input state vector + type(State1dFM), intent(in) :: x +! Output observation vector + type(Obs1dRefrac), intent(out) :: y,y_p + + integer, intent(in) :: nlev + real(kind=kind_real), intent(in) :: rlat,rlon + real(kind=wp) :: r8lat + real(kind=kind_real) :: rlon_local + +!------------------------------------------------------------------------- + y%time = real(0.0, kind=wp)!)real(ob_time,kind=wp) + r8lat = real(rlat,kind=wp) + y%lat = real(rlat,kind=wp) + rlon_local = rlon + if (rlon_local .gt. 180) rlon_local = rlon_local - 360. + y%lon = real(rlon_local,kind=wp) + +! allocate refractivity +!--------------------------------------------------------- + allocate(y%refrac(1:nlev)) ! value computed in fwd model + allocate(y%weights(1:nlev)) + allocate(y%geop(1:nlev)) + allocate(y_p%refrac(1:nlev)) ! value computed in fwd model + allocate(y_p%weights(1:nlev)) + allocate(y_p%geop(1:nlev)) + + y%weights(:) = 1.0 + y_p%weights(:) = 1.0 + y%refrac(:) = 0.0 + y_p%refrac(:) = 0.0 + y%geop(:) = x%geop(:) + y_p%geop(:) = x%geop(:) + + y%obs_ok = .TRUE. + return + +end subroutine init_ropp_1d_obvec_tlad + +!------------------------------------------------------------------------- +!------------------------------------------------------------------------- +subroutine gnss_ztd_integral(lm, model_refrac, model_z, ob_terr, model_ztd, l_linear) + + implicit none + + integer, intent(in) :: lm + real(kind=kind_real), intent(in), dimension(lm) :: model_refrac, model_z + real(kind=kind_real), intent(in) :: ob_terr + real(kind=kind_real), intent(out) :: model_ztd + logical, intent(inout) :: l_linear + + integer, parameter :: max_string = 800 + character(max_string) :: err_msg + real :: ddzd, c_i + integer :: k + !------------------------------------------------------------------------------- + ! integral of ROPP refractivity values to compute zenith total delay (ztd) + !------------------------------------------------------------------------------- + l_linear = .false. + model_ztd = 0.0 + ddzd = 0.0 + do k = 1, lm + if (k==1) then + if ( model_refrac(k) <= 0 .or. model_refrac(k+1) <= 0 ) then + write(err_msg,'(a,2es13.3)') ' unphysical refractivity ', & + model_refrac(k), model_refrac(k+1) + call fckit_log%info(err_msg) + cycle + end if + ! lower boundary condition -- station below lowest model level + if ( ob_terr < model_z(k) ) then + c_i = ( log(model_refrac(k+1)) - log(model_refrac(k)) ) / & + ( model_z(k) - model_z(k+1) ) + if ( c_i == 0 ) then + write(err_msg,'(a,2es13.3)') ' model refractivity repeating1 ', & + model_refrac(k), model_refrac(k+1) + call fckit_log%info(err_msg) + l_linear = .true. + ddzd = model_refrac(k)*(model_z(k+1) - ob_terr) ! use linear estimate + cycle + end if + ddzd = -1.0 * model_refrac(k)/c_i * exp(c_i*model_z(k)) * & + ( exp(-c_i*model_z(k+1)) - exp(-c_i*ob_terr) ) +! ddzd = model_refrac(k)*(model_z(k+1) - ob_terr) ! linear estimate +! -- new seems wrong but looks better +! ddzd = model_refrac(k)*(model_z(k) - ob_terr) ! linear estimate +! -- old + end if + else if ( ob_terr >= model_z(k)) then + cycle + else + if ( model_refrac(k) <= 0 .or. model_refrac(k-1) <= 0 ) then + write(err_msg,'(a,2es13.3)') ' unphysical refractivity ', & + model_refrac(k), model_refrac(k-1) + call fckit_log%info(err_msg) + cycle + end if + ! alternate lower boundary condition -- station between levels + if ( ob_terr >= model_z(k-1) .and. ob_terr < model_z(k) ) then + c_i = ( log(model_refrac(k)) - log(model_refrac(k-1)) ) / & + ( model_z(k-1) - model_z(k) ) + if ( c_i == 0 ) then + write(err_msg,'(a,2es13.3)') ' model refractivity repeating2 ', & + model_refrac(k), model_refrac(k-1) + call fckit_log%info(err_msg) + l_linear = .true. + ddzd = model_refrac(k) * ( ob_terr - model_z(k-1) ) ! use linear estimate + ddzd = ddzd + 0.5*(model_refrac(k)+model_refrac(k-1))*(model_z(k) - ob_terr) + cycle + end if + ddzd = -1.0 * model_refrac(k-1)/c_i * exp(c_i*ob_terr) * & + ( exp(-c_i*model_z(k)) - exp(-c_i*ob_terr) ) +! ddzd = model_refrac(k) * ( ob_terr - model_z(k-1) ) ! linear +! estimate -- new +! ddzd = model_refrac(k) * ( model_z(k) - ob_terr ) ! linear +! estimate -- old + ddzd = ddzd + 0.5*(model_refrac(k)+model_refrac(k-1))*(model_z(k) - ob_terr) + else ! normal integral up the column + ddzd = 0.5*(model_refrac(k)+model_refrac(k-1))*(model_z(k) - model_z(k-1)) + end if + end if + model_ztd = model_ztd + ddzd + enddo + + ! a very bad place to put this suggestions? + model_ztd = model_ztd * 1.e-6 + +end subroutine gnss_ztd_integral + +!------------------------------------------------------------------------- +!------------------------------------------------------------------------- +subroutine ropp_tidy_up_1d(x,y) + implicit none + type(state1dfm), intent(inout) :: x + type(obs1drefrac), intent(inout) :: y +! x + if (associated(x%temp)) deallocate(x%temp) + if (associated(x%shum)) deallocate(x%shum) + if (associated(x%pres)) deallocate(x%pres) + if (associated(x%geop)) deallocate(x%geop) +! y + if (associated(y%refrac)) deallocate(y%refrac) + if (associated(y%geop)) deallocate(y%geop) + if (associated(y%weights)) deallocate(y%weights) + +end subroutine ropp_tidy_up_1d + +!------------------------------------------------------------------------- +!------------------------------------------------------------------------- +subroutine ropp_tidy_up_tlad_1d(x,x_p,y,y_p) + implicit none + type(state1dfm), intent(inout) :: x,x_p ! x_p can be either x_tl or x_ad + type(obs1drefrac), intent(inout) :: y,y_p ! y_p can be either y_tl or y_ad +! x + deallocate(x%temp) + deallocate(x%shum) + deallocate(x%pres) + deallocate(x%geop) + + deallocate(x_p%temp) + deallocate(x_p%shum) + deallocate(x_p%pres) + deallocate(x_p%geop) +! y + deallocate(y%refrac) + deallocate(y_p%refrac) + + deallocate(y%weights) + deallocate(y_p%weights) + + deallocate(y%geop) + deallocate(y_p%geop) + return + +end subroutine ropp_tidy_up_tlad_1d + +!------------------------------------------------------------------------- + +end module ufo_gnssgb_ropp1d_utils_mod diff --git a/src/ufo/groundgnss/ZenithTotalDelayMetOffice/CMakeLists.txt b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/CMakeLists.txt new file mode 100644 index 000000000..da9e02fd3 --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/CMakeLists.txt @@ -0,0 +1,21 @@ +# (C) Copyright 2017-2018 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( groundgnssmetoffice_src_files + ObsGroundgnssMetOffice.h + ObsGroundgnssMetOffice.cc + ObsGroundgnssMetOffice.interface.h + ObsGroundgnssMetOffice.interface.F90 + ObsGroundgnssMetOfficeTLAD.h + ObsGroundgnssMetOfficeTLAD.cc + ObsGroundgnssMetOfficeTLAD.interface.h + ObsGroundgnssMetOfficeTLAD.interface.F90 + ufo_groundgnss_metoffice_mod.F90 + ufo_groundgnss_metoffice_utils_mod.F90 + ufo_groundgnss_metoffice_tlad_mod.F90 +PARENT_SCOPE +) + + diff --git a/src/ufo/groundgnss/ObsGroundgnssMetOffice.cc b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.cc similarity index 95% rename from src/ufo/groundgnss/ObsGroundgnssMetOffice.cc rename to src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.cc index b9c682df1..0220cc518 100644 --- a/src/ufo/groundgnss/ObsGroundgnssMetOffice.cc +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.cc @@ -1,11 +1,11 @@ /* - * (C) Copyright 2017-2018 UCAR + * (C) Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#include "ufo/groundgnss/ObsGroundgnssMetOffice.h" +#include "ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.h" #include #include diff --git a/src/ufo/groundgnss/ObsGroundgnssMetOffice.h b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.h similarity index 81% rename from src/ufo/groundgnss/ObsGroundgnssMetOffice.h rename to src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.h index 3b4594211..bf58b0875 100644 --- a/src/ufo/groundgnss/ObsGroundgnssMetOffice.h +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.h @@ -1,12 +1,12 @@ /* - * (C) Copyright 2017-2018 UCAR + * (C) Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#ifndef UFO_GROUNDGNSS_OBSGROUNDGNSSMETOFFICE_H_ -#define UFO_GROUNDGNSS_OBSGROUNDGNSSMETOFFICE_H_ +#ifndef UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICE_H_ +#define UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICE_H_ #include #include @@ -14,7 +14,7 @@ #include "oops/base/Variables.h" #include "oops/util/ObjectCounter.h" -#include "ufo/groundgnss/ObsGroundgnssMetOffice.interface.h" +#include "ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.interface.h" #include "ufo/ObsOperatorBase.h" namespace eckit { @@ -61,4 +61,4 @@ class ObsGroundgnssMetOffice : public ObsOperatorBase, } // namespace ufo -#endif // UFO_GROUNDGNSS_OBSGROUNDGNSSMETOFFICE_H_ +#endif // UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICE_H_ diff --git a/src/ufo/groundgnss/ObsGroundgnssMetOffice.interface.F90 b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.interface.F90 similarity index 98% rename from src/ufo/groundgnss/ObsGroundgnssMetOffice.interface.F90 rename to src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.interface.F90 index faefbe691..15f2d38d1 100644 --- a/src/ufo/groundgnss/ObsGroundgnssMetOffice.interface.F90 +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.interface.F90 @@ -1,4 +1,4 @@ -! (C) Copyright 2017 UCAR +! (C) Copyright 2021 Met Office ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. diff --git a/src/ufo/groundgnss/ObsGroundgnssMetOffice.interface.h b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.interface.h similarity index 78% rename from src/ufo/groundgnss/ObsGroundgnssMetOffice.interface.h rename to src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.interface.h index 6a852bff1..e144f3564 100644 --- a/src/ufo/groundgnss/ObsGroundgnssMetOffice.interface.h +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOffice.interface.h @@ -1,12 +1,12 @@ /* - * (C) Copyright 2017 UCAR + * (C) Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#ifndef UFO_GROUNDGNSS_OBSGROUNDGNSSMETOFFICE_INTERFACE_H_ -#define UFO_GROUNDGNSS_OBSGROUNDGNSSMETOFFICE_INTERFACE_H_ +#ifndef UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICE_INTERFACE_H_ +#define UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICE_INTERFACE_H_ #include "ioda/ObsSpace.h" #include "ufo/Fortran.h" @@ -32,4 +32,4 @@ extern "C" { } // extern C } // namespace ufo -#endif // UFO_GROUNDGNSS_OBSGROUNDGNSSMETOFFICE_INTERFACE_H_ +#endif // UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICE_INTERFACE_H_ diff --git a/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.cc b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.cc new file mode 100644 index 000000000..b63d7f2c5 --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.cc @@ -0,0 +1,85 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.h" + +#include +#include +#include + + +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +#include "ufo/GeoVaLs.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static LinearObsOperatorMaker + makerGroundgnssMetOfficeTL_("GroundgnssMetOffice"); +// ----------------------------------------------------------------------------- + +ObsGroundgnssMetOfficeTLAD::ObsGroundgnssMetOfficeTLAD(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : LinearObsOperatorBase(odb), keyOperGroundgnssMetOffice_(0), varin_() +{ + const eckit::LocalConfiguration obsOptions(config, "obs options"); + const eckit::Configuration * configc = &obsOptions; + + ufo_groundgnss_metoffice_tlad_setup_f90(keyOperGroundgnssMetOffice_, &configc); + const std::vector vv{"air_pressure_levels", "specific_humidity", + "geopotential_height", "geopotential_height_levels"}; + + varin_.reset(new oops::Variables(vv)); + oops::Log::info() << "ObsGroundgnssMetOfficeTLAD vars: " << *varin_ << std::endl; + oops::Log::trace() << "ObsGroundgnssMetOfficeTLAD created" << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsGroundgnssMetOfficeTLAD::~ObsGroundgnssMetOfficeTLAD() { + ufo_groundgnss_metoffice_tlad_delete_f90(keyOperGroundgnssMetOffice_); + oops::Log::trace() << "ObsGroundgnssMetOfficeTLAD destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsGroundgnssMetOfficeTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, + ObsDiagnostics &) { + ufo_groundgnss_metoffice_tlad_settraj_f90(keyOperGroundgnssMetOffice_, geovals.toFortran(), + obsspace()); +} + +// ----------------------------------------------------------------------------- + +void ObsGroundgnssMetOfficeTLAD::simulateObsTL( + const GeoVaLs & geovals, ioda::ObsVector & ovec) const { + ufo_groundgnss_metoffice_simobs_tl_f90(keyOperGroundgnssMetOffice_, geovals.toFortran(), + obsspace(), ovec.size(), ovec.toFortran()); +} + +// ----------------------------------------------------------------------------- + +void ObsGroundgnssMetOfficeTLAD::simulateObsAD( + GeoVaLs & geovals, const ioda::ObsVector & ovec) const { + ufo_groundgnss_metoffice_simobs_ad_f90(keyOperGroundgnssMetOffice_, geovals.toFortran(), + obsspace(), ovec.size(), ovec.toFortran()); +} + +// ----------------------------------------------------------------------------- + +void ObsGroundgnssMetOfficeTLAD::print(std::ostream & os) const { + os << "ObsGroundgnssMetOfficeTLAD::print not implemented" << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.h b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.h new file mode 100644 index 000000000..2131b7911 --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.h @@ -0,0 +1,65 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICETLAD_H_ +#define UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICETLAD_H_ + +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.interface.h" +#include "ufo/LinearObsOperatorBase.h" + +// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsBias; + class ObsDiagnostics; + +// ----------------------------------------------------------------------------- +/// GroundgnssMetOffice observation operator +class ObsGroundgnssMetOfficeTLAD : public LinearObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsGroundgnssMetOfficeTLAD";} + + ObsGroundgnssMetOfficeTLAD(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsGroundgnssMetOfficeTLAD(); + + // Obs Operators + void setTrajectory(const GeoVaLs &, const ObsBias &, ObsDiagnostics &) override; + void simulateObsTL(const GeoVaLs &, ioda::ObsVector &) const override; + void simulateObsAD(GeoVaLs &, const ioda::ObsVector &) const override; + + // Other + const oops::Variables & requiredVars() const override {return *varin_;} + + int & toFortran() {return keyOperGroundgnssMetOffice_;} + const int & toFortran() const {return keyOperGroundgnssMetOffice_;} + + private: + void print(std::ostream &) const override; + F90hop keyOperGroundgnssMetOffice_; + std::unique_ptr varin_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICETLAD_H_ diff --git a/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.interface.F90 b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.interface.F90 new file mode 100644 index 000000000..d7a7bc99b --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.interface.F90 @@ -0,0 +1,120 @@ +! (C) Copyright 2021 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to handle ground gnss observations + +module ufo_groundgnss_metoffice_tlad_mod_c + + use fckit_configuration_module, only: fckit_configuration + use ufo_groundgnss_metoffice_tlad_mod + implicit none + private + +#define LISTED_TYPE ufo_groundgnss_metoffice_tlad + + !> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + + !> Global registry + type(registry_t) :: ufo_groundgnss_metoffice_tlad_registry + + ! ------------------------------------------------------------------------------ +contains + ! ------------------------------------------------------------------------------ + !> Linked list implementation +#include "oops/util/linkedList_c.f" + +! ------------------------------------------------------------------------------ + +subroutine ufo_groundgnss_metoffice_tlad_setup_c(c_key_self, c_conf) bind(c,name='ufo_groundgnss_metoffice_tlad_setup_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), intent(in) :: c_conf + +type(ufo_groundgnss_metoffice_tlad), pointer :: self +type(fckit_configuration) :: f_conf + +call ufo_groundgnss_metoffice_tlad_registry%setup(c_key_self, self) +f_conf = fckit_configuration(c_conf) + +call self%setup(f_conf) + +end subroutine ufo_groundgnss_metoffice_tlad_setup_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_groundgnss_metoffice_tlad_delete_c(c_key_self) bind(c,name='ufo_groundgnss_metoffice_tlad_delete_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self + +type(ufo_groundgnss_metoffice_tlad), pointer :: self + +call ufo_groundgnss_metoffice_tlad_registry%get(c_key_self, self) +call self%opr_delete() +call ufo_groundgnss_metoffice_tlad_registry%remove(c_key_self) + +end subroutine ufo_groundgnss_metoffice_tlad_delete_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_groundgnss_metoffice_tlad_settraj_c(c_key_self, c_key_geovals, c_obsspace) bind(c,name='ufo_groundgnss_metoffice_tlad_settraj_f90') + +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace + +type(ufo_groundgnss_metoffice_tlad), pointer :: self + +character(len=*), parameter :: myname_="ufo_groundgnss_metoffice_tlad_settraj_c" + +call ufo_groundgnss_metoffice_tlad_registry%get(c_key_self, self) +call self%opr_settraj(c_key_geovals, c_obsspace) + +end subroutine ufo_groundgnss_metoffice_tlad_settraj_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_groundgnss_metoffice_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nobs, c_hofx) bind(c,name='ufo_groundgnss_metoffice_simobs_tl_f90') + +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nobs +real(c_double), intent(inout) :: c_hofx(c_nobs) + +type(ufo_groundgnss_metoffice_tlad), pointer :: self + +character(len=*), parameter :: myname_="ufo_groundgnss_metoffice_simobs_tl_c" + +call ufo_groundgnss_metoffice_tlad_registry%get(c_key_self, self) +call self%opr_simobs_tl(c_key_geovals, c_obsspace, c_hofx) + +end subroutine ufo_groundgnss_metoffice_simobs_tl_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_groundgnss_metoffice_simobs_ad_c(c_key_self, c_key_geovals, c_obsspace, c_nobs, c_hofx) bind(c,name='ufo_groundgnss_metoffice_simobs_ad_f90') + +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nobs +real(c_double), intent(in) :: c_hofx(c_nobs) + +type(ufo_groundgnss_metoffice_tlad), pointer :: self + +character(len=*), parameter :: myname_="ufo_groundgnss_metoffice_simobs_ad_c" + +call ufo_groundgnss_metoffice_tlad_registry%get(c_key_self, self) +call self%opr_simobs_ad(c_key_geovals, c_obsspace, c_hofx) + +end subroutine ufo_groundgnss_metoffice_simobs_ad_c + +! ------------------------------------------------------------------------------ + +end module ufo_groundgnss_metoffice_tlad_mod_c diff --git a/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.interface.h b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.interface.h new file mode 100644 index 000000000..f685e41e1 --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ObsGroundgnssMetOfficeTLAD.interface.h @@ -0,0 +1,39 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICETLAD_INTERFACE_H_ +#define UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICETLAD_INTERFACE_H_ + +#include "ioda/ObsSpace.h" +#include "ufo/Fortran.h" + +namespace ufo { + +/// Interface to Fortran UFO routines +/*! + * The core of the UFO is coded in Fortran. + * Here we define the interfaces to the Fortran code. + */ + +extern "C" { +// ----------------------------------------------------------------------------- +// Ground Based GNSS tl/ad observation operators +// ----------------------------------------------------------------------------- + void ufo_groundgnss_metoffice_tlad_setup_f90(F90hop &, const eckit::Configuration * const *); + void ufo_groundgnss_metoffice_tlad_delete_f90(F90hop &); + void ufo_groundgnss_metoffice_tlad_settraj_f90(const F90hop &, const F90goms &, + const ioda::ObsSpace &); + void ufo_groundgnss_metoffice_simobs_tl_f90( + const F90hop &, const F90goms &, const ioda::ObsSpace &, const int &, double &); + void ufo_groundgnss_metoffice_simobs_ad_f90( + const F90hop &, const F90goms &, const ioda::ObsSpace &, const int &, const double &); +// ----------------------------------------------------------------------------- + +} // extern C + +} // namespace ufo +#endif // UFO_GROUNDGNSS_ZENITHTOTALDELAYMETOFFICE_OBSGROUNDGNSSMETOFFICETLAD_INTERFACE_H_ diff --git a/src/ufo/groundgnss/ufo_groundgnss_metoffice_mod.F90 b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ufo_groundgnss_metoffice_mod.F90 similarity index 52% rename from src/ufo/groundgnss/ufo_groundgnss_metoffice_mod.F90 rename to src/ufo/groundgnss/ZenithTotalDelayMetOffice/ufo_groundgnss_metoffice_mod.F90 index c1925c201..a3926e409 100644 --- a/src/ufo/groundgnss/ufo_groundgnss_metoffice_mod.F90 +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ufo_groundgnss_metoffice_mod.F90 @@ -18,8 +18,8 @@ module ufo_groundgnss_metoffice_mod use obsspace_mod use missing_values_mod use ufo_groundgnss_ukmo_utils_mod -use ufo_refractivity_utils_mod use fckit_log_module, only : fckit_log +use ufo_utils_refractivity_calculator, only: ufo_calculate_refractivity implicit none public :: ufo_groundgnss_metoffice @@ -27,6 +27,9 @@ module ufo_groundgnss_metoffice_mod !> Fortran derived type for groundgnss trajectory type, extends(ufo_basis) :: ufo_groundgnss_MetOffice + logical :: vert_interp_ops + logical :: pseudo_ops + real(kind_real) :: min_temp_grad contains procedure :: setup => ufo_groundgnss_metoffice_setup procedure :: simobs => ufo_groundgnss_metoffice_simobs @@ -44,6 +47,7 @@ subroutine ufo_groundgnss_metoffice_setup(self, f_conf) implicit none class(ufo_groundgnss_MetOffice), intent(inout) :: self type(fckit_configuration), intent(in) :: f_conf +call f_conf%get_or_die("min_temp_grad", self % min_temp_grad) end subroutine ufo_groundgnss_metoffice_setup @@ -74,16 +78,23 @@ subroutine ufo_groundgnss_metoffice_simobs(self, geovals, hofx, obss) type(ufo_geoval), pointer :: rho_heights ! Model heights of levels containing air pressure real(kind_real), allocatable :: zStation(:) + + ! Local variables + INTEGER :: ilev, nlevp, nlevq, iflip + REAL(kind_real), allocatable :: pressure(:) ! Model background values of air pressure (monotonic order) + REAL(kind_real), allocatable :: humidity(:) ! Model background specific humidity (in pressure monotonic order) + REAL(kind_real), allocatable :: za(:) ! Model heights of rho levs (in pressure monotonic order) + REAL(kind_real), allocatable :: zb(:) ! Model heights of theta levs (in pressure monotonic order) + write(err_msg,*) "TRACE: ufo_groundgnss_metoffice_simobs: begin" call fckit_log%info(err_msg) ! check if nlocs is consistent in geovals & hofx - if (geovals%nlocs /= size(hofx)) then - write(err_msg,*) myname_, ' error: nlocs inconsistent!' - call abor1_ftn(err_msg) - endif - + IF (geovals%nlocs /= size(hofx)) THEN + write(err_msg,*) myname_, ' error: nlocs inconsistent!' + call abor1_ftn(err_msg) + END IF ! get variables from geovals call ufo_geovals_get_var(geovals, var_q, q) ! specific humidity @@ -91,6 +102,16 @@ subroutine ufo_groundgnss_metoffice_simobs(self, geovals, hofx, obss) call ufo_geovals_get_var(geovals, var_z, theta_heights) ! Geopotential height of the normal model levels call ufo_geovals_get_var(geovals, var_zi, rho_heights) ! Geopotential height of the pressure levels + nlevp = prs % nval + nlevq = q % nval + + iflip = 0 + IF (prs % vals(1,1)-prs % vals(nlevp,1) < 0.0) THEN + iflip = 1 + WRITE(message, *) "Pressure is in ascending order. Reorder the variables in vertical direction" + CALL fckit_log % warning(message) + END IF + nobs = obsspace_get_nlocs(obss) allocate(zStation(nobs)) @@ -99,61 +120,93 @@ subroutine ufo_groundgnss_metoffice_simobs(self, geovals, hofx, obss) write(err_msg,*) "TRACE: ufo_groundgnss_metoffice_simobs: begin observation loop, nobs = ", nobs call fckit_log%info(err_msg) - obs_loop: do iobs = 1, nobs + hofx(:) = 0 + + allocate(pressure(1:nlevp)) + allocate(humidity(1:nlevq)) + allocate(za(1:nlevp)) + allocate(zb(1:nlevq)) - call Ops_Groundgnss_ForwardModel(prs % nval, & - q % nval, & - rho_heights % vals(:,iobs), & - theta_heights % vals(:,iobs), & - prs % vals(:,iobs), & - q % vals(:,iobs), & - 1, & - zStation(iobs), & + obs_loop: do iobs = 1, nobs + + IF (iflip == 1) THEN + do ilev = 1, nlevp + pressure(ilev) = prs % vals(nlevp-ilev+1,iobs) + za(ilev) = rho_heights % vals(nlevp-ilev+1,iobs) + end do + do ilev = 1, nlevq + humidity(ilev) = q % vals(nlevq-ilev+1,iobs) + zb(ilev) = theta_heights % vals(nlevq-ilev+1,iobs) + end do + ELSE + pressure = prs % vals(:,iobs) + humidity = q % vals(:,iobs) + za = rho_heights % vals(:,iobs) + zb = theta_heights % vals(:,iobs) + END IF + + call Ops_Groundgnss_ForwardModel(nlevp, & + nlevq, & + za(1:nlevp), & + zb(1:nlevq), & + pressure(1:nlevp), & + humidity(1:nlevq), & + self % min_temp_grad, & + 1, & + zStation(iobs), & hofx(iobs)) write(message,'(A,10I6)') "Size of hofx = ", shape(hofx) - call fckit_log%info(message) - write(message,'(A,1F2.4)') "hofx(iobs) = ", hofx(iobs) - call fckit_log%info(message) + call fckit_log%debug(message) + write(message,'(A,F12.4)') "hofx(iobs) = ", hofx(iobs) + call fckit_log%debug(message) + end do obs_loop write(err_msg,*) "TRACE: ufo_groundgnss_metoffice_simobs: completed" call fckit_log%info(err_msg) + + deallocate(pressure) + deallocate(humidity) + deallocate(za) + deallocate(zb) end subroutine ufo_groundgnss_metoffice_simobs ! ------------------------------------------------------------------------------ -SUBROUTINE Ops_Groundgnss_ForwardModel(nlevP, & - nlevq, & - za, & - zb, & - pressure, & - humidity, & - nobs, & - zStation, & +SUBROUTINE Ops_Groundgnss_ForwardModel(nlevp, & + nlevq, & + za, & + zb, & + pressure, & + humidity, & + gbgnss_min_temp_grad, & + nobs, & + zStation, & Model_ZTD) -INTEGER, INTENT(IN) :: nlevP ! no. of p levels in state vec. +INTEGER, INTENT(IN) :: nlevp ! no. of p levels in state vec. INTEGER, INTENT(IN) :: nlevq ! no. of theta levels -REAL(kind_real), INTENT(IN) :: za(1:nlevP) ! heights of rho levs +REAL(kind_real), INTENT(IN) :: za(1:nlevp) ! heights of rho levs REAL(kind_real), INTENT(IN) :: zb(1:nlevq) ! heights of theta levs -REAL(kind_real), INTENT(IN) :: pressure(1:nlevP) ! Model background pressure +REAL(kind_real), INTENT(IN) :: pressure(1:nlevp) ! Model background pressure REAL(kind_real), INTENT(IN) :: humidity(1:nlevq) ! Model background specific humidity +REAL(kind_real), INTENT(IN) :: gbgnss_min_temp_grad ! The minimum temperature gradient which is used INTEGER, INTENT(IN) :: nobs ! Number of observations -REAL(kind_real), INTENT(IN) :: zStation -REAL(kind_real), INTENT(INOUT) :: Model_ZTD ! Model forecast of the observations +REAL(kind_real), INTENT(IN) :: zStation ! Station heights +REAL(kind_real), INTENT(INOUT) :: Model_ZTD ! Model forecast of the observations ! ! Things that may need to be output, as they are used by the TL/AD calculation ! -REAL(kind_real) :: pN(nlevq) ! Presure on theta levels -REAL(kind_real) :: refrac(nlevq) -LOGICAL :: refracerr - -REAL(kind_real) :: TopCorrection +REAL(kind_real) :: pN(nlevq) ! Presure on theta levels +REAL(kind_real), ALLOCATABLE :: refrac(:) ! Model refractivity +LOGICAL :: refracerr ! Refraction error +INTEGER :: nRefLevels ! Number of levels in refractivity calculation +REAL(kind_real) :: TopCorrection ! Zenith Total Delay Top of atmos correction ! ! Local parameters ! @@ -163,45 +216,52 @@ SUBROUTINE Ops_Groundgnss_ForwardModel(nlevP, & ! Local variables ! INTEGER :: nstate ! no. of levels in state vec. -REAL(kind_real) :: x(1:nlevP+nlevQ) ! state vector +REAL(kind_real) :: x(1:nlevp+nlevq) ! state vector character(max_string) :: err_msg ! Error message to be output -character(max_string) :: message ! General message for output +character(max_string) :: message ! General message for output + +REAL(kind_real), ALLOCATABLE :: model_heights(:) ! Geopotential heights of the refractivity levels (not needed for this oper) ! The model data must be on a staggered grid, with nlevp = nlevq+1 -IF (nlevP /= nlevQ + 1) THEN +IF (nlevp /= nlevq + 1) THEN write(err_msg,*) myname_ // ':' // ' Data must be on a staggered grid nlevp, nlevq = ', nlevp, nlevq call fckit_log % warning(err_msg) write(err_msg,*) myname_ // ':' // ' error: number of levels inconsistent!' call abor1_ftn(err_msg) END IF -nstate = nlevP + nlevq -x(1:nlevP) = pressure -x(nlevP+1:nstate) = humidity - -CALL ufo_refractivity(nlevq, & - nlevP, & - za, & - zb, & - x, & - pN, & - refracerr, & - refrac) - -CALL Ops_groundgnss_TopCorrection(pN, & - nlevq, & +CALL ufo_calculate_refractivity(nlevp, & + nlevq, & + za, & + zb, & + pressure, & + humidity, & + .TRUE., & ! vert_interp_ops + .FALSE., & ! pseudo_ops + gbgnss_min_temp_grad, & + refracerr, & + nRefLevels, & + refrac, & + model_heights) + +CALL Ops_groundgnss_TopCorrection(pressure, & + nlevq, & + za, & + zb, & TopCorrection) -CALL Ops_Groundgnss_ZTD (nlevq, & - refrac, & - zb, & + +CALL Ops_Groundgnss_ZTD (nlevq, & + refrac, & + zb, & zStation, & Model_ZTD) Model_ZTD = Model_ZTD + TopCorrection -write(message,'(A,F10.4)') "Model_ZTD = ", Model_ZTD -call fckit_log%info(message) +write(message,'(A,F16.14)') "Model_ZTD = ", Model_ZTD +call fckit_log%debug(message) + END SUBROUTINE ops_groundgnss_forwardmodel diff --git a/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ufo_groundgnss_metoffice_tlad_mod.F90 b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ufo_groundgnss_metoffice_tlad_mod.F90 new file mode 100644 index 000000000..5943d5a7b --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ufo_groundgnss_metoffice_tlad_mod.F90 @@ -0,0 +1,801 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- +!> Fortran module for ground based GNSS Met Office's tangent linear and adjoint + +module ufo_groundgnss_metoffice_tlad_mod +use iso_c_binding + +use kinds +use ufo_vars_mod +use ufo_geovals_mod +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_basis_tlad_mod, only: ufo_basis_tlad +use obsspace_mod +use missing_values_mod +use fckit_log_module, only : fckit_log +use ufo_utils_refractivity_calculator, only: & + ufo_calculate_refractivity, ufo_refractivity_kmat + + + +use ufo_constants_mod, only: & + rd, & ! Gas constant for dry air + grav, & ! Gravitational field strength + n_alpha, & ! Refractivity constant a + n_beta + +integer, parameter :: max_string=800 + +!> Fortran derived type for groundgnss trajectory +type, extends(ufo_basis_tlad) :: ufo_groundgnss_metoffice_tlad + private + + integer :: nlevp, nlevq, nlocs, iflip + real(kind_real), allocatable :: K(:,:) + real(kind_real), allocatable :: dztd_dp(:,:) + real(kind_real), allocatable :: dztd_dq(:,:) + logical :: vert_interp_ops + logical :: pseudo_ops + real(kind_real) :: min_temp_grad + contains + procedure :: setup => ufo_groundgnss_metoffice_setup + procedure :: delete => ufo_groundgnss_metoffice_tlad_delete + procedure :: settraj => ufo_groundgnss_metoffice_tlad_settraj + procedure :: simobs_tl => ufo_groundgnss_metoffice_simobs_tl + procedure :: simobs_ad => ufo_groundgnss_metoffice_simobs_ad +end type ufo_groundgnss_metoffice_tlad + +contains + +! ------------------------------------------------------------------------------ +! Get the optional settings for the forward model, and save them in the object +! so that they can be used in the code. +! ------------------------------------------------------------------------------ +subroutine ufo_groundgnss_metoffice_setup(self, f_conf) + +use fckit_configuration_module, only: fckit_configuration +implicit none +class(ufo_groundgnss_metoffice_tlad), intent(inout) :: self +type(fckit_configuration), intent(in) :: f_conf +call f_conf%get_or_die("min_temp_grad", self % min_temp_grad) + +end subroutine ufo_groundgnss_metoffice_setup + + +! ------------------------------------------------------------------------------ +! Calculate the K-matrix (Jacobian) for the observation. It is necessary to run +! this routine before calling the TL or AD routines. +! ------------------------------------------------------------------------------ +subroutine ufo_groundgnss_metoffice_tlad_settraj(self, geovals, obss) + + implicit none +! Subroutine arguments + class(ufo_groundgnss_metoffice_tlad), intent(inout) :: self ! The object that we use to save data in + type(ufo_geovals), intent(in) :: geovals ! The input geovals + type(c_ptr), value, intent(in) :: obss ! The input observations + +! Local parameters + character(len=*), parameter :: myname_="ufo_groundgnss_metoffice_tlad_settraj" + +! Local variables + character(max_string) :: err_msg ! Messages to be output to the user + type(ufo_geoval), pointer :: q ! The model geovals - specific humidity + type(ufo_geoval), pointer :: prs ! The model geovals - atmospheric pressure + type(ufo_geoval), pointer :: rho_heights ! The model geovals - heights of the pressure-levels + type(ufo_geoval), pointer :: theta_heights ! The model geovals - heights of the theta-levels (q on theta) + integer :: nstate ! The size of the state vector + integer :: iobs ! Loop variable, observation number + integer :: nobs ! Number of observations + integer :: ilev ! Loop variable, vertical level number + real(kind_real), allocatable :: zStation(:) ! The station height + real(kind_real), allocatable :: pressure(:) ! Model background values of air pressure (monotonic order) + real(kind_real), allocatable :: humidity(:) ! Model background specific humidity (in pressure monotonic order) + real(kind_real), allocatable :: za(:) ! Model heights of rho levs (in pressure monotonic order) + real(kind_real), allocatable :: zb(:) ! Model heights of theta levs (in pressure monotonic order) + + integer, parameter :: max_string = 800 + character(max_string) :: message ! General message for output + + write(err_msg,*) "TRACE: ufo_groundgnss_metoffice_tlad_settraj: begin" + call fckit_log%info(err_msg) + +! Make sure that any previous values of geovals don't get carried over + call self%delete() + +! get model state variables from geovals + call ufo_geovals_get_var(geovals, var_q, q) ! specific humidity + call ufo_geovals_get_var(geovals, var_prsi, prs) ! pressure + call ufo_geovals_get_var(geovals, var_z, theta_heights) ! Geopotential height of the normal model levels + call ufo_geovals_get_var(geovals, var_zi, rho_heights) ! Geopotential height of the pressure levels + +! Keep copy of dimensions + self % nlevp = prs % nval + self % nlevq = q % nval + self % nlocs = obsspace_get_nlocs(obss) + +! Check whether the pressure levels are in descending order + self%iflip = 0 + IF (prs % vals(1,1)-prs % vals(self%nlevp,1) < 0.0) THEN + self%iflip = 1 + WRITE(message, *) "Pressure is in ascending order. Reorder the variables in vertical direction" + CALL fckit_log % warning(message) + END IF + + +! Get the meta-data from the observations + nobs = obsspace_get_nlocs(obss) + allocate(zStation(nobs)) + + call obsspace_get_db(obss, "MetaData", "station_height", zStation) + + nstate = prs % nval + q % nval + ALLOCATE(self % K(1:self%nlocs, 1:nstate)) + ALLOCATE(pressure(1:self%nlevp)) + ALLOCATE(humidity(1:self%nlevq)) + ALLOCATE(za(1:self%nlevp)) + ALLOCATE(zb(1:self%nlevq)) + +! For each observation, calculate the K-matrix + obs_loop: do iobs = 1, self % nlocs + IF (self%iflip == 1) THEN + do ilev = 1, self%nlevp + pressure(ilev) = prs % vals(self%nlevp-ilev+1,iobs) + za(ilev) = rho_heights % vals(self%nlevp-ilev+1,iobs) + end do + do ilev = 1, self%nlevq + humidity(ilev) = q % vals(self%nlevq-ilev+1,iobs) + zb(ilev) = theta_heights % vals(self%nlevq-ilev+1,iobs) + end do + ELSE + pressure = prs % vals(:,iobs) + humidity = q % vals(:,iobs) + za = rho_heights % vals(:,iobs) + zb = theta_heights % vals(:,iobs) + END IF + CALL groundgnss_jacobian_interface(self % nlevp, & ! Number of pressure levels + self % nlevq, & ! Number of specific humidity levels + za(1:self%nlevp), & ! Heights of the pressure levels + zb(1:self%nlevq), & ! Heights of the specific humidity levels + humidity(1:self%nlevq), & ! Values of the specific humidity + pressure(1:self%nlevp), & ! Values of the pressure + zStation(iobs), & ! Station height + iobs, & ! Ob number + self % min_temp_grad, & ! Minimum temperature gradient allowed + self % K(:, 1:nstate)) ! K-matrix (Jacobian of the observation with respect to the inputs + + end do obs_loop + +! Note that this routine has been run. + self%ltraj = .true. + + deallocate(zStation) + +end subroutine ufo_groundgnss_metoffice_tlad_settraj + + +! ------------------------------------------------------------------------------ +! Given an increment to the model state, calculate an increment to the +! observation +! ------------------------------------------------------------------------------ +subroutine ufo_groundgnss_metoffice_simobs_tl(self, geovals, hofx, obss) + + implicit none + +! Subroutine arguments + class(ufo_groundgnss_metoffice_tlad), intent(in) :: self ! Object which is being used to transfer information + type(ufo_geovals), intent(in) :: geovals ! Model perturbations + real(kind_real), intent(inout) :: hofx(:) ! Increment to the observations + type(c_ptr), value, intent(in) :: obss ! Input - the observations + +! Local parameters + character(len=*), parameter :: myname_="ufo_groundgnss_metoffice_simobs_tl" + +! Local variables + integer :: iobs ! Loop variable, observation number + integer :: nlocs ! Number of observations + integer :: ilev ! Loop variable, pressure level number + integer :: iflip ! Index for vertical flip + character(max_string) :: err_msg ! Message to be output + type(ufo_geoval), pointer :: q_d ! Increment to the specific humidity + type(ufo_geoval), pointer :: prs_d ! Increment to the air pressure + real(kind_real), allocatable :: pressure_d(:) ! Increment to the air pressure (monotonic order) + real(kind_real), allocatable :: humidity_d(:) ! Increment to the specific humidity (in pressure monotonic order) + real(kind_real), allocatable :: x_d(:) ! Increment to the complete state + + write(err_msg,*) "TRACE: ufo_groundgnss_metoffice_simobs_tl: begin" + call fckit_log%info(err_msg) + +! Check if trajectory was set + if (.not. self%ltraj) then + write(err_msg,*) myname_, ' trajectory wasnt set!' + call abor1_ftn(err_msg) + endif + +! Check if nlocs is consistent in geovals & hofx + if (geovals%nlocs /= size(hofx)) then + write(err_msg,*) myname_, ' error: nlocs inconsistent!' + call abor1_ftn(err_msg) + endif + +! Get variables from geovals + call ufo_geovals_get_var(geovals, var_q, q_d) ! specific humidity + call ufo_geovals_get_var(geovals, var_prsi, prs_d) ! pressure on rho levels + + nlocs = self % nlocs ! number of observations + + allocate(x_d(1:prs_d%nval+q_d%nval)) + allocate(pressure_d(1:self % nlevp)) + allocate(humidity_d(1:self % nlevq)) + + iflip = self%iflip + +! Loop through the obs, calculating the increment to the observation + obs_loop: do iobs = 1, nlocs ! order of loop doesn't matter + + IF (iflip == 1) THEN + do ilev = 1, self % nlevp + pressure_d(ilev) = prs_d % vals(self % nlevp-ilev+1,iobs) + end do + do ilev = 1, self % nlevq + humidity_d(ilev) = q_d % vals(self % nlevq-ilev+1,iobs) + end do + ELSE + pressure_d(1:self % nlevp) = prs_d % vals(:,iobs) + humidity_d(1:self % nlevq) = q_d % vals(:,iobs) + END IF + + x_d(1:prs_d%nval) = pressure_d + x_d(prs_d%nval+1:prs_d%nval+q_d%nval) = humidity_d + hofx(iobs) = SUM(self % K(iobs,:) * x_d) + + end do obs_loop + + deallocate(x_d) + deallocate(pressure_d) + deallocate(humidity_d) + + write(err_msg,*) "TRACE: ufo_groundgnss_metoffice_simobs_tl: complete" + call fckit_log%info(err_msg) + + return + +end subroutine ufo_groundgnss_metoffice_simobs_tl + + + +! ------------------------------------------------------------------------------ +! Given an increment to the observation, find the equivalent increment to the +! model state +! ------------------------------------------------------------------------------ +subroutine ufo_groundgnss_metoffice_simobs_ad(self, geovals, hofx, obss) + + use typesizes, only: wp => EightByteReal + + implicit none + +! Subroutine arguments + class(ufo_groundgnss_metoffice_tlad), intent(in) :: self ! Object which is being used to transfer information + type(ufo_geovals), intent(inout) :: geovals ! Calculated perturbations to model state + real(kind_real), intent(in) :: hofx(:) ! Increment to the observations + type(c_ptr), value, intent(in) :: obss ! Input - the observations + +! Local parameters + character(len=*), parameter :: myname_="ufo_groundgnss_metoffice_simobs_ad" + +! Local variables + real(c_double) :: missing ! Missing data values + type(ufo_geoval), pointer :: q_d ! Pointer to the specific humidity perturbations + type(ufo_geoval), pointer :: prs_d ! Pointer to the pressure perturbations + integer :: iobs ! Loop variable, observation number + integer :: ilev ! Loop variable, pressure level number + integer :: iflip ! Index for vertical flip + real(kind_real), allocatable :: x_d(:) ! Perturbation to the full model state + real(kind_real), allocatable :: pressure_d(:) ! Perturbation to pressure (monotonic order) + real(kind_real), allocatable :: humidity_d(:) ! Perturbation to specific humidity (in pressure monotonic order) + character(max_string) :: err_msg ! Message to be output + + write(err_msg,*) "TRACE: ufo_groundgnss_metoffice_simobs_ad: begin" + call fckit_log%info(err_msg) + +! Check if trajectory was set + if (.not. self%ltraj) then + write(err_msg,*) myname_, ' trajectory wasnt set!' + call abor1_ftn(err_msg) + endif + +! Check if nlocs is consistent in geovals & hofx + if (geovals%nlocs /= size(hofx)) then + write(err_msg,*) myname_, ' error: nlocs inconsistent!' + call abor1_ftn(err_msg) + endif + +! Get variables from geovals + call ufo_geovals_get_var(geovals, var_q, q_d) ! specific humidity + call ufo_geovals_get_var(geovals, var_prsi, prs_d) ! pressure + +! Allocate the output for the air pressure + if (.not. allocated(prs_d%vals)) then + prs_d % nlocs = self % nlocs + prs_d % nval = self % nlevp + allocate(prs_d%vals(prs_d%nval, prs_d%nlocs)) + prs_d % vals = 0.0_kind_real + endif + +! Allocate the output for the specific humidity + if (.not. allocated(q_d%vals)) then + q_d % nlocs = self % nlocs + q_d % nval = self % nlevq + allocate(q_d%vals(q_d%nval, q_d%nlocs)) + q_d % vals = 0.0_kind_real + endif + + missing = missing_value(missing) + allocate(x_d(1:prs_d%nval + q_d%nval)) + allocate(pressure_d(1:prs_d%nval)) + allocate(humidity_d(1:q_d%nval)) + + iflip = self%iflip + +! Loop through the obs, calculating the increment to the model state + obs_loop: do iobs = 1, self % nlocs + + if (hofx(iobs) /= missing) then + x_d = self % K(iobs,:) * hofx(iobs) + pressure_d(1:prs_d%nval) = x_d(1:prs_d%nval) + humidity_d(1:q_d%nval) = x_d(prs_d%nval+1:prs_d%nval+q_d%nval) + end if + + if (iflip == 1) then + do ilev = 1, self % nlevp + prs_d % vals(self % nlevp-ilev+1,iobs) = pressure_d(ilev) + end do + do ilev = 1, self % nlevq + q_d % vals(self % nlevq-ilev+1,iobs) = humidity_d(ilev) + end do + else + prs_d % vals(:,iobs) = pressure_d(1:self % nlevp) + q_d % vals(:,iobs) = humidity_d(1:self % nlevq) + end if + + + end do obs_loop + + deallocate(x_d) + deallocate(pressure_d) + deallocate(humidity_d) + + write(err_msg,*) "TRACE: ufo_groundgnss_metoffice_simobs_ad: complete" + call fckit_log%info(err_msg) + + return + +end subroutine ufo_groundgnss_metoffice_simobs_ad + + + +!------------------------------------------------------------------------- +! Tidy up the variables that are used for passing information +!------------------------------------------------------------------------- +subroutine ufo_groundgnss_metoffice_tlad_delete(self) + + implicit none + class(ufo_groundgnss_metoffice_tlad), intent(inout) :: self + character(len=*), parameter :: myname_="ufo_groundgnss_metoffice_tlad_delete" + + self%nlocs = 0 + self%nlevp = 0 + self%nlevq = 0 + if (allocated(self%K)) deallocate(self%K) + self%ltraj = .false. + +end subroutine ufo_groundgnss_metoffice_tlad_delete + + +!------------------------------------------------------------------------- +! Interface for calculating the K-matrix for calculating TL/AD +!------------------------------------------------------------------------- +SUBROUTINE groundgnss_jacobian_interface(nlevp, & + nlevq, & + za, & + zb, & + q, & + prs, & + zStation, & + iobs, & + gbgnss_min_temp_grad, & + K) + +IMPLICIT NONE + +INTEGER, INTENT(IN) :: nlevP ! The number of model pressure levels +INTEGER, INTENT(IN) :: nlevq ! The number of model theta levels +REAL(kind_real), INTENT(IN) :: za(:) ! The geometric height of the model pressure levels +REAL(kind_real), INTENT(IN) :: zb(:) ! The geometric height of the model theta levels +REAL(kind_real), INTENT(IN) :: q(1:nlevq) ! The model values that are being perturbed +REAL(kind_real), INTENT(IN) :: prs(1:nlevP) ! The model values that are being perturbed +REAL(kind_real), INTENT(IN) :: zStation ! Station height +REAL(kind_real), INTENT(IN) :: gbgnss_min_temp_grad ! The minimum temperature gradient which is used +INTEGER, INTENT(IN) :: iobs ! Ob number + +REAL(kind_real), INTENT(INOUT) :: K(:,:) ! The calculated K matrix +! +! Things that may need to be output, as they are used by the TL/AD calculation +! + +REAL(kind_real) :: T(1:nlevq) ! Temperature on model levels +REAL(kind_real), ALLOCATABLE :: refrac(:) ! model refractivity on theta levels +! +! Local variables +! +INTEGER :: nstate ! Number of levels in state vector +REAL(kind_real) :: x(1:nlevp+nlevq) ! state vector +LOGICAL :: refracerr ! Whether we encountered an error in calculating the refractivity +CHARACTER(LEN=200) :: err_msg ! Output message + +REAL(kind_real) :: pN(nlevq) ! Presure on theta levels + +REAL(kind_real), ALLOCATABLE :: model_heights(:) ! Geopotential heights of the refractivity levels (not needed for this oper) +INTEGER :: nRefLevels ! Number of levels in refractivity calculation + +! Set up the size of the state +nstate = nlevP + nlevq + +refracerr = .FALSE. + +! Calculate the refractivity +CALL ufo_calculate_refractivity(nlevp, & + nlevq, & + za, & + zb, & + prs, & + q, & + .TRUE., & ! vert_interp_ops + .FALSE., & ! pseudo_ops + gbgnss_min_temp_grad, & + refracerr, & + nRefLevels, & + refrac, & + model_heights) + +IF (.NOT. refracerr) THEN + ! Calculate the K-matrix (Jacobian) + CALL Groundgnss_GetK(nstate, & + nlevP, & + nlevq, & + za, & + zb, & + prs, & + q, & + zStation, & + iobs, & + gbgnss_min_temp_grad, & + refracerr, & + refrac, & + K) +ELSE + K = 0 + write(err_msg,*) "Error in refractivity calculation" + CALL fckit_log % warning(err_msg) +END IF + + +END SUBROUTINE groundgnss_jacobian_interface + + + +!------------------------------------------------------------------------- +! Calculate the K-matrix (Jacobian) +!------------------------------------------------------------------------- +SUBROUTINE Groundgnss_GetK(nstate, & + nlevP, & + nlevq, & + za, & + zb, & + P, & + q, & + zStation, & + iobs, & + gbgnss_min_temp_grad, & + refracerr, & + refrac, & + K) + +! +! Return the K-matrix for calculating TL/AD +! +IMPLICIT NONE + +INTEGER, INTENT(IN) :: nstate +INTEGER, INTENT(IN) :: nlevP ! The number of model pressure levels +INTEGER, INTENT(IN) :: nlevq ! The number of model theta levels +REAL(kind_real), INTENT(IN) :: za(:) ! The geometric height of the model pressure levels +REAL(kind_real), INTENT(IN) :: zb(:) ! The geometric height of the model theta levels +REAL(kind_real), INTENT(IN) :: P(:) ! The model pressure values +REAL(kind_real), INTENT(IN) :: q(:) ! The model humidity values +REAL(kind_real), INTENT(IN) :: zStation ! Station height +INTEGER, INTENT(IN) :: iobs ! Ob number +REAL(kind_real), INTENT(IN) :: gbgnss_min_temp_grad ! The minimum temperature gradient which is used +LOGICAL, INTENT(INOUT) :: refracerr ! Whether we encountered an error in calculating the refractivity +REAL(kind_real), INTENT(IN) :: refrac(:) ! Model refractivity on theta levels - returned from forward model +REAL(kind_real), INTENT(INOUT) :: K(:,:) ! The calculated K matrix + +REAL(kind_real), ALLOCATABLE :: dref_dP(:, :) ! Partial derivative of refractivity wrt. pressure +REAL(kind_real), ALLOCATABLE :: dref_dq(:, :) ! Partial derivative of refractivity wrt. specific humidity + + +! Local constants + +CHARACTER (LEN=*), PARAMETER :: RoutineName = "Groundgnss_GetK" +REAL, PARAMETER :: refrac_scale = 1.0E-6 ! Conversion factor between refractivity and refractive index + +! Local variables + +INTEGER :: Level ! Used for iteration over levels +INTEGER :: Lowest_Level ! Lowest height level +INTEGER :: FirstNeg ! First negative + +REAL(kind_real) :: p_local(nlevp) ! pressure on rho levels (with no negative pressures) +REAL(kind_real) :: pN(nlevq) ! pressure on theta levels +REAL(kind_real) :: LocalZenithDelay ! Zenith Total Delay +REAL(kind_real) :: h_diff, station_diff ! Height diff, station height diff +REAL(kind_real) :: c_rep ! 1/scale height +REAL(kind_real) :: const, c, term1, term2 ! constant, scale height, +REAL(kind_real) :: StationRefrac ! Refraction at station height +REAL(kind_real) :: z_weight1 ! For linear interpolation +REAL(kind_real) :: z_weight2 ! For linear interpolation +REAL(kind_real) :: dc_dref ! derivative of scale height wrt refrac term1 +REAL(kind_real) :: dc_dref2 ! derivative of scale height wrt refrac term2 +REAL(kind_real) :: dztd_dc ! derivative of ZTD wrt scale height +REAL(kind_real) :: drefsta_dref ! derivative of station refrac wrt refrac +REAL(kind_real) :: drefsta_dc ! derivative of station refrac wrt scale height +REAL(kind_real) :: dztd_drefsta ! derivative of ZTD wrt station refrac +REAL(kind_real), ALLOCATABLE :: dp_local_dPin(:,:) ! derivative of pressure rho levels wrt pressure +REAL(kind_real), ALLOCATABLE :: dztd_dpN(:) ! array for derivative w.r.t top theta level +REAL(kind_real) :: dztd_dq(nlevq) ! The calculated K matrix +REAL(kind_real) :: dztd_dp(nlevP) ! The calculated K matrix +REAL(kind_real), ALLOCATABLE :: dPb_dP(:,:) ! derivative of pressure theta wrt pressure +REAL(kind_real), ALLOCATABLE :: dztd_dref(:) ! derivative of ZTD wrt refrac +REAL(kind_real), ALLOCATABLE :: x1(:,:) ! Matrix placeholder +REAL(kind_real), ALLOCATABLE :: x2(:) ! Matrix placeholder + +REAL(kind_real), PARAMETER :: hpa_to_pa = 100.0 ! hPa to Pascal conversion +REAL(kind_real), PARAMETER :: PressScale = 6000.0 ! Pressure scale height +CHARACTER(LEN=200) :: err_msg ! Output message +integer, parameter :: max_string = 800 +character(max_string) :: message ! General message for output + +!---------------------------------------------------------------------- + +!------------------------------------------------------- +! 0. Initialise variables +!------------------------------------------------------- + +ALLOCATE (dref_dP(nlevq,nlevp)) +ALLOCATE (dref_dq(nlevq,nlevq)) +ALLOCATE (dPb_dP(nlevq,nlevp)) +ALLOCATE (dztd_dref(nlevq)) +ALLOCATE (dztd_dpN(nlevq)) +ALLOCATE (x2(nlevp)) +ALLOCATE (x1(nlevq,nlevp)) +ALLOCATE (dp_local_dPin(nlevp,nlevp)) + +! Initialise matrices + +dref_dq(:,:) = 0.0 +dref_dP(:,:) = 0.0 +dztd_dref(:) = 0.0 +dztd_dpN(:) = 0.0 +dPb_dP(:,:) = 0.0 +dztd_dq(:) = 0.0 +dztd_dp(:) = 0.0 +x1(:,:) = 0.0 +x2(:) = 0.0 +dp_local_dpin(:,:) = 0.0 +p_local(:) = 0.0 +pN(:) = 0.0 + +LocalZenithDelay = 0.0 +StationRefrac = 0.0 + +! If negative pressures exist, replace these +! and any above with values calculated as an exponential decay (scale height +! = 6km) from the highest positive pressure. + +FirstNeg = 0 +DO Level=1, nlevp + IF (Level==1) THEN + p_local(Level) = P(Level) + dp_local_dPin(Level,Level) = 1.0 + ELSE + IF ((P(Level) <= 0.0 .OR. (FirstNeg /= 0 .AND. Level > FirstNeg)) .OR. & + (P(Level) > P(Level-1))) THEN + + IF (FirstNeg == 0) FirstNeg = Level + + p_local(Level) = P(FirstNeg-1) * EXP(-(za(Level) - za(FirstNeg-1)) / PressScale) + dp_local_dPin(Level,FirstNeg-1) = p_local(Level) / P(FirstNeg-1) + + ELSE + p_local(Level) = P(Level) + dp_local_dPin(Level,Level) = 1.0 + END IF + END IF +END DO + +! Calculate Pressure on theta +! Assume ln(p) linear with height + +DO Level = 1, nlevp-1 + + z_weight1 = (za(Level+1) - zb(Level)) / (za(Level+1) - za(Level)) + z_weight2 = 1.0 - z_weight1 + + pN(Level) = EXP(z_weight1 * LOG(p_local(Level)) + z_weight2 * LOG(p_local(Level+1))) + + dPb_dP(Level,Level) = pN(Level) * z_weight1 / p_local(Level) + dPb_dP(Level,Level+1) = pN(Level) * z_weight2 / p_local(Level+1) + +END DO + +! Calculate the gradient of ref wrt p (on rho levels) and q (on theta levels) + +CALL ufo_refractivity_kmat (nlevP, & + nlevq, & + nlevq, & + za, & + zb, & + P, & + q, & + .False., & !pseudo_ops + .TRUE., & ! vert_interp_ops + gbgnss_min_temp_grad, & + dref_dP, & + dref_dq, & + dPb_dP) + + + +! In Layer where station height lies, define lowest level required for +! iteration and integration + +DO Level = 1, nlevp-1 + IF (zb(Level) > zStation) THEN + Lowest_Level = Level + EXIT + END IF +END DO + +DO Level = Lowest_Level, nlevq-1 + IF (refrac(Level+1) / refrac(Level) <= 0.0) THEN + + write(err_msg,*) "Refractivity error. Refractivity < 0.0" + CALL fckit_log % warning(err_msg) + + RETURN + + END IF +END DO + +!--------------------------- +! 3. Calculate Zenith delays +!--------------------------- + +! Start at bottom level + +DO Level = Lowest_Level, nlevq + + LocalZenithDelay = 0.0 + + IF (Level == Lowest_Level .AND. Level /= 1) THEN + + ! If station lies above the lowest model level, interpolate refractivity + ! to station height + h_diff = zb(Level - 1) - zb(Level) + station_diff = zStation - zb(Level) + c = (LOG (refrac(Level) / refrac(Level - 1))) / h_diff + StationRefrac = refrac(Level - 1) * EXP(-c * (zStation-zb(Level - 1))) + const = (-StationRefrac / c) * EXP(c * zStation) + term1 = EXP(-c * (zb(Level))) + term2 = EXP(-c * zStation) + LocalZenithDelay = refrac_scale * const * (term1 - term2) + + c_rep = 1.0 / c + dztd_dc = (-refrac_scale * StationRefrac / c) * & + (c_rep + EXP(c * station_diff) * (station_diff - c_rep)) + dztd_dc = dztd_dc - (station_diff * LocalZenithDelay) + dc_dref = -1.0 / (refrac(Level - 1) * h_diff) + dc_dref2 = 1.0 / (refrac(Level) * h_diff) + drefsta_dref = StationRefrac / refrac(Level - 1) + drefsta_dc = -(zStation - zb(Level - 1)) * StationRefrac + drefsta_dref = drefsta_dref + drefsta_dc * dc_dref + dztd_drefsta = LocalZenithDelay / StationRefrac + + dztd_dref(Level-1) = dztd_drefsta * drefsta_dref + dztd_dc * dc_dref + dztd_dref(Level) = dztd_dc * dc_dref2 + + ELSE IF (Level == 1) THEN + + ! If station lies below model level 1 (ie. the lowest level for which refractivity is + ! calculated), then use c from the first full layer, but integrate down to height of + ! station + + h_diff = zb(Level) - zb(Level + 1) + c = (LOG (refrac(Level + 1) / refrac(Level))) / h_diff + const = (-refrac(Level) / c) * EXP(c * (zb(Level))) + term1 = EXP(-c * (zb(Level + 1))) + term2 = EXP(-c * zStation) + LocalZenithDelay = refrac_scale * const * (term1 - term2) + + c_rep = 1.0 / c + dztd_dc = (-refrac_scale * refrac(Level) / c) * & + (c_rep + EXP(c * h_diff) * (h_diff - c_rep)) + dc_dref = -1.0 / (refrac(Level) * h_diff) + dc_dref2 = 1.0 / (refrac(Level + 1) * h_diff) + + dztd_dref(Level) = LocalZenithDelay / refrac(Level) + dztd_dref(Level) = dztd_dref(Level) + dztd_dc * dc_dref + dztd_dref(Level+1)= dztd_dc * dc_dref2 + + ELSE IF (Level <= nlevq .AND. Level > 2 .AND. Level > Lowest_Level) THEN + + ! For the other Levels in the column above the station. + + h_diff = zb(Level - 1) - zb(Level) + c = (LOG (refrac(Level) / refrac(Level-1))) / h_diff + const = (-refrac(Level - 1) / c) * EXP(c * (zb(Level - 1))) + term1 = EXP(-c * (zb(Level))) + term2 = EXP(-c * (zb(Level - 1))) + LocalZenithDelay = refrac_scale * const * (term1 - term2) + + c_rep = 1.0 / c + dztd_dc = (-refrac_scale * refrac(Level - 1) / c) * (c_rep + EXP(c * h_diff) * (h_diff - c_rep)) + dc_dref = -1.0 / (refrac(Level - 1) * h_diff) + dc_dref2 = 1.0 / (refrac(Level) * h_diff) + + dztd_dref(Level-1) = dztd_dref(Level - 1) + LocalZenithDelay / refrac(Level - 1) + dztd_dc * dc_dref + dztd_dref(Level) = dztd_dc * dc_dref2 + + END IF + +END DO + +!------------------------------------------- +! Construct K Matrices +!------------------------------------------- + +! Account for negative pressure +dref_dP = MATMUL(dref_dP,dp_local_dPin) + +dztd_dq(:) = MATMUL(dztd_dref,dref_dq) +dztd_dp(:) = MATMUL(dztd_dref,dref_dP) + +! Account for negative pressure +dztd_dp(:) = MATMUL(dztd_dp,dp_local_dPin) + +! First add in dZTD/dp for the top correction, which only depends on top level theta pressure + +dztd_dpN(nlevq) = refrac_scale * n_alpha * rd / (hpa_to_pa * grav) +x1 = MATMUL(dPb_dP, dp_local_dPin) +x2 = MATMUL(dztd_dpN, x1) +dztd_dp = x2 + dztd_dp + +K(iobs, 1:nlevp) = dztd_dp +K(iobs, nlevp+1:nstate) = dztd_dq + +DEALLOCATE (dp_local_dPin) +DEALLOCATE (x1) +DEALLOCATE (x2) +DEALLOCATE (dztd_dpN) +DEALLOCATE (dztd_dref) +DEALLOCATE (dPb_dP) +DEALLOCATE (dref_dq) +DEALLOCATE (dref_dP) + +END SUBROUTINE Groundgnss_GetK + +end module ufo_groundgnss_metoffice_tlad_mod + diff --git a/src/ufo/groundgnss/ufo_groundgnss_metoffice_utils_mod.F90 b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ufo_groundgnss_metoffice_utils_mod.F90 similarity index 56% rename from src/ufo/groundgnss/ufo_groundgnss_metoffice_utils_mod.F90 rename to src/ufo/groundgnss/ZenithTotalDelayMetOffice/ufo_groundgnss_metoffice_utils_mod.F90 index 8f1e7c686..c8e03682e 100644 --- a/src/ufo/groundgnss/ufo_groundgnss_metoffice_utils_mod.F90 +++ b/src/ufo/groundgnss/ZenithTotalDelayMetOffice/ufo_groundgnss_metoffice_utils_mod.F90 @@ -14,9 +14,9 @@ module ufo_groundgnss_ukmo_utils_mod ! Generic routines from elsewhere in jedi use missing_values_mod use ufo_constants_mod, only: & - rd, & ! Gas constant for dry air - grav, & ! Gravitational field strength - n_alpha, & ! Refractivity constant a + rd, & ! Gas constant for dry air + grav, & ! Gravitational field strength + n_alpha, & ! Refractivity constant a n_beta ! Refractivity constant b implicit none @@ -30,29 +30,29 @@ module ufo_groundgnss_ukmo_utils_mod !------------------------------------------------------------------------------- ! Ground based GNSS Observation operator !------------------------------------------------------------------------------- -SUBROUTINE Ops_Groundgnss_ZTD (nlevq, & - refrac, & - zb, & +SUBROUTINE Ops_Groundgnss_ZTD (nlevq, & + refrac, & + zb, & zStation, & Model_ZTD) IMPLICIT NONE - INTEGER, INTENT(IN) :: nlevq ! no. of theta levels - REAL(kind_real), INTENT(IN) :: refrac(:) - REAL(kind_real), INTENT(IN) :: zb(:) - REAL(kind_real), INTENT(IN) :: zStation - REAL(kind_real), INTENT(INOUT) :: Model_ZTD + INTEGER, INTENT(IN) :: nlevq ! no. of temperature/theta levels + REAL(kind_real), INTENT(IN) :: refrac(:) ! refractivty + REAL(kind_real), INTENT(IN) :: zb(:) ! The geometric height of the model theta levels + REAL(kind_real), INTENT(IN) :: zStation ! Station heights + REAL(kind_real), INTENT(INOUT) :: Model_ZTD ! Model background ZTD - REAL(kind_real) :: LocalZenithDelay - INTEGER :: Level - REAL(kind_real) :: StationRefrac - REAL(kind_real) :: c - REAL(kind_real) :: const - REAL(kind_real) :: term1 - REAL(kind_real) :: term2 - INTEGER :: Lowest_Level + REAL(kind_real) :: LocalZenithDelay ! Zenith total delay + INTEGER :: Level ! level iterator + REAL(kind_real) :: StationRefrac ! refrac at station + REAL(kind_real) :: c ! scale height + REAL(kind_real) :: const ! refrac constant + REAL(kind_real) :: term1 ! refrac term1 + REAL(kind_real) :: term2 ! refrac term2 + INTEGER :: Lowest_Level ! lowest height level !------------------------------------------------------------ ! Calculate the zenith delay for each layer and add to total @@ -91,7 +91,7 @@ SUBROUTINE Ops_Groundgnss_ZTD (nlevq, & ! calculated, then use c from the first full layer, but integrate down to height of ! station - c = (LOG (Refrac(Level + 1) / Refrac(Level))) / (zb(Level) - zb(Level + 1)) + c = (LOG (refrac(Level + 1) / refrac(Level))) / (zb(Level) - zb(Level + 1)) const = -refrac(Level) / c * EXP (c * (zb(Level))) term1 = EXP (-c * (zb(Level + 1))) term2 = EXP (-c * ZStation) @@ -114,18 +114,37 @@ SUBROUTINE Ops_Groundgnss_ZTD (nlevq, & END SUBROUTINE Ops_Groundgnss_ZTD -SUBROUTINE Ops_groundgnss_TopCorrection(pN, & +SUBROUTINE Ops_groundgnss_TopCorrection(P, & nlevq, & + za, & + zb, & TopCorrection) IMPLICIT NONE - REAL(kind_real), INTENT(IN) :: pN(:) - INTEGER, INTENT(IN) :: nlevq - REAL(kind_real), INTENT(INOUT) :: TopCorrection + REAL(kind_real), INTENT(IN) :: P(:) ! Pressure on pressure (rho) levels + INTEGER, INTENT(IN) :: nlevq ! no. of temperature/theta levels + REAL(kind_real), INTENT(IN) :: za(:) ! heights of pressure (rho) levels + REAL(kind_real), INTENT(IN) :: zb(:) ! Heights of temperature/theta levels + REAL(kind_real), INTENT(INOUT) :: TopCorrection ! ZTD Top of atmos correction + + + INTEGER :: Level ! Loop counter + REAL(kind_real) :: pN(nlevq) ! Pressure on theta levels + REAL(kind_real) :: pwt1 ! Weighting variable + REAL(kind_real) :: pwt2 ! Weighting variable + REAL(kind_real) :: TCconstant ! Top correction constant + REAL(kind_real), PARAMETER :: hpa_to_pa = 100.0 ! hPa to Pascal conversion + + + DO Level = 1, nlevq + + pwt1 = (za(Level + 1) - zb(Level)) / (za(Level + 1) - za(Level)) + pwt2 = 1.0 - pwt1 - REAL(kind_real) :: TCconstant - REAL(kind_real), PARAMETER :: hpa_to_pa = 100.0 + pN(Level) = EXP (pwt1 * LOG (P(Level)) + pwt2 * LOG (P(Level + 1))) + + END DO TCconstant = (refrac_scale * n_alpha * rd)/ (hpa_to_pa * grav) diff --git a/src/ufo/groundgnss/ZenithTotalDelayROPP/CMakeLists.txt b/src/ufo/groundgnss/ZenithTotalDelayROPP/CMakeLists.txt new file mode 100644 index 000000000..a04d6061b --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayROPP/CMakeLists.txt @@ -0,0 +1,24 @@ +# (C) Copyright 2017-2018 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +if( ${ROPP-UFO_FOUND} ) +set ( groundgnssropp_src_files + ObsGroundgnssROPP.h + ObsGroundgnssROPP.cc + ObsGroundgnssROPP.interface.h + ObsGroundgnssROPP.interface.F90 + ufo_groundgnss_ropp_mod.F90 + ufo_groundgnss_ropp_utils_mod.F90 +PARENT_SCOPE +) +else( ${ROPP-UFO_FOUND} ) +set ( groundgnssropp_src_files + ObsGroundgnssROPP.h + ObsGroundgnssROPP.cc + ObsGroundgnssROPP.interface.h + ObsGroundgnssROPP.interface.F90 + ufo_groundgnss_ropp_mod_stub.F90 +PARENT_SCOPE +) +endif( ${ROPP-UFO_FOUND} ) diff --git a/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.cc b/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.cc new file mode 100644 index 000000000..c053c2cb6 --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.cc @@ -0,0 +1,63 @@ +/* + * (C) Copyright 2017-2018 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.h" + +#include +#include +#include + +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsDiagnostics.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static ObsOperatorMaker makerGroundgnssROPP_("GroundgnssROPP"); +// ----------------------------------------------------------------------------- + +ObsGroundgnssROPP::ObsGroundgnssROPP(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : ObsOperatorBase(odb, config), keyOperGroundgnssROPP_(0), odb_(odb), varin_() +{ + const std::vector vv{"air_temperature", "specific_humidity", "air_pressure", + "geopotential_height", "surface_altitude"}; + varin_.reset(new oops::Variables(vv)); + + ufo_groundgnss_ropp_setup_f90(keyOperGroundgnssROPP_, config); + oops::Log::trace() << "ObsGroundgnssROPP created." << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsGroundgnssROPP::~ObsGroundgnssROPP() { + ufo_groundgnss_ropp_delete_f90(keyOperGroundgnssROPP_); + oops::Log::trace() << "ObsGroundgnssROPP destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsGroundgnssROPP::simulateObs(const GeoVaLs & gom, ioda::ObsVector & ovec, + ObsDiagnostics &) const { + ufo_groundgnss_ropp_simobs_f90(keyOperGroundgnssROPP_, gom.toFortran(), odb_, + ovec.size(), ovec.toFortran()); +} + +// ----------------------------------------------------------------------------- + +void ObsGroundgnssROPP::print(std::ostream & os) const { + os << "ObsGroundgnssROPP::print not implemented"; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.h b/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.h new file mode 100644 index 000000000..1acb62bdc --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.h @@ -0,0 +1,64 @@ +/* + * (C) Copyright 2017-2018 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_GROUNDGNSS_ZENITHTOTALDELAYROPP_OBSGROUNDGNSSROPP_H_ +#define UFO_GROUNDGNSS_ZENITHTOTALDELAYROPP_OBSGROUNDGNSSROPP_H_ + +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.interface.h" +#include "ufo/ObsOperatorBase.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsDiagnostics; + +// ----------------------------------------------------------------------------- + +/// GroundgnssROPP observation operator +class ObsGroundgnssROPP : public ObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsGroundgnssROPP";} + + ObsGroundgnssROPP(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsGroundgnssROPP(); + +// Obs Operator + void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; + +// Other + const oops::Variables & requiredVars() const override {return *varin_;} + + int & toFortran() {return keyOperGroundgnssROPP_;} + const int & toFortran() const {return keyOperGroundgnssROPP_;} + + private: + void print(std::ostream &) const override; + F90hop keyOperGroundgnssROPP_; + const ioda::ObsSpace& odb_; + std::unique_ptr varin_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_GROUNDGNSS_ZENITHTOTALDELAYROPP_OBSGROUNDGNSSROPP_H_ diff --git a/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.interface.F90 b/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.interface.F90 new file mode 100644 index 000000000..0c3e4cbe4 --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.interface.F90 @@ -0,0 +1,79 @@ +! (C) Copyright 2017 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to handle ground-based gnss observations ROPP operator + +module ufo_groundgnss_ropp_mod_c + + use fckit_configuration_module, only: fckit_configuration + use iso_c_binding + use ufo_groundgnss_ropp_mod + + implicit none + private + +#define LISTED_TYPE ufo_groundgnss_ROPP + + !> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + + !> Global registry + type(registry_t) :: ufo_groundgnss_ROPP_registry + + ! ------------------------------------------------------------------------------ +contains + ! ------------------------------------------------------------------------------ + !> Linked list implementation +#include "oops/util/linkedList_c.f" + +! ------------------------------------------------------------------------------ + +subroutine ufo_groundgnss_ropp_setup_c(c_key_self, c_conf) bind(c,name='ufo_groundgnss_ropp_setup_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), value, intent(in) :: c_conf + +type(ufo_groundgnss_ROPP), pointer :: self +type(fckit_configuration) :: f_conf + +call ufo_groundgnss_ROPP_registry%setup(c_key_self, self) +f_conf = fckit_configuration(c_conf) + +end subroutine ufo_groundgnss_ROPP_setup_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_groundgnss_ropp_delete_c(c_key_self) bind(c,name='ufo_groundgnss_ropp_delete_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self + +type(ufo_groundgnss_ROPP), pointer :: self + +call ufo_groundgnss_ROPP_registry%delete(c_key_self,self) + +end subroutine ufo_groundgnss_ropp_delete_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_groundgnss_ropp_simobs_c(c_key_self, c_key_geovals, c_obsspace, c_nobs, c_hofx) & + bind(c,name='ufo_groundgnss_ropp_simobs_f90') + +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nobs +real(c_double), intent(inout) :: c_hofx(c_nobs) +type(ufo_groundgnss_ROPP), pointer :: self + +character(len=*), parameter :: myname_="ufo_groundgnss_ropp_simobs_c" +call ufo_groundgnss_ROPP_registry%get(c_key_self, self) +call self%opr_simobs(c_key_geovals, c_obsspace, c_hofx) + +end subroutine ufo_groundgnss_ropp_simobs_c + +! ------------------------------------------------------------------------------ + +end module ufo_groundgnss_ropp_mod_c diff --git a/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.interface.h b/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.interface.h new file mode 100644 index 000000000..19a1c2372 --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayROPP/ObsGroundgnssROPP.interface.h @@ -0,0 +1,35 @@ +/* + * (C) Copyright 2017 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_GROUNDGNSS_ZENITHTOTALDELAYROPP_OBSGROUNDGNSSROPP_INTERFACE_H_ +#define UFO_GROUNDGNSS_ZENITHTOTALDELAYROPP_OBSGROUNDGNSSROPP_INTERFACE_H_ + +#include "ioda/ObsSpace.h" +#include "ufo/Fortran.h" + +namespace ufo { + +/// Interface to Fortran UFO routines +/*! + * The core of the UFO is coded in Fortran. + * Here we define the interfaces to the Fortran code. + */ + +extern "C" { +// ----------------------------------------------------------------------------- +// Ground based GNSS observation operators - (ROPP) +// ----------------------------------------------------------------------------- + void ufo_groundgnss_ropp_setup_f90(F90hop &, const eckit::Configuration &); + void ufo_groundgnss_ropp_delete_f90(F90hop &); + void ufo_groundgnss_ropp_simobs_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + const int &, double &); +// ----------------------------------------------------------------------------- + +} // extern C + +} // namespace ufo +#endif // UFO_GROUNDGNSS_ZENITHTOTALDELAYROPP_OBSGROUNDGNSSROPP_INTERFACE_H_ diff --git a/src/ufo/groundgnss/ZenithTotalDelayROPP/ufo_groundgnss_ropp_mod.F90 b/src/ufo/groundgnss/ZenithTotalDelayROPP/ufo_groundgnss_ropp_mod.F90 new file mode 100644 index 000000000..e852cd9a9 --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayROPP/ufo_groundgnss_ropp_mod.F90 @@ -0,0 +1,207 @@ +! (C) Copyright 2017-2018 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module for ground-based gnss refractivity ropp forward operator +!> following the ROPP (2018 Aug) implementation + +module ufo_groundgnss_ropp_mod + +use iso_c_binding +use kinds +use ufo_vars_mod +use ufo_geovals_mod +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_basis_mod, only: ufo_basis +use obsspace_mod +use missing_values_mod +use ufo_groundgnss_ropp_utils_mod +use fckit_log_module, only : fckit_log + +implicit none +public :: ufo_groundgnss_ropp +private + + !> Fortran derived type for ground based gnss trajectory +type, extends(ufo_basis) :: ufo_groundgnss_ROPP + contains + procedure :: simobs => ufo_groundgnss_ropp_simobs +end type ufo_groundgnss_ROPP + +contains + +! ------------------------------------------------------------------------------ +! Description: +! simulate the zenith total delay point observation +! this is done by integrating refractivities +! computed by the ROPP forward operator +! +! Inputs: +! obsLat latitude +! obsLon longitude +! obsHeight station height +! +! t temperature +! q specific humidity +! prs pressure +! gph geopotential height +! z_sfc surface geometric height +! +! Outputs: +! model_ztd simulated zenith total delay (put into HofX) +! +! ------------------------------------------------------------------------------ +subroutine ufo_groundgnss_ropp_simobs(self, geovals, hofx, obss) + use ropp_fm_types, only: State1dFM + use ropp_fm_types, only: Obs1dRefrac + use typesizes, only: wp => EightByteReal + use datetimetypes, only: dp + + implicit none + class(ufo_groundgnss_ROPP), intent(in) :: self + type(ufo_geovals), intent(in) :: geovals + real(kind_real), intent(inout) :: hofx(:) + type(c_ptr), value, intent(in) :: obss + real(c_double) :: missingDouble + + type(State1dFM) :: x ! ROPP 1d state vector data type + type(Obs1dRefrac) :: y ! ROPP 1d Refractivity "observations" vector data type + + character(len=*), parameter :: myname_="ufo_groundgnss_ropp_simobs" + real(kind=dp) :: ob_time + integer, parameter :: max_string = 800 + + character(max_string) :: err_msg ! error message + integer :: nlev, nobs, iobs ! number of model level, observations, and counter + integer, allocatable, dimension(:) :: ichk ! quality indicator for observation + type(ufo_geoval), pointer :: t, q, prs, gph, z_sfc ! model state variables + real(kind_real), allocatable :: obsLat(:), obsLon(:) ! observation latitude and longitude + real(kind_real), allocatable :: obsHeight(:), obsValue(:) ! observation station height and value + real(kind_real), allocatable :: model_z(:) ! model geometric height + real(kind_real) :: station_phi ! observation geopotential height + real(kind_real) :: model_ztd ! simulated observation (zenith total delay) + integer :: iflip ! monotonic increasing height + logical :: l_linear ! indicator of refractivity interpolation method + write(err_msg,*) "TRACE: ufo_groundgnss_ropp_simobs: begin" + call fckit_log%info(err_msg) + +! check if nlocs is consistent in geovals & hofx + if (geovals%nlocs /= size(hofx)) then + write(err_msg,*) myname_, ' error: nlocs inconsistent!' + call abor1_ftn(err_msg) + endif + +! get variables from geovals + call ufo_geovals_get_var(geovals, var_ts, t) ! temperature + call ufo_geovals_get_var(geovals, var_q, q) ! specific humidity + call ufo_geovals_get_var(geovals, var_prs, prs) ! pressure + call ufo_geovals_get_var(geovals, var_z, gph) ! geopotential height + call ufo_geovals_get_var(geovals, var_sfc_geomz, z_sfc) ! surface geometric height + + missingDouble = missing_value(missingDouble) + + nlev = t%nval ! number of model levels + nobs = obsspace_get_nlocs(obss) + + if (nobs > 0) then + iflip = 0 + if (prs%vals(1,1) .lt. prs%vals(prs%nval,1) ) then + iflip = 1 + write(err_msg,'(a)') ' ufo_groundgnss_ropp_simobs:'//new_line('a')// & + ' Model vertical height profile is in descending order,'//new_line('a')// & + ' but ROPP requires it to be ascending order, need flip' + call fckit_log%info(err_msg) + end if + + ! set obs space struture + allocate(obsLon(nobs)) + allocate(obsLat(nobs)) + allocate(obsHeight(nobs)) + allocate(obsValue(nobs)) + + ! create array for model geometric heights + allocate(model_z(nlev)) + model_z(:) = 0.0 + + call obsspace_get_db(obss, "MetaData", "longitude", obsLon) + call obsspace_get_db(obss, "MetaData", "latitude", obsLat) + call obsspace_get_db(obss, "MetaData", "station_height", obsHeight) + call obsspace_get_db(obss, "ObsValue", "ZTD", obsValue) +! obsValue = 0.0 + + allocate(ichk(nlev)) + ichk(:) = 0 ! this will hold QC values for observation from QC flags + + write(err_msg,*) "TRACE: ufo_groundgnss_ropp_simobs: begin observation loop, nobs = ", nobs + call fckit_log%info(err_msg) ! always print + + obs_loop: do iobs = 1, nobs + + ob_time = 0.0 + l_linear = .False. + call init_ropp_1d_statevec(ob_time, & + obsLon(iobs), & + obsLat(iobs), & + t%vals(:,iobs), & + q%vals(:,iobs), & + prs%vals(:,iobs), & + gph%vals(:,iobs), & + nlev, & + z_sfc%vals(1,iobs), & + x, iflip) + + call calc_station_phi(obsLat(iobs), obsHeight(iobs), station_phi) + call init_ropp_1d_obvec(nlev, & + ichk, ob_time, & + obsLat(iobs), & + obsLon(iobs), & + station_phi, & + x, y) + + call ropp_fm_refrac_1d(x,y) + + call calc_model_z(nlev, obsLat(iobs), y%geop, model_z) + ! note the scaling of the refractivity by 1.e-6 is done in subroutine after integral + call gnss_ztd_integral(nlev, y%refrac, model_z, obsHeight(iobs), model_ztd, l_linear) + ! add error trap ! model_ztd initialized to 0 in integral if 0 is returned something very wrong + if ( model_ztd == 0.0 ) then + model_ztd = missingDouble + end if + + ! matching a print format used in initialization of obvec + if ( iobs == 1 ) then + write(err_msg,'(a9,2a11,2a16,2a15)') 'ROPPsim: ', 'lat', 'lon', & + 'ob', 'bk', 'station_hgt', 'model_terr' + call fckit_log%debug(err_msg) ! print when MAIN_DEBUG=1 + end if + write(err_msg,'(9x,2f11.3,2f16.6,2f15.3)') & + obsLat(iobs), & + obsLon(iobs), & + obsValue(iobs), & + model_ztd, & + obsHeight(iobs), & + model_z(1) + call fckit_log%debug(err_msg) ! print when MAIN_DEBUG=1 + + hofx(iobs) = model_ztd + + ! hack -- handling ropp missing value + call ropp_tidy_up_1d(x,y) + + end do obs_loop + + deallocate(ichk) + deallocate(obsLat) + deallocate(obsLon) + deallocate(obsHeight) + deallocate(obsValue) + end if ! nobs > 0 + + write(err_msg,*) "TRACE: ufo_groundgnss_ropp_simobs: completed" + call fckit_log%info(err_msg) + +end subroutine ufo_groundgnss_ropp_simobs +! ------------------------------------------------------------------------------ + +end module ufo_groundgnss_ropp_mod diff --git a/src/ufo/groundgnss/ZenithTotalDelayROPP/ufo_groundgnss_ropp_mod_stub.F90 b/src/ufo/groundgnss/ZenithTotalDelayROPP/ufo_groundgnss_ropp_mod_stub.F90 new file mode 100644 index 000000000..503aad106 --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayROPP/ufo_groundgnss_ropp_mod_stub.F90 @@ -0,0 +1,71 @@ +! (C) Copyright 2017-2018 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!>Stubbed Fortran module for ground based gnss ropp1d forward operator +!> following the ROPP (2018 Aug) implementation + +module ufo_groundgnss_ropp_mod + +use iso_c_binding +use kinds +use ufo_vars_mod +use ufo_geovals_mod +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_basis_mod, only: ufo_basis +use obsspace_mod +use missing_values_mod +use fckit_log_module, only : fckit_log + +implicit none +public :: ufo_groundgnss_ropp +private + + !> Fortran derived type for ground based gnss trajectory +type, extends(ufo_basis) :: ufo_groundgnss_ROPP + contains + procedure :: simobs => ufo_groundgnss_ropp_simobs +end type ufo_groundgnss_ROPP + +contains + +! ------------------------------------------------------------------------------ +! ------------------------------------------------------------------------------ +subroutine ufo_groundgnss_ropp_simobs(self, geovals, hofx, obss) + + implicit none + class(ufo_groundgnss_ROPP), intent(in) :: self + type(ufo_geovals), intent(in) :: geovals + real(kind_real), intent(inout) :: hofx(:) + type(c_ptr), value, intent(in) :: obss + real(c_double) :: missingDouble + + character(len=*), parameter :: myname_="ufo_groundgnss_ropp_simobs" + integer, parameter :: max_string = 800 + + character(max_string) :: err_msg + + write(err_msg,*) "TRACE: ufo_groundgnss_ropp_simobs_stub: begin" + call fckit_log%info(err_msg) + write(err_msg,*) "WARNING: GroundgnssROPP operator cannot run when ROPP code is not available" + call fckit_log%info(err_msg) + +! check if nobs is consistent in geovals & hofx + if (geovals%nlocs /= size(hofx)) then + write(err_msg,*) myname_, ' error: nlocs inconsistent!' + call abor1_ftn(err_msg) + endif + + missingDouble = missing_value(missingDouble) + +! initialize HofX to missing + hofx(:) = missingDouble + + write(err_msg,*) "TRACE: ufo_groundgnss_ropp_simobs_stub: completed" + call fckit_log%info(err_msg) + +end subroutine ufo_groundgnss_ropp_simobs +! ------------------------------------------------------------------------------ + +end module ufo_groundgnss_ropp_mod diff --git a/src/ufo/groundgnss/ZenithTotalDelayROPP/ufo_groundgnss_ropp_utils_mod.F90 b/src/ufo/groundgnss/ZenithTotalDelayROPP/ufo_groundgnss_ropp_utils_mod.F90 new file mode 100644 index 000000000..1b07fc2f2 --- /dev/null +++ b/src/ufo/groundgnss/ZenithTotalDelayROPP/ufo_groundgnss_ropp_utils_mod.F90 @@ -0,0 +1,547 @@ +! (C) Copyright 2017-2018 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to handle ground-based gnss observations following +!> the ROPP (2018 Aug) implementation + +module ufo_groundgnss_ropp_utils_mod + +!use iso_c_binding +use fckit_log_module, only: fckit_log +use kinds, only: kind_real + +! ROPP data type and library subroutines +use typesizes, only: wp => EightByteReal +use datetimetypes, only: dp +use ropp_fm_types, only: State1dFM, obs1dRefrac +use geodesy, only: gravity, R_eff, geometric2geopotential, geopotential2geometric +use arrays, only: callocate + +implicit none +public :: init_ropp_1d_statevec +public :: init_ropp_1d_statevec_ad +public :: init_ropp_1d_obvec +public :: init_ropp_1d_obvec_tlad +public :: ropp_tidy_up_1d +public :: ropp_tidy_up_tlad_1d +public :: calc_model_z +public :: calc_station_phi +public :: gnss_ztd_integral +private + +contains + +! ------------------------------------------------------------------------------------ +! ------------------------------------------------------------------------------------ + +subroutine init_ropp_1d_statevec(step_time, rlon, rlat, temp, shum, & + pres, phi, lm, z_sfc, x, iflip) +! Description: +! subroutine to fill a ROPP state vector structure with +! model background fields: Temperature, pressure, specific +! humidity at full-levels, and surface geopotential height. +! +! Inputs: +! temp background temperature +! shum background specific humidity +! pres background pressure +! phi geopotential height +! z_sfc terrain height of background field +! lm number of vertical levels in the background +! +! Outputs: +! x Forward model state vector +! ------------------------------------------------------------------------------------ + implicit none + + real(kind=dp), intent(in) :: step_time ! JulianTime of bg + real(kind=kind_real), intent(in) :: rlat, rlon ! latitude and longitude + real(kind=kind_real), intent(in) :: z_sfc ! Surface geometric height + integer, intent(in) :: lm ! number of model level + real(kind=kind_real), dimension(lm), intent(in) :: temp, shum, pres, phi ! model state variables + integer, optional, intent(in) :: iflip ! order indicator of model heights + type(State1dFM), intent(out) :: x ! ROPP 1d state vector data type + +! Local variables + real(kind=kind_real) :: rlon_local ! ROPP uses -180/180 + integer :: n, i, j, k + integer :: local_flip + + + x%non_ideal = .FALSE. ! Non-ideal gas flag + x%direct_ion = .FALSE. ! Calculate L1,L2 bangles + x%state_ok = .TRUE. ! State vector ok? + +! ROPP Longitude value is -180.0 to 180.0 + x%lat = real(rlat,kind=wp) + rlon_local = rlon + if (rlon_local .gt. 180) rlon_local = rlon_local - 360. + x%lon = real(rlon_local,kind=wp) + x%time = real(step_time, kind=wp) + +! Number of levels in background profile. What about (lm+1) field ? + x%n_lev = lm + +!-------------------------------------------------------------- +! allocate arrays for temperature, specific humidity, pressure +! and geopotential height data +!-------------------------------------------------------------- + if (associated(x%temp)) deallocate(x%temp) + if (associated(x%shum)) deallocate(x%shum) + if (associated(x%pres)) deallocate(x%pres) + if (associated(x%geop)) deallocate(x%geop) + + allocate(x%temp(x%n_lev)) + allocate(x%shum(x%n_lev)) + allocate(x%pres(x%n_lev)) + allocate(x%geop(x%n_lev)) + +!---------------------------------------------------- +! ROPP FM requires vertical height profile to be of the ascending order. +! (see ropp_io_ascend ( ROdata )). We may need to flip the data. +!---------------------------------------------------- + local_flip = 0 + if ( present(iflip) ) then + local_flip = iflip + end if + if ( local_flip .eq. 1) then + do k = 1, lm + x%temp(lm+1-k) = real(temp(k),kind=wp) + x%shum(lm+1-k) = real(shum(k),kind=wp) + x%pres(lm+1-k) = real(pres(k),kind=wp) + x%geop(lm+1-k) = real(phi(k),kind=wp) + end do + else + x%temp(1:lm) = real(temp(1:lm),kind=wp) + x%shum(1:lm) = real(shum(1:lm),kind=wp) + x%pres(1:lm) = real(pres(1:lm),kind=wp) + x%geop(1:lm) = real(phi(1:lm),kind=wp) + end if + +! surface geopotential height value + x%geop_sfc = geometric2geopotential(real(rlat,kind=wp), real(z_sfc,kind=wp)) +!------------------------------------------------ +! covariance matrix, is this used by ROPP FM? +!------------------------------------------------ + x%cov_ok = .TRUE. + +! Allocate memory +! For ECMWF example, Covariance matrix for temperature sigma and +! specific humidity sigma, and surface pressure. There the +! size of covariance matrix is 2 * nlevel + 1. + + n = (2*x%n_lev)+1 ! Number of elements in the state vector + + if (associated(x%cov%d)) deallocate(x%cov%d) + call callocate(x%cov%d, n*(n+1)/2) ! From ROPP utility library + + do i = 1, x%n_lev + x%cov%d(i + i*(i-1)/2) = 1.0_wp + end do + + do i = 1, x%n_lev + j = x%n_lev + i + x%cov%d(j + j*(j-1)/2) = 1.0_wp + enddo + + x%cov%d(n + n*(n-1)/2) = 1.0_wp + +!------------------------------ +! Rest of the covariance marix +!------------------------------ + if (associated(x%cov%e)) deallocate(x%cov%e) + if (associated(x%cov%f)) deallocate(x%cov%f) + if (associated(x%cov%s)) deallocate(x%cov%s) + + x%cov%fact_chol = .FALSE. + x%cov%equi_chol = 'N' + +end subroutine init_ropp_1d_statevec + +!------------------------------------------------------------------------------------ +!------------------------------------------------------------------------------------ + +subroutine init_ropp_1d_statevec_ad(temp_d, shum_d, pres_d, phi_d, & + lm, x_ad, iflip) + +! Description: +! subroutine to fill a ROPP state vector structure with +! model background fields: Temperature, pressure, specific +! humidity at full-levels, and surface geopotential height. +! +! Inputs: +! temp background temperature +! shum background specific humidity +! pres background pressure +! phi geopotential height +! lm number of vertical levels in the background +! +! Outputs: +! x Forward model state vector +! +! ############################################################### + type(State1dFM), intent(inout) :: x_ad + integer, intent(in) :: lm + integer, optional, intent(in) :: iflip + real(kind=kind_real), dimension(lm), intent(inout) :: temp_d, shum_d, pres_d, phi_d + +! Local variables + integer :: k + integer :: local_flip +!------------------------------------------------------------------------- + + +! all this appears to be is a function to flip arguments before passing to ROPP + local_flip = 0 + if ( present(iflip) ) then + local_flip = iflip + end if + + if ( local_flip == 1 ) then + do k = 1, lm + temp_d(k) = real(x_ad%temp(lm+1-k),kind=kind_real) + shum_d(k) = real(x_ad%shum(lm+1-k),kind=kind_real) + pres_d(k) = real(x_ad%pres(lm+1-k),kind=kind_real) + phi_d(k) = real(x_ad%geop(lm+1-k),kind=kind_real) + end do + else + temp_d(1:lm) = real(x_ad%temp(1:lm),kind=kind_real) + shum_d(1:lm) = real(x_ad%shum(1:lm),kind=kind_real) + pres_d(1:lm) = real(x_ad%pres(1:lm),kind=kind_real) + phi_d(1:lm) = real(x_ad%geop(1:lm),kind=kind_real) + end if + x_ad%temp(:) = 0.0_wp + x_ad%shum(:) = 0.0_wp + x_ad%pres(:) = 0.0_wp + x_ad%geop(:) = 0.0_wp + +end subroutine init_ropp_1d_statevec_ad + +!------------------------------------------------------------------------------------ +!------------------------------------------------------------------------------------ + +subroutine calc_station_phi(rlat, station_height, station_phi) + +! Description: +! convert station geometric height to geopotential height +! provide inputs of: +! lat, station_height +! +! Inputs: +! rlat latitude +! station_height station geometric height +! +! Outputs: +! station_phi: station geopotential height +!----------------------------------------------------------------------------------- + implicit none + + real(kind=kind_real), intent(in) :: rlat, station_height + real(kind=kind_real), intent(out) :: station_phi + + ! not used in forward operator at this time -- simulate at model levels only + station_phi = geometric2geopotential(real(rlat,kind=wp), real(station_height,kind=wp)) + +end subroutine calc_station_phi + +!------------------------------------------------------------------------------------ +!------------------------------------------------------------------------------------ + +subroutine calc_model_z(nlev, rlat, model_phi, model_z) + +! Description: +! convert model geopotential height to geometric height +! provide inputs of: +! number_of_model_levels, lat, model_geopotential +! +! Inputs: +! nlev number of model levels +! rlat latitude +! model_phi model geopotential height +! +! Outputs: +! model_z: model geometric height +!----------------------------------------------------------------------------------- + implicit none + + integer, intent(in) :: nlev + real(kind=kind_real), intent(in) :: rlat + real(kind=kind_real), dimension(nlev), intent(in) :: model_phi + real(kind=kind_real), dimension(nlev), intent(out) :: model_z + + integer ilev + + ! not used in forward operator at this time -- simulate at model levels only + do ilev = 1, nlev + model_z(ilev) = geopotential2geometric(real(rlat,kind=wp), real(model_phi(ilev),kind=wp)) + end do + +end subroutine calc_model_z + +!------------------------------------------------------------------------------------ +!------------------------------------------------------------------------------------ + +subroutine init_ropp_1d_obvec(nlev, ichk, ob_time, rlat, rlon, station_phi, x, y) + +! Description: +! subroutine to fill a ROPP observation vector structure +! observation provides the inputs of: +! lat, lon, time +! +! forward model will provide the simulation of refractivity +! +! Inputs: +! ob_time time of the observation +! rlat latitude +! rlon longitude +! +! Outputs: +! y: Partially filled Forward model observation vector +!----------------------------------------------------------------------------------- + implicit none +! Input model state vector + type(State1dFM), intent(in) :: x ! ROPP 1d state vector data type +! Output observation vector + type(Obs1dRefrac), intent(out) :: y ! ROPP 1d Refractivity "observations" vector data type + + integer, intent(in) :: nlev ! number of model level + integer, dimension(nlev), intent(in) :: ichk ! quality flag for observation + real(kind=kind_real), intent(in) :: rlat, rlon ! latitude and longitude + real(kind=kind_real), intent(in) :: station_phi ! observation geopoential height + real(kind=dp), intent(in) :: ob_time ! JulianTime of obs +! Local variables + real(kind=kind_real) :: rlon_local ! ROPP uses -180/180 + integer :: i + + y%time = real(ob_time,kind=wp) + y%lat = real(rlat,kind=wp) + rlon_local = rlon + if (rlon_local .gt. 180) rlon_local = rlon_local - 360. + y%lon = real(rlon_local,kind=wp) + +!---------------------------------------------------- +! allocate refractivity & weights +!---------------------------------------------------- + if (associated(y%refrac)) then + deallocate(y%refrac) + deallocate(y%weights) + deallocate(y%geop) + nullify(y%refrac) + nullify(y%weights) + nullify(y%geop) + end if + + allocate(y%refrac(1:nlev)) ! value computed in fwd model + allocate(y%weights(1:nlev)) ! value set in fwd model + allocate(y%geop(1:nlev)) ! value set in fwd model + + do i=1,nlev + if (ichk(i) .le. 0) then + y%weights(i) = 1.0_wp ! following t_fascod example + else + y%weights(i) = 0.0_wp + end if + y%geop(i) = x%geop(i) + end do + + y%refrac(:) = 0.0_wp + +!--------------------------------------------- +! covariance matrix, is this used by ROPP FM? +!-------------------------------------------- + y%obs_ok = .TRUE. + +end subroutine init_ropp_1d_obvec + +!------------------------------------------------------------------------- +!------------------------------------------------------------------------- +subroutine init_ropp_1d_obvec_tlad(nlev, & + rlat,rlon,x,y,y_p) + implicit none +! Input state vector + type(State1dFM), intent(in) :: x +! Output observation vector + type(Obs1dRefrac), intent(out) :: y,y_p + + integer, intent(in) :: nlev + real(kind=kind_real), intent(in) :: rlat,rlon + real(kind=kind_real) :: rlon_local + +!------------------------------------------------------------------------- + y%time = real(0.0, kind=wp)!)real(ob_time,kind=wp) + y%lat = real(rlat,kind=wp) + rlon_local = rlon + if (rlon_local .gt. 180) rlon_local = rlon_local - 360. + y%lon = real(rlon_local,kind=wp) + +! allocate refractivity +!--------------------------------------------------------- + allocate(y%refrac(1:nlev)) ! value computed in fwd model + allocate(y%weights(1:nlev)) + allocate(y%geop(1:nlev)) + allocate(y_p%refrac(1:nlev)) ! value computed in fwd model + allocate(y_p%weights(1:nlev)) + allocate(y_p%geop(1:nlev)) + + y%weights(:) = 1.0 + y_p%weights(:) = 1.0 + y%refrac(:) = 0.0 + y_p%refrac(:) = 0.0 + y%geop(:) = x%geop(:) + y_p%geop(:) = x%geop(:) + + y%obs_ok = .TRUE. + +end subroutine init_ropp_1d_obvec_tlad + +! ------------------------------------------------------------------------------ +! Description: +! integrate refractivities to obtain the zenith total delay +! the station height is used to determine a correction +! to match to the observation height +! +! Inputs: +! lm number of model levels +! model_refrac refractivities to integrate +! model_z model geometric heights +! ob_terr observation station height +! +! Outputs: +! model_ztd integrated zenith total delay +! l_linear logical which indicates if exponential terrain adjustment +! not possible, and linear was assumed +! +! ------------------------------------------------------------------------------ +subroutine gnss_ztd_integral(lm, model_refrac, model_z, ob_terr, model_ztd, l_linear) + + implicit none + + integer, intent(in) :: lm + real(kind=kind_real), intent(in), dimension(lm) :: model_refrac, model_z + real(kind=kind_real), intent(in) :: ob_terr + real(kind=kind_real), intent(out) :: model_ztd + logical, intent(inout) :: l_linear + +! local variables + integer, parameter :: max_string = 800 + character(max_string) :: err_msg + real(kind=kind_real) :: ddzd, scaled_refrac, c_i + integer :: k + !------------------------------------------------------------------------------- + ! integral of ROPP refractivity values to compute zenith total delay (ztd) + !------------------------------------------------------------------------------- + l_linear = .false. + model_ztd = 0.0 + ddzd = 0.0 + do k = 1, lm + if (k==1) then + if ( model_refrac(k) <= 0 .or. model_refrac(k+1) <= 0 ) then + write(err_msg,'(a,2es13.3)') ' unphysical refractivity ', & + model_refrac(k), model_refrac(k+1) + call fckit_log%info(err_msg) + cycle + end if + ! lower boundary condition -- station below lowest model level + if ( ob_terr < model_z(k) ) then + c_i = ( log(model_refrac(k+1)) - log(model_refrac(k)) ) / & + ( model_z(k) - model_z(k+1) ) + if ( c_i == 0 ) then + write(err_msg,'(a,2es13.3)') ' model refractivity repeating1 ', & + model_refrac(k), model_refrac(k+1) + call fckit_log%info(err_msg) + l_linear = .true. + ddzd = model_refrac(k)*(model_z(k+1) - ob_terr) ! use linear estimate + cycle + end if + ddzd = -1.0 * model_refrac(k)/c_i * exp(c_i*model_z(k)) * & + ( exp(-c_i*model_z(k+1)) - exp(-c_i*ob_terr) ) + end if + else if ( ob_terr >= model_z(k)) then + cycle + else + if ( model_refrac(k) <= 0 .or. model_refrac(k-1) <= 0 ) then + write(err_msg,'(a,2es13.3)') ' unphysical refractivity ', & + model_refrac(k), model_refrac(k-1) + call fckit_log%info(err_msg) + cycle + end if + ! alternate lower boundary condition -- station between levels + if ( ob_terr >= model_z(k-1) .and. ob_terr < model_z(k) ) then + c_i = ( log(model_refrac(k)) - log(model_refrac(k-1)) ) / & + ( model_z(k-1) - model_z(k) ) + if ( c_i == 0 ) then + write(err_msg,'(a,2es13.3)') ' model refractivity repeating2 ', & + model_refrac(k), model_refrac(k-1) + call fckit_log%info(err_msg) + l_linear = .true. + ddzd = model_refrac(k) * ( ob_terr - model_z(k-1) ) ! use linear estimate +! ddzd = ddzd + 0.5*(model_refrac(k)+model_refrac(k-1))*(model_z(k) - ob_terr) ! not needed + cycle + end if + scaled_refrac = model_refrac(k-1) * exp ( -c_i * (ob_terr - model_z(k-1)) ) + ddzd = -1.0 * scaled_refrac/c_i * exp(c_i*ob_terr) * & + ( exp(-c_i*model_z(k)) - exp(-c_i*ob_terr) ) +! ddzd = ddzd + 0.5*(model_refrac(k)+model_refrac(k-1))*(model_z(k) - ob_terr) ! not needed + else ! normal integral up the column + ddzd = 0.5*(model_refrac(k)+model_refrac(k-1))*(model_z(k) - model_z(k-1)) + end if + end if + model_ztd = model_ztd + ddzd + enddo + + ! a very bad place to put this suggestions? + model_ztd = model_ztd * 1.e-6 + +end subroutine gnss_ztd_integral + +!------------------------------------------------------------------------- +!------------------------------------------------------------------------- +subroutine ropp_tidy_up_1d(x,y) + implicit none + type(state1dfm), intent(inout) :: x + type(obs1drefrac), intent(inout) :: y +! x + if (associated(x%temp)) deallocate(x%temp) + if (associated(x%shum)) deallocate(x%shum) + if (associated(x%pres)) deallocate(x%pres) + if (associated(x%geop)) deallocate(x%geop) +! y + if (associated(y%refrac)) deallocate(y%refrac) + if (associated(y%geop)) deallocate(y%geop) + if (associated(y%weights)) deallocate(y%weights) + +end subroutine ropp_tidy_up_1d + +!------------------------------------------------------------------------- +!------------------------------------------------------------------------- +subroutine ropp_tidy_up_tlad_1d(x,x_p,y,y_p) + implicit none + type(state1dfm), intent(inout) :: x,x_p ! x_p can be either x_tl or x_ad + type(obs1drefrac), intent(inout) :: y,y_p ! y_p can be either y_tl or y_ad +! x + deallocate(x%temp) + deallocate(x%shum) + deallocate(x%pres) + deallocate(x%geop) + + deallocate(x_p%temp) + deallocate(x_p%shum) + deallocate(x_p%pres) + deallocate(x_p%geop) +! y + deallocate(y%refrac) + deallocate(y_p%refrac) + + deallocate(y%weights) + deallocate(y_p%weights) + + deallocate(y%geop) + deallocate(y_p%geop) + +end subroutine ropp_tidy_up_tlad_1d + +!------------------------------------------------------------------------- + +end module ufo_groundgnss_ropp_utils_mod diff --git a/src/ufo/groundgnss/ufo_refractivity_utils_mod.F90 b/src/ufo/groundgnss/ufo_refractivity_utils_mod.F90 deleted file mode 100644 index 93b33e305..000000000 --- a/src/ufo/groundgnss/ufo_refractivity_utils_mod.F90 +++ /dev/null @@ -1,212 +0,0 @@ -!------------------------------------------------------------------------------- -! (C) British Crown Copyright 2020 Met Office -! -! This software is licensed under the terms of the Apache Licence Version 2.0 -! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -!------------------------------------------------------------------------------- - -module ufo_refractivity_utils_mod - - -!use iso_c_binding -use fckit_log_module, only: fckit_log -use kinds, only: kind_real - -! Generic routines from elsewhere in jedi -use missing_values_mod -use ufo_constants_mod, only: & - rd, & ! Gas constant for dry air - cp, & ! Heat capacity at constant pressure for air - rd_over_cp, & ! Ratio of gas constant to heat capacity - pref, & ! Reference pressure for calculating exner - grav, & ! Gravitational field strength - n_alpha, & ! Refractivity constant a - n_beta, & ! Refractivity constant b - mw_ratio, & ! Ratio of molecular weights of water and dry air - C_virtual ! Related to mw ratio - -implicit none - -public :: ufo_refractivity -private - -contains - - -SUBROUTINE ufo_refractivity(nlevq, & - nlevP, & - za, & - zb, & - x, & - pN, & - refracerr, & - refrac) - -IMPLICIT NONE - -! Subroutine arguments: - -INTEGER, INTENT(IN) :: nlevq ! no. of levels of wet refractivity required -INTEGER, INTENT(IN) :: nlevP ! no. of levels of dry refractivity required -REAL(kind_real), INTENT(IN) :: za(:) ! heights of pressure levels - -REAL(kind_real), INTENT(IN) :: zb(:) ! Heights of theta levels -REAL(kind_real), INTENT(IN) :: x(:) ! state vector -REAL(kind_real), INTENT(INOUT) :: pN(:) ! pressure on refractivity (theta) levels -LOGICAL, INTENT(OUT) :: refracerr ! errors in refractivity calculation -REAL(kind_real), INTENT(INOUT) :: refrac(:) ! refrac on refractivity (theta) levels - - -! Local declarations: -CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_refractivity" -INTEGER :: i -INTEGER :: Level -REAL, ALLOCATABLE :: ExnerN(:) ! Exner on refractivity (theta) levels -REAL, ALLOCATABLE :: Exner(:) ! Exner on pressure (rho) levels -REAL(kind_real) :: T(nlevq) ! Temp. on theta levs -REAL :: Tv ! Tv on refractivity (theta) level -REAL :: Ndry ! Dry refractivity -REAL :: Nwet ! Wet refractivity - -INTEGER :: numPlevs ! Number of levels of pInter -INTEGER :: numqlevs ! Numbers of levels of qN -LOGICAL :: nonmon ! non-monotonic pressure warning -LOGICAL :: unphys ! zero or negative pressure warning -LOGICAL :: levelerr ! nlevq greater than nlevp error -integer, parameter :: max_string = 800 -CHARACTER(len=max_string) :: message - -REAL(kind_real) :: P(nlevP) -REAL(kind_real) :: q(nlevq) -REAL(kind_real) :: pwt1 -REAL(kind_real) :: pwt2 -INTEGER :: nstate - - -! Get constants into right units - -nstate = nlevP + nlevq -P(:) = x(1:nlevP) -q(:) = x(nlevP + 1:nstate) - -ALLOCATE (ExnerN(nlevq)) -ALLOCATE (Exner(nlevP)) - -refrac(:) = missing_value(refrac(1)) -T(:) = missing_value(T(1)) -nonmon = .FALSE. -unphys = .FALSE. -levelerr = .FALSE. -refracerr = .FALSE. - -DO i = 1, nlevP - IF (P(i) == missing_value(P(i))) THEN !pressure missing - refracerr = .TRUE. - WRITE(message, *) RoutineName, "Missing value P", i - CALL fckit_log % warning(message) - EXIT - END IF -END DO - - -DO i = 1, nlevP-1 - IF (P(i) - P(i + 1) < 0.0) THEN !or non-monotonic pressure - refracerr = .TRUE. - nonmon = .TRUE. - WRITE(message,*) "Non monotonic", i, P(i), P(i+1) - CALL fckit_log % warning(message) - EXIT - END IF -END DO - -IF (ANY (P(:) <= 0.0)) THEN !pressure zero or negative - refracerr = .TRUE. - unphys = .TRUE. -END IF - -IF (nlevq >= nlevP) THEN ! nlevq must be < than nlevP - refracerr = .TRUE. - levelerr = .TRUE. -END IF - -! only proceed if pressure is valid -IF (refracerr) THEN - IF (nonmon) THEN - CALL fckit_log%warning (RoutineName // ": Pressure non-monotonic") - ELSE IF (unphys) THEN - CALL fckit_log%warning (RoutineName // ": Pressure <= zero") - ELSE - CALL fckit_log%warning (RoutineName // ": Pressure missing") - END IF - IF (levelerr) THEN - CALL fckit_log%warning (RoutineName // ": Too many wet levels") - END IF - -ELSE - - ! Calculate exner on rho(pressure) levels. - - Exner(:) = (P(:) / pref) ** rd_over_cp - - ! Calculate the refractivity on the b levels - - - DO Level = 1, nlevP - 1 - - pwt1 = (za(Level + 1) - zb(Level)) / (za(Level + 1) - za(Level)) - pwt2 = 1.0 - pwt1 - - pN(Level) = EXP (pwt1 * LOG (P(Level)) + pwt2 * LOG (P(Level + 1))) - - END DO - - DO i = 1, nlevq - - ! Calculate Exner on the refractivity level. - ExnerN(i) = (pN(i) / pref) ** rd_over_cp - - ! Calculate mean layer Tv using ND definition - - Tv = grav * (za(i + 1) - za(i)) * ExnerN(i) / & - (cp * (Exner(i) - Exner(i + 1))) - - IF (i > nlevq) THEN - - T(i) = Tv - - ! No wet component - - Nwet = 0.0 - - ELSE - - T(i) = Tv / (1.0 + C_virtual * q(i)) - - ! Wet component - - Nwet = n_beta * pN(i) * q(i) / (T(i) ** 2 * (mw_ratio + (1.0 - mw_ratio) * q(i))) - - - END IF - - IF (i > nlevp ) THEN - ! No dry component - - Ndry = 0.0 - - ELSE - ! Dry component - - Ndry = n_alpha * pN(i) / T(i) - - END IF - - refrac(i) = Ndry + Nwet - - END DO - -END IF - -END SUBROUTINE ufo_refractivity - -END MODULE ufo_refractivity_utils_mod diff --git a/src/ufo/identity/ObsIdentity.cc b/src/ufo/identity/ObsIdentity.cc index 05aeebf01..11c0c29e3 100644 --- a/src/ufo/identity/ObsIdentity.cc +++ b/src/ufo/identity/ObsIdentity.cc @@ -8,6 +8,7 @@ #include "ufo/identity/ObsIdentity.h" #include +#include #include "ioda/ObsVector.h" @@ -16,6 +17,7 @@ #include "ufo/GeoVaLs.h" #include "ufo/ObsDiagnostics.h" +#include "ufo/utils/OperatorUtils.h" // for getOperatorVariables namespace ufo { @@ -27,7 +29,12 @@ ObsIdentity::ObsIdentity(const ioda::ObsSpace & odb, const eckit::Configuration & config) : ObsOperatorBase(odb, config), keyOperObsIdentity_(0), odb_(odb), varin_() { - ufo_identity_setup_f90(keyOperObsIdentity_, config, odb.obsvariables(), varin_); + std::vector operatorVarIndices; + getOperatorVariables(config, odb.obsvariables(), operatorVars_, operatorVarIndices); + + ufo_identity_setup_f90(keyOperObsIdentity_, config, + operatorVars_, operatorVarIndices.data(), operatorVarIndices.size(), + varin_); oops::Log::trace() << "ObsIdentity created." << std::endl; } diff --git a/src/ufo/identity/ObsIdentity.h b/src/ufo/identity/ObsIdentity.h index 55a14c199..586862b43 100644 --- a/src/ufo/identity/ObsIdentity.h +++ b/src/ufo/identity/ObsIdentity.h @@ -46,6 +46,8 @@ class ObsIdentity : public ObsOperatorBase, // Other const oops::Variables & requiredVars() const override {return varin_;} + oops::Variables simulatedVars() const override { return operatorVars_; } + int & toFortran() {return keyOperObsIdentity_;} const int & toFortran() const {return keyOperObsIdentity_;} @@ -54,6 +56,7 @@ class ObsIdentity : public ObsOperatorBase, F90hop keyOperObsIdentity_; const ioda::ObsSpace& odb_; oops::Variables varin_; + oops::Variables operatorVars_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/identity/ObsIdentity.interface.F90 b/src/ufo/identity/ObsIdentity.interface.F90 index d73eeb1c5..60d85668e 100644 --- a/src/ufo/identity/ObsIdentity.interface.F90 +++ b/src/ufo/identity/ObsIdentity.interface.F90 @@ -34,20 +34,27 @@ module ufo_identity_mod_c ! ------------------------------------------------------------------------------ -subroutine ufo_identity_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_identity_setup_f90') +subroutine ufo_identity_setup_c(c_key_self, c_conf, c_obsvars, c_obsvarindices, c_nobsvars, & + c_geovars) bind(c,name='ufo_identity_setup_f90') use fckit_configuration_module, only: fckit_configuration use oops_variables_mod implicit none integer(c_int), intent(inout) :: c_key_self type(c_ptr), value, intent(in) :: c_conf -type(c_ptr), value, intent(in) :: c_obsvars ! variables to be simulated -type(c_ptr), value, intent(in) :: c_geovars ! variables requested from the model +! variables to be simulated +type(c_ptr), intent(in), value :: c_obsvars ! variables to be simulated... +integer(c_int), intent(in), value :: c_nobsvars +integer(c_int), intent(in) :: c_obsvarindices(c_nobsvars) ! ... and their global indices +type(c_ptr), intent(in), value :: c_geovars ! variables requested from the model type(ufo_identity), pointer :: self call ufo_identity_registry%setup(c_key_self, self) self%obsvars = oops_variables(c_obsvars) +allocate(self%obsvarindices(self%obsvars%nvars())) +self%obsvarindices(:) = c_obsvarindices(:) + 1 ! Convert from C to Fortran indexing + self%geovars = oops_variables(c_geovars) call self%setup() diff --git a/src/ufo/identity/ObsIdentity.interface.h b/src/ufo/identity/ObsIdentity.interface.h index 62856190b..d545ed8c1 100644 --- a/src/ufo/identity/ObsIdentity.interface.h +++ b/src/ufo/identity/ObsIdentity.interface.h @@ -18,8 +18,24 @@ extern "C" { // ----------------------------------------------------------------------------- + /// \param operatorVars + /// Variables to be simulated by this operator. + /// \param operatorVarIndices + /// Indices of the variables from \p operatorVar in the list of all simulated + /// variables in the ObsSpace. + /// \param numOperatorVarIndices + /// Size of the \p operatorVarIndices array (must be the same as the number of variables in + /// \p operatorVars). + /// \param[out] requiredVars + /// GeoVaLs required for the simulation of the variables \p operatorVars. + /// + /// Example: if the list of simulated variables in the ObsSpace is + /// [air_temperature, northward_wind, eastward_wind] and \p operatorVars is + /// [northward_wind, eastward_wind], then \p operatorVarIndices should be set to [1, 2]. void ufo_identity_setup_f90(F90hop &, const eckit::Configuration &, - const oops::Variables &, oops::Variables &); + const oops::Variables &operatorVars, + const int *operatorVarIndices, const int numOperatorVarIndices, + oops::Variables &requiredVars); void ufo_identity_delete_f90(F90hop &); void ufo_identity_simobs_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, const int &, const int &, double &); diff --git a/src/ufo/identity/ObsIdentityTLAD.cc b/src/ufo/identity/ObsIdentityTLAD.cc index e5e50c25c..aec347669 100644 --- a/src/ufo/identity/ObsIdentityTLAD.cc +++ b/src/ufo/identity/ObsIdentityTLAD.cc @@ -1,5 +1,5 @@ /* - * (C) Copyright 2017-2018 UCAR + * (C) Copyright 2017-2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -8,6 +8,7 @@ #include "ufo/identity/ObsIdentityTLAD.h" #include +#include #include "ioda/ObsSpace.h" #include "ioda/ObsVector.h" @@ -17,6 +18,7 @@ #include "ufo/GeoVaLs.h" #include "ufo/ObsBias.h" +#include "ufo/utils/OperatorUtils.h" // for getOperatorVariables namespace ufo { @@ -26,9 +28,15 @@ static LinearObsOperatorMaker makerIdentityTL_("Identity"); ObsIdentityTLAD::ObsIdentityTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOperObsIdentity_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOperObsIdentity_(0), varin_() { - ufo_identity_tlad_setup_f90(keyOperObsIdentity_, config, odb.obsvariables(), varin_); + std::vector operatorVarIndices; + getOperatorVariables(config, odb.obsvariables(), operatorVars_, operatorVarIndices); + + ufo_identity_tlad_setup_f90(keyOperObsIdentity_, config, + operatorVars_, + operatorVarIndices.data(), operatorVarIndices.size(), + varin_); oops::Log::trace() << "ObsIdentityTLAD created." << std::endl; } @@ -44,23 +52,23 @@ ObsIdentityTLAD::~ObsIdentityTLAD() { void ObsIdentityTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_identity_tlad_settraj_f90(keyOperObsIdentity_, geovals.toFortran(), odb_); + ufo_identity_tlad_settraj_f90(keyOperObsIdentity_, geovals.toFortran(), obsspace()); oops::Log::trace() << "ObsIdentityTLAD: trajectory set" << std::endl; } // ----------------------------------------------------------------------------- void ObsIdentityTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_identity_simobs_tl_f90(keyOperObsIdentity_, geovals.toFortran(), odb_, - ovec.size(), ovec.toFortran()); + ufo_identity_simobs_tl_f90(keyOperObsIdentity_, geovals.toFortran(), obsspace(), + ovec.nvars(), ovec.nlocs(), ovec.toFortran()); oops::Log::trace() << "ObsIdentityTLAD: TL observation operator run" << std::endl; } // ----------------------------------------------------------------------------- void ObsIdentityTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_identity_simobs_ad_f90(keyOperObsIdentity_, geovals.toFortran(), odb_, - ovec.size(), ovec.toFortran()); + ufo_identity_simobs_ad_f90(keyOperObsIdentity_, geovals.toFortran(), obsspace(), + ovec.nvars(), ovec.nlocs(), ovec.toFortran()); oops::Log::trace() << "ObsIdentityTLAD: adjoint observation operator run" << std::endl; } diff --git a/src/ufo/identity/ObsIdentityTLAD.h b/src/ufo/identity/ObsIdentityTLAD.h index 1fb07a09b..19c5eedbb 100644 --- a/src/ufo/identity/ObsIdentityTLAD.h +++ b/src/ufo/identity/ObsIdentityTLAD.h @@ -50,6 +50,7 @@ class ObsIdentityTLAD : public LinearObsOperatorBase, // Other const oops::Variables & requiredVars() const override {return varin_;} + oops::Variables simulatedVars() const override {return operatorVars_;} int & toFortran() {return keyOperObsIdentity_;} const int & toFortran() const {return keyOperObsIdentity_;} @@ -57,8 +58,8 @@ class ObsIdentityTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOperObsIdentity_; - const ioda::ObsSpace& odb_; oops::Variables varin_; + oops::Variables operatorVars_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/identity/ObsIdentityTLAD.interface.F90 b/src/ufo/identity/ObsIdentityTLAD.interface.F90 index 3a334bfea..9123160e4 100644 --- a/src/ufo/identity/ObsIdentityTLAD.interface.F90 +++ b/src/ufo/identity/ObsIdentityTLAD.interface.F90 @@ -32,19 +32,24 @@ module ufo_identity_tlad_mod_c ! ------------------------------------------------------------------------------ -subroutine ufo_identity_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_identity_tlad_setup_f90') +subroutine ufo_identity_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_obsvarindices, c_nobsvars, & + c_geovars) bind(c,name='ufo_identity_tlad_setup_f90') use oops_variables_mod implicit none -integer(c_int), intent(inout) :: c_key_self -type(c_ptr), value, intent(in) :: c_conf -type(c_ptr), value, intent(in) :: c_obsvars ! variables to be simulated -type(c_ptr), value, intent(in) :: c_geovars ! variables requested from the model +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), value, intent(in) :: c_conf +type(c_ptr), intent(in), value :: c_obsvars ! variables to be simulated... +integer(c_int), intent(in), value :: c_nobsvars +integer(c_int), intent(in) :: c_obsvarindices(c_nobsvars) ! ... and their global indices +type(c_ptr), value, intent(in) :: c_geovars ! variables requested from the model type(ufo_identity_tlad), pointer :: self call ufo_identity_tlad_registry%setup(c_key_self, self) self%obsvars = oops_variables(c_obsvars) +allocate(self%obsvarindices(self%obsvars%nvars())) +self%obsvarindices(:) = c_obsvarindices(:) + 1 ! Convert from C to Fortran indexing self%geovars = oops_variables(c_geovars) call self%setup() @@ -84,14 +89,14 @@ end subroutine ufo_identity_tlad_settraj_c ! ------------------------------------------------------------------------------ -subroutine ufo_identity_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nobs, c_hofx) bind(c,name='ufo_identity_simobs_tl_f90') +subroutine ufo_identity_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) bind(c,name='ufo_identity_simobs_tl_f90') implicit none integer(c_int), intent(in) :: c_key_self integer(c_int), intent(in) :: c_key_geovals type(c_ptr), value, intent(in) :: c_obsspace -integer(c_int), intent(in) :: c_nobs -real(c_double), intent(inout) :: c_hofx(c_nobs) +integer(c_int), intent(in) :: c_nvars, c_nlocs +real(c_double), intent(inout) :: c_hofx(c_nvars, c_nlocs) type(ufo_identity_tlad), pointer :: self type(ufo_geovals), pointer :: geovals @@ -99,27 +104,27 @@ subroutine ufo_identity_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nob call ufo_identity_tlad_registry%get(c_key_self, self) call ufo_geovals_registry%get(c_key_geovals, geovals) -call self%simobs_tl(geovals, c_hofx, c_obsspace) +call self%simobs_tl(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) end subroutine ufo_identity_simobs_tl_c ! ------------------------------------------------------------------------------ -subroutine ufo_identity_simobs_ad_c(c_key_self, c_key_geovals, c_obsspace, c_nobs, c_hofx) bind(c,name='ufo_identity_simobs_ad_f90') +subroutine ufo_identity_simobs_ad_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) bind(c,name='ufo_identity_simobs_ad_f90') implicit none integer(c_int), intent(in) :: c_key_self integer(c_int), intent(in) :: c_key_geovals type(c_ptr), value, intent(in) :: c_obsspace -integer(c_int), intent(in) :: c_nobs -real(c_double), intent(in) :: c_hofx(c_nobs) +integer(c_int), intent(in) :: c_nvars, c_nlocs +real(c_double), intent(in) :: c_hofx(c_nvars, c_nlocs) type(ufo_identity_tlad), pointer :: self type(ufo_geovals), pointer :: geovals call ufo_identity_tlad_registry%get(c_key_self, self) call ufo_geovals_registry%get(c_key_geovals, geovals) -call self%simobs_ad(geovals, c_hofx, c_obsspace) +call self%simobs_ad(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) end subroutine ufo_identity_simobs_ad_c diff --git a/src/ufo/identity/ObsIdentityTLAD.interface.h b/src/ufo/identity/ObsIdentityTLAD.interface.h index f53f4bd0b..70a89dcf5 100644 --- a/src/ufo/identity/ObsIdentityTLAD.interface.h +++ b/src/ufo/identity/ObsIdentityTLAD.interface.h @@ -20,14 +20,30 @@ extern "C" { // ----------------------------------------------------------------------------- + /// \param operatorVars + /// Variables to be simulated by this operator. + /// \param operatorVarIndices + /// Indices of the variables from \p operatorVar in the list of all simulated + /// variables in the ObsSpace. + /// \param numOperatorVarIndices + /// Size of the \p operatorVarIndices array (must be the same as the number of variables in + /// \p operatorVars). + /// \param[out] requiredVars + /// GeoVaLs required for the simulation of the variables \p operatorVars. + /// + /// Example: if the list of simulated variables in the ObsSpace is + /// [air_temperature, northward_wind, eastward_wind] and \p operatorVars is + /// [northward_wind, eastward_wind], then \p operatorVarIndices should be set to [1, 2]. void ufo_identity_tlad_setup_f90(F90hop &, const eckit::Configuration &, - const oops::Variables &, oops::Variables &); + const oops::Variables &operatorVars, + const int *operatorVarIndices, const int numOperatorVarIndices, + oops::Variables &requiredVars); void ufo_identity_tlad_delete_f90(F90hop &); void ufo_identity_tlad_settraj_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &); void ufo_identity_simobs_tl_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, - const int &, double &); + const int &nvars, const int &nlocs, double &hofx); void ufo_identity_simobs_ad_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, - const int &, const double &); + const int &nvars, const int &nlocs, const double &hofx); // ----------------------------------------------------------------------------- } // extern C diff --git a/src/ufo/identity/ufo_identity_mod.F90 b/src/ufo/identity/ufo_identity_mod.F90 index bdc405074..496ca3317 100644 --- a/src/ufo/identity/ufo_identity_mod.F90 +++ b/src/ufo/identity/ufo_identity_mod.F90 @@ -13,7 +13,9 @@ module ufo_identity_mod ! Fortran derived type for the observation type !--------------------------------------------------------------------------------------------------- type, public :: ufo_identity - type(oops_variables), public :: obsvars + type(oops_variables), public :: obsvars ! Variables to be simulated + integer, allocatable, public :: obsvarindices(:) ! Indices of obsvars in the list of all + ! simulated variables in the ObsSpace type(oops_variables), public :: geovars contains procedure :: setup => identity_setup_ @@ -50,13 +52,16 @@ subroutine identity_simobs_(self, geovals, obss, nvars, nlocs, hofx) real(c_double), intent(inout) :: hofx(nvars, nlocs) type(c_ptr), value, intent(in) :: obss - integer :: iobs, ivar + integer :: iobs, ivar, iobsvar type(ufo_geoval), pointer :: point character(len=MAXVARLEN) :: geovar - do ivar = 1, nvars - !> Get the name of input variable in geovals - geovar = self%geovars%variable(ivar) + do iobsvar = 1, size(self%obsvarindices) + ! Get the index of the row of hofx to fill + ivar = self%obsvarindices(iobsvar) + + ! Get the name of input variable in geovals + geovar = self%geovars%variable(iobsvar) !> Get profile for this variable from geovals call ufo_geovals_get_var(geovals, geovar, point) diff --git a/src/ufo/identity/ufo_identity_tlad_mod.F90 b/src/ufo/identity/ufo_identity_tlad_mod.F90 index a0cd2fbf3..cca0ce4b8 100644 --- a/src/ufo/identity/ufo_identity_tlad_mod.F90 +++ b/src/ufo/identity/ufo_identity_tlad_mod.F90 @@ -16,7 +16,9 @@ module ufo_identity_tlad_mod !> Fortran derived type for the tl/ad observation operator type, public :: ufo_identity_tlad private - type(oops_variables), public :: obsvars + type(oops_variables), public :: obsvars ! Variables to be simulated + integer, allocatable, public :: obsvarindices(:) ! Indices of obsvars in the list of all + ! simulated variables in the ObsSpace type(oops_variables), public :: geovars contains procedure :: setup => identity_tlad_setup_ @@ -34,7 +36,7 @@ subroutine identity_tlad_setup_(self) integer :: ivar, nvars - !> copy observed variables to variables requested from the model + !> copy simulated variables to variables requested from the model nvars = self%obsvars%nvars() do ivar = 1, nvars call self%geovars%push_back(self%obsvars%variable(ivar)) @@ -56,7 +58,7 @@ subroutine identity_tlad_settraj_(self, geovals, obss) end subroutine identity_tlad_settraj_ ! ------------------------------------------------------------------------------ -subroutine identity_simobs_tl_(self, geovals, hofx, obss) +subroutine identity_simobs_tl_(self, geovals, obss, nvars, nlocs, hofx) use iso_c_binding use kinds use ufo_geovals_mod, only: & @@ -67,33 +69,34 @@ subroutine identity_simobs_tl_(self, geovals, hofx, obss) implicit none class(ufo_identity_tlad), intent(in) :: self type(ufo_geovals), intent(in) :: geovals - real(c_double), intent(inout) :: hofx(:) type(c_ptr), value, intent(in) :: obss + integer, intent(in) :: nvars, nlocs + real(c_double), intent(inout) :: hofx(nvars, nlocs) - integer :: iobs, ivar, nlocs, nvars + integer :: iobs, iobsvar, ivar type(ufo_geoval), pointer :: point character(len=MAXVARLEN) :: geovar - !> Get the observation vertical coordinates - nlocs = obsspace_get_nlocs(obss) - nvars = self%obsvars%nvars() - do ivar = 1, nvars + do iobsvar = 1, size(self%obsvarindices) + ! Get the index of the row of hofx to fill + ivar = self%obsvarindices(iobsvar) + !> Get the name of input variable in geovals - geovar = self%geovars%variable(ivar) + geovar = self%geovars%variable(iobsvar) !> Get profile for this variable from geovals call ufo_geovals_get_var(geovals, geovar, point) !> Here we just apply a identity hofx do iobs = 1, nlocs - hofx(ivar + (iobs-1)*nvars) = point%vals(1,iobs) + hofx(ivar, iobs) = point%vals(1, iobs) enddo enddo end subroutine identity_simobs_tl_ ! ------------------------------------------------------------------------------ -subroutine identity_simobs_ad_(self, geovals, hofx, obss) +subroutine identity_simobs_ad_(self, geovals, obss, nvars, nlocs, hofx) use iso_c_binding use kinds use ufo_geovals_mod, only: & @@ -105,10 +108,11 @@ subroutine identity_simobs_ad_(self, geovals, hofx, obss) implicit none class(ufo_identity_tlad), intent(in) :: self type(ufo_geovals), intent(inout) :: geovals - real(c_double), intent(in) :: hofx(:) type(c_ptr), value, intent(in) :: obss + integer, intent(in) :: nvars, nlocs + real(c_double), intent(in) :: hofx(nvars, nlocs) - integer :: iobs, ivar, nlocs, nvars + integer :: iobs, iobsvar, ivar type(ufo_geoval), pointer :: point character(len=MAXVARLEN) :: geovar real(c_double) :: missing @@ -118,25 +122,26 @@ subroutine identity_simobs_ad_(self, geovals, hofx, obss) if (.not. geovals%linit ) geovals%linit=.true. - nlocs = obsspace_get_nlocs(obss) - nvars = self%obsvars%nvars() - do ivar = 1, nvars + do iobsvar = 1, size(self%obsvarindices) + ! Get the index of the row of hofx to fill + ivar = self%obsvarindices(iobsvar) + !> Get the name of input variable in geovals - geovar = self%geovars%variable(ivar) + geovar = self%geovars%variable(iobsvar) !> Get profile for this variable from geovals call ufo_geovals_get_var(geovals, geovar, point) if (.not.(allocated(point%vals))) then point%nval=1 - allocate(point%vals(1,size(hofx,1))) + allocate(point%vals(1,size(hofx,2))) point%vals = 0.0 end if ! backward obs operator do iobs = 1, nlocs - if (hofx(ivar + (iobs-1)*nvars) /= missing) then - point%vals(1,iobs) = hofx(ivar + (iobs-1)*nvars) + if (hofx(ivar, iobs) /= missing) then + point%vals(1, iobs) = hofx(ivar, iobs) endif enddo enddo diff --git a/src/ufo/instantiateObsFilterFactory.h b/src/ufo/instantiateObsFilterFactory.h index 280092a2c..caf31a774 100644 --- a/src/ufo/instantiateObsFilterFactory.h +++ b/src/ufo/instantiateObsFilterFactory.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2019 UCAR + * (C) Copyright 2019-2021 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -10,78 +10,132 @@ #include "oops/base/instantiateObsFilterFactory.h" #include "oops/interface/ObsFilter.h" +#include "ufo/filters/AcceptList.h" #include "ufo/filters/BackgroundCheck.h" +#include "ufo/filters/BayesianBackgroundCheck.h" +#include "ufo/filters/BayesianBackgroundQCFlags.h" #include "ufo/filters/BlackList.h" #include "ufo/filters/DifferenceCheck.h" #include "ufo/filters/Gaussian_Thinning.h" +#include "ufo/filters/gnssroonedvarcheck/GNSSROOneDVarCheck.h" +#include "ufo/filters/HistoryCheck.h" +#include "ufo/filters/ImpactHeightCheck.h" #include "ufo/filters/MetOfficeBuddyCheck.h" +#include "ufo/filters/ModelObThreshold.h" #include "ufo/filters/MWCLWCheck.h" #include "ufo/filters/ObsBoundsCheck.h" #include "ufo/filters/ObsDerivativeCheck.h" #include "ufo/filters/ObsDiagnosticsWriter.h" #include "ufo/filters/ObsDomainCheck.h" #include "ufo/filters/ObsDomainErrCheck.h" +#include "ufo/filters/PerformAction.h" #include "ufo/filters/PoissonDiskThinning.h" #include "ufo/filters/PreQC.h" +#include "ufo/filters/ProfileBackgroundCheck.h" #include "ufo/filters/ProfileConsistencyChecks.h" +#include "ufo/filters/ProfileFewObsCheck.h" #include "ufo/filters/QCmanager.h" +#include "ufo/filters/SatName.h" +#include "ufo/filters/StuckCheck.h" #include "ufo/filters/TemporalThinning.h" #include "ufo/filters/Thinning.h" #include "ufo/filters/TrackCheck.h" #include "ufo/filters/TrackCheckShip.h" +#include "ufo/filters/VariableAssignment.h" +#include "ufo/filters/VariableTransforms.h" #include "ufo/gnssro/QC/BackgroundCheckRONBAM.h" #include "ufo/gnssro/QC/ROobserror.h" +#if defined(RTTOV_FOUND) + #include "ufo/filters/rttovonedvarcheck/RTTOVOneDVarCheck.h" +#endif + namespace ufo { -template void instantiateObsFilterFactory() { - oops::instantiateObsFilterFactory(); - static oops::FilterMaker > +template void instantiateObsFilterFactory() { + oops::instantiateObsFilterFactory(); + static oops::FilterMaker > qcManagerMaker("QCmanager"); - static oops::FilterMaker > + static oops::FilterMaker > preQCMaker("PreQC"); - static oops::FilterMaker > + static oops::FilterMaker > domainCheckMaker("Domain Check"); - static oops::FilterMaker > + static oops::FilterMaker > + satnameCheckMaker("satname"); + static oops::FilterMaker > boundsCheckMaker("Bounds Check"); - static oops::FilterMaker > + static oops::FilterMaker > blackListMaker("BlackList"); - static oops::FilterMaker > + static oops::FilterMaker > + rejectListMaker("RejectList"); // alternative name + static oops::FilterMaker > backgroundCheckMaker("Background Check"); - static oops::FilterMaker > + static oops::FilterMaker > + BayesianBackgroundCheckMaker("Bayesian Background Check"); + static oops::FilterMaker > differenceCheckMaker("Difference Check"); - static oops::FilterMaker > + static oops::FilterMaker > + historyCheckMaker("History Check"); + static oops::FilterMaker > + ModelObThresholdMaker("ModelOb Threshold"); + static oops::FilterMaker > ROobserrorMaker("ROobserror"); - static oops::FilterMaker > + static oops::FilterMaker > thinningMaker("Thinning"); - static oops::FilterMaker > + static oops::FilterMaker > gaussianThinningMaker("Gaussian Thinning"); - static oops::FilterMaker > + static oops::FilterMaker > MWCLWCheckMaker("MWCLW Check"); - static oops::FilterMaker > + static oops::FilterMaker > domainErrCheckMaker("DomainErr Check"); - static oops::FilterMaker > + static oops::FilterMaker > profileConsistencyChecksMaker("Profile Consistency Checks"); - static oops::FilterMaker > + static oops::FilterMaker > backgroundCheckRONBAMMaker("Background Check RONBAM"); - static oops::FilterMaker > + static oops::FilterMaker > temporalThinningMaker("Temporal Thinning"); - static oops::FilterMaker > + static oops::FilterMaker > poissonDiskThinningMaker("Poisson Disk Thinning"); - static oops::FilterMaker > + static oops::FilterMaker > YDIAGsaverMaker("YDIAGsaver"); - static oops::FilterMaker > + static oops::FilterMaker > TrackCheckMaker("Track Check"); - static oops::FilterMaker > + static oops::FilterMaker > MetOfficeBuddyCheckMaker("Met Office Buddy Check"); - static oops::FilterMaker > + static oops::FilterMaker > DerivativeCheckMaker("Derivative Check"); - static oops::FilterMaker > + static oops::FilterMaker > ShipTrackCheckMaker("Ship Track Check"); + static oops::FilterMaker > + StuckCheckMaker("Stuck Check"); + static oops::FilterMaker > + GNSSROOneDVarCheckMaker("GNSS-RO 1DVar Check"); + static oops::FilterMaker > + variableAssignmentMaker("Variable Assignment"); + static oops::FilterMaker > + VariableTransformsMaker("Variable Transforms"); + static oops::FilterMaker > + ProfileBackgroundCheckMaker("Profile Background Check"); + static oops::FilterMaker > + ProfileFewObsCheckMaker("Profile Few Observations Check"); + static oops::FilterMaker > + acceptListMaker("AcceptList"); + static oops::FilterMaker > + performActionMaker("Perform Action"); + static oops::FilterMaker > + BayesianBackgroundQCFlagsMaker("Bayesian Background QC Flags"); + static oops::FilterMaker > + ImpactHeightCheckMaker("GNSSRO Impact Height Check"); + + // Only include this filter if rttov is present + #if defined(RTTOV_FOUND) + static oops::FilterMaker > + RTTOVOneDVarCheckMaker("RTTOV OneDVar Check"); + #endif // For backward compatibility, register some filters under legacy names used in the past - static oops::FilterMaker > + static oops::FilterMaker > legacyGaussianThinningMaker("Gaussian_Thinning"); - static oops::FilterMaker > + static oops::FilterMaker > legacyTemporalThinningMaker("TemporalThinning"); } diff --git a/src/ufo/instantiateObsLocFactory.h b/src/ufo/instantiateObsLocFactory.h new file mode 100644 index 000000000..5044b339a --- /dev/null +++ b/src/ufo/instantiateObsLocFactory.h @@ -0,0 +1,29 @@ +/* + * (C) Copyright 2020-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_INSTANTIATEOBSLOCFACTORY_H_ +#define UFO_INSTANTIATEOBSLOCFACTORY_H_ + +#include "oops/base/ObsLocalizationBase.h" +#include "ufo/obslocalization/ObsLocalization.h" +#include "ufo/obslocalization/ObsLocGC99.h" +#include "ufo/obslocalization/ObsLocSOAR.h" +#include "ufo/ObsTraits.h" + +namespace ufo { +template void instantiateObsLocFactory() { + static oops::ObsLocalizationMaker> + maker_("Gaspari-Cohn"); + static oops::ObsLocalizationMaker> + makerSOAR_("SOAR"); + static oops::ObsLocalizationMaker> + makerBoxCar_("Box car"); +} + +} // namespace ufo + +#endif // UFO_INSTANTIATEOBSLOCFACTORY_H_ diff --git a/src/ufo/locations_f.cc b/src/ufo/locations_f.cc new file mode 100644 index 000000000..9aea341f6 --- /dev/null +++ b/src/ufo/locations_f.cc @@ -0,0 +1,44 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/locations_f.h" + +#include +#include + +#include "eckit/exception/Exceptions.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +std::size_t locations_get_nlocs_f(const Locations & locs) { + return locs.size(); +} +// ----------------------------------------------------------------------------- +void locations_get_lons_f(const Locations & locs, + const std::size_t & nlocs, double * lons) { + ASSERT(nlocs == locs.size()); + const std::vector & data = locs.lons(); + std::copy(data.begin(), data.end(), lons); +} +// ----------------------------------------------------------------------------- +void locations_get_lats_f(const Locations & locs, + const std::size_t & nlocs, double * lats) { + ASSERT(nlocs == locs.size()); + const std::vector & data = locs.lats(); + std::copy(data.begin(), data.end(), lats); +} +// ----------------------------------------------------------------------------- +void locations_get_timemask_f(const Locations & locs, + const util::DateTime & t1, const util::DateTime & t2, + const std::size_t & nlocs, bool * mask) { + ASSERT(nlocs == locs.size()); + std::vector data = locs.isInTimeWindow(t1, t2); + std::copy(data.begin(), data.end(), mask); +} +// ----------------------------------------------------------------------------- +} // namespace ufo diff --git a/src/ufo/locations_f.h b/src/ufo/locations_f.h new file mode 100644 index 000000000..301dc6d47 --- /dev/null +++ b/src/ufo/locations_f.h @@ -0,0 +1,30 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_LOCATIONS_F_H_ +#define UFO_LOCATIONS_F_H_ + +#include + +#include "ufo/Locations.h" + +// ----------------------------------------------------------------------------- +// These functions provide a Fortran-callable interface to Locations +// ----------------------------------------------------------------------------- +namespace ufo { + +extern "C" { + std::size_t locations_get_nlocs_f(const Locations &); + void locations_get_lons_f(const Locations &, const std::size_t &, double *); + void locations_get_lats_f(const Locations &, const std::size_t &, double *); + void locations_get_timemask_f(const Locations &, const util::DateTime &, + const util::DateTime &, const std::size_t &, bool *); +} + +} // namespace ufo + +#endif // UFO_LOCATIONS_F_H_ diff --git a/src/ufo/locations_interface.f b/src/ufo/locations_interface.f new file mode 100644 index 000000000..7d9a0e06f --- /dev/null +++ b/src/ufo/locations_interface.f @@ -0,0 +1,48 @@ +! +! (C) Copyright 2020 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Define interface for C++ ufo::Locations code called from Fortran + +!------------------------------------------------------------------------------- +interface +!------------------------------------------------------------------------------- +integer(kind=c_size_t) function c_locations_get_nlocs(locs) bind(C,name='locations_get_nlocs_f') + use, intrinsic :: iso_c_binding + implicit none + + type(c_ptr), value :: locs +end function c_locations_get_nlocs + +subroutine c_locations_get_lons(locs, nlocs, lons) & + & bind(C,name='locations_get_lons_f') + use, intrinsic :: iso_c_binding, only : c_ptr,c_char,c_size_t,c_double + implicit none + type(c_ptr), value :: locs + integer(c_size_t), intent(in) :: nlocs + real(c_double), intent(inout) :: lons(nlocs) +end subroutine c_locations_get_lons + +subroutine c_locations_get_lats(locs, nlocs, lats) & + & bind(C,name='locations_get_lats_f') + use, intrinsic :: iso_c_binding, only : c_ptr,c_char,c_size_t,c_double + implicit none + type(c_ptr), value :: locs + integer(c_size_t), intent(in) :: nlocs + real(c_double), intent(inout) :: lats(nlocs) +end subroutine c_locations_get_lats + +subroutine c_locations_get_timemask(locs, t1, t2, nlocs, mask) & + & bind(C,name='locations_get_timemask_f') + use, intrinsic :: iso_c_binding, only : c_ptr,c_char,c_size_t,c_bool + implicit none + type(c_ptr), value :: locs, t1, t2 + integer(c_size_t), intent(in) :: nlocs + logical(c_bool), intent(inout) :: mask(nlocs) +end subroutine c_locations_get_timemask + +!------------------------------------------------------------------------------- +end interface +!------------------------------------------------------------------------------- diff --git a/src/ufo/marine/CMakeLists.txt b/src/ufo/marine/CMakeLists.txt index 2d3261af8..4adb0b749 100644 --- a/src/ufo/marine/CMakeLists.txt +++ b/src/ufo/marine/CMakeLists.txt @@ -12,11 +12,11 @@ add_subdirectory( seaicefraction ) add_subdirectory( seaicethickness ) add_subdirectory( chleuzintegr ) add_subdirectory( utils ) -if( ${GSW_FOUND} ) +if( ${gsw_FOUND} ) add_subdirectory( insitutemperature ) add_subdirectory( marinevertinterp ) add_subdirectory( obsop ) -endif( ${GSW_FOUND} ) +endif( ${gsw_FOUND} ) PREPEND( _p_utils_files "marine/utils" ${utils_files} ) PREPEND( _p_adt_files "marine/adt" ${adt_files} ) @@ -24,11 +24,11 @@ PREPEND( _p_coolskin_files "marine/coolskin" ${coolskin_fi PREPEND( _p_seaicefraction_files "marine/seaicefraction" ${seaicefraction_files} ) PREPEND( _p_seaicethickness_files "marine/seaicethickness" ${seaicethickness_files} ) PREPEND( _p_chleuzintegr_files "marine/chleuzintegr" ${chleuzintegr_files} ) -if( ${GSW_FOUND} ) +if( ${gsw_FOUND} ) PREPEND( _p_insitutemperature_files "marine/insitutemperature" ${insitutemperature_files} ) PREPEND( _p_marinevertinterp_files "marine/marinevertinterp" ${marinevertinterp_files} ) PREPEND( _p_obsop_files "marine/obsop" ${obsop_files} ) -endif( ${GSW_FOUND} ) +endif( ${gsw_FOUND} ) set ( marine_src_files ${_p_utils_files} diff --git a/src/ufo/marine/adt/ObsADTTLAD.cc b/src/ufo/marine/adt/ObsADTTLAD.cc index da1c071da..7252b4cf8 100644 --- a/src/ufo/marine/adt/ObsADTTLAD.cc +++ b/src/ufo/marine/adt/ObsADTTLAD.cc @@ -25,7 +25,7 @@ static LinearObsOperatorMaker makerADTTL_("ADT"); // ----------------------------------------------------------------------------- ObsADTTLAD::ObsADTTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOper_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOper_(0), varin_() { const std::vector vv{"sea_surface_height_above_geoid"}; varin_.reset(new oops::Variables(vv)); @@ -44,21 +44,21 @@ ObsADTTLAD::~ObsADTTLAD() { void ObsADTTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_adt_tlad_settraj_f90(keyOper_, geovals.toFortran(), odb_); + ufo_adt_tlad_settraj_f90(keyOper_, geovals.toFortran(), obsspace()); oops::Log::trace() << "ObsADTTLAD: trajectory set" << std::endl; } // ----------------------------------------------------------------------------- void ObsADTTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_adt_simobs_tl_f90(keyOper_, geovals.toFortran(), odb_, ovec.size(), ovec.toFortran()); + ufo_adt_simobs_tl_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsADTTLAD: tangent linear observation operator run" << std::endl; } // ----------------------------------------------------------------------------- void ObsADTTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_adt_simobs_ad_f90(keyOper_, geovals.toFortran(), odb_, ovec.size(), ovec.toFortran()); + ufo_adt_simobs_ad_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsADTTLAD: adjoint observation operator run" << std::endl; } diff --git a/src/ufo/marine/adt/ObsADTTLAD.h b/src/ufo/marine/adt/ObsADTTLAD.h index 223cfa7d2..e46a77342 100644 --- a/src/ufo/marine/adt/ObsADTTLAD.h +++ b/src/ufo/marine/adt/ObsADTTLAD.h @@ -57,7 +57,6 @@ class ObsADTTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOper_; - const ioda::ObsSpace& odb_; std::unique_ptr varin_; }; diff --git a/src/ufo/marine/adt/ufo_adt_tlad_mod.F90 b/src/ufo/marine/adt/ufo_adt_tlad_mod.F90 index 3c14ed098..b5e79c25a 100644 --- a/src/ufo/marine/adt/ufo_adt_tlad_mod.F90 +++ b/src/ufo/marine/adt/ufo_adt_tlad_mod.F90 @@ -188,8 +188,11 @@ subroutine ufo_adt_simobs_ad(self, geovals, hofx, obss) call f_comm%allreduce(cnt, cnt_glb, fckit_mpi_sum()) offset_hofx = offset_hofx/cnt_glb -if (.not. allocated(geoval_adt%vals)) allocate(geoval_adt%vals(1,nlocs)) -geoval_adt%vals = 0.0 +if (.not. allocated(geoval_adt%vals)) then + allocate(geoval_adt%vals(1,nlocs)) + geoval_adt%nval = 1 + geoval_adt%vals = 0.0 +end if do iobs = 1, nlocs if (hofx(iobs)/=self%r_miss_val) then geoval_adt%vals(1,iobs) = geoval_adt%vals(1,iobs) + hofx(iobs) - offset_hofx diff --git a/src/ufo/marine/coolskin/ObsCoolSkinTLAD.cc b/src/ufo/marine/coolskin/ObsCoolSkinTLAD.cc index 54936f59b..af2fc45ec 100644 --- a/src/ufo/marine/coolskin/ObsCoolSkinTLAD.cc +++ b/src/ufo/marine/coolskin/ObsCoolSkinTLAD.cc @@ -25,7 +25,7 @@ static LinearObsOperatorMaker makerCoolSkinTL_("CoolSkin"); // ----------------------------------------------------------------------------- ObsCoolSkinTLAD::ObsCoolSkinTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOper_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOper_(0), varin_() { const std::vector vv{"sea_surface_temperature", "net_downwelling_shortwave_radiation", @@ -49,21 +49,23 @@ ObsCoolSkinTLAD::~ObsCoolSkinTLAD() { void ObsCoolSkinTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_CoolSkin_tlad_settraj_f90(keyOper_, geovals.toFortran(), odb_); + ufo_CoolSkin_tlad_settraj_f90(keyOper_, geovals.toFortran(), obsspace()); oops::Log::trace() << "ObsCoolSkinTLAD: trajectory set" << std::endl; } // ----------------------------------------------------------------------------- void ObsCoolSkinTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_CoolSkin_simobs_tl_f90(keyOper_, geovals.toFortran(), odb_, ovec.size(), ovec.toFortran()); + ufo_CoolSkin_simobs_tl_f90(keyOper_, geovals.toFortran(), obsspace(), + ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsCoolSkinTLAD: tangent linear observation operator run" << std::endl; } // ----------------------------------------------------------------------------- void ObsCoolSkinTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_CoolSkin_simobs_ad_f90(keyOper_, geovals.toFortran(), odb_, ovec.size(), ovec.toFortran()); + ufo_CoolSkin_simobs_ad_f90(keyOper_, geovals.toFortran(), obsspace(), + ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsCoolSkinTLAD: adjoint observation operator run" << std::endl; } diff --git a/src/ufo/marine/coolskin/ObsCoolSkinTLAD.h b/src/ufo/marine/coolskin/ObsCoolSkinTLAD.h index 3d6f5feac..025980dd1 100644 --- a/src/ufo/marine/coolskin/ObsCoolSkinTLAD.h +++ b/src/ufo/marine/coolskin/ObsCoolSkinTLAD.h @@ -57,7 +57,6 @@ class ObsCoolSkinTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOper_; - const ioda::ObsSpace& odb_; std::unique_ptr varin_; }; diff --git a/src/ufo/marine/coolskin/ufo_coolskin_sim_mod.F90 b/src/ufo/marine/coolskin/ufo_coolskin_sim_mod.F90 index fadd9037f..58f6eccf9 100644 --- a/src/ufo/marine/coolskin/ufo_coolskin_sim_mod.F90 +++ b/src/ufo/marine/coolskin/ufo_coolskin_sim_mod.F90 @@ -65,8 +65,8 @@ subroutine ufo_coolskin_jac(jac,S_ns,H_I,H_s,R_nl,Tdc,u0) Qb = Q0 + c0*H_I !Saunder’s constant - lamda = 6.0*(1.0+((alpha*gr*Qb/(Rou*cw))*(16.0*Rou**2.0 * cw**2.0 * v_w**3.0 /\ - k_t**2.0)* (1/u**4.0))**(0.75))**(-1.0/3.0) + lamda = 6.0*(1.0+((alpha*gr*Qb/(Rou*cw))*(16.0*Rou**2.0 * cw**2.0 * v_w**3.0 & + / k_t**2.0)* (1/u**4.0))**(0.75))**(-1.0/3.0) ! cool layer thickness delta = lamda * v_w / u @@ -79,12 +79,12 @@ subroutine ufo_coolskin_jac(jac,S_ns,H_I,H_s,R_nl,Tdc,u0) const = ((alpha*gr/(Rou*cw))*(16.0*Rou**2.0 * cw**2.0 * v_w**3.0 / k_t**2.0))**(0.75) ! calculate d(fc)/d(delta) - dfc_d_delta = 11 + 3.3E-5 / delta**2 *(1.0-exp(-delta/(8.0E-4))) - 3.3E-5\ + dfc_d_delta = 11 + 3.3E-5 / delta**2 *(1.0-exp(-delta/(8.0E-4))) - 3.3E-5 & / delta * (1.0/(8.0E-4) * exp(-delta/(8.0E-4))) ! calculate d(lamda)/d(Qb) - d_lamda_dQb = -2.0 *(1.0+const *(Qb/u**4) **(0.75))**(-4.0/3.0) *\ - (0.75) * const * (Qb/u**4) ** (-0.25) + d_lamda_dQb = -2.0 *(1.0+const *(Qb/u**4) **(0.75))**(-4.0/3.0) * & + (0.75) * const * (Qb/u**4) ** (-0.25) ! this apears in several of jacobians, I decided to calculate it once (4 eps * sig * Ts**3) y = 4 * eps * sig * Ts**3 diff --git a/src/ufo/marine/coolskin/ufo_coolskin_tlad_mod.F90 b/src/ufo/marine/coolskin/ufo_coolskin_tlad_mod.F90 index 29c9de3a7..28edc4f57 100644 --- a/src/ufo/marine/coolskin/ufo_coolskin_tlad_mod.F90 +++ b/src/ufo/marine/coolskin/ufo_coolskin_tlad_mod.F90 @@ -188,12 +188,36 @@ subroutine ufo_coolskin_simobs_ad(self, geovals, hofx, obss) call ufo_geovals_get_var(geovals, var_sea_fric_vel , u ) ! If called from a model interface, allocate adjoint variables -if (.not. allocated(Td%vals)) allocate(Td%vals(1,nobs)); Td%vals = 0.0 -if (.not. allocated(R_nl%vals)) allocate(R_nl%vals(1,nobs)); R_nl%vals = 0.0 -if (.not. allocated(H_I%vals)) allocate(H_I%vals(1,nobs)); H_I%vals = 0.0 -if (.not. allocated(H_s%vals)) allocate(H_s%vals(1,nobs)); H_s%vals = 0.0 -if (.not. allocated(S_ns%vals)) allocate(S_ns%vals(1,nobs)); S_ns%vals = 0.0 -if (.not. allocated(u%vals)) allocate(u%vals(1,nobs)); u%vals = 0.0 +if (.not. allocated(Td%vals)) then + allocate(Td%vals(1,nobs)) + Td%nval = 1 + Td%vals = 0.0 +end if +if (.not. allocated(R_nl%vals)) then + allocate(R_nl%vals(1,nobs)) + R_nl%nval = 1 + R_nl%vals = 0.0 +end if +if (.not. allocated(H_I%vals)) then + allocate(H_I%vals(1,nobs)) + H_I%nval = 1 + H_I%vals = 0.0 +end if +if (.not. allocated(H_s%vals)) then + allocate(H_s%vals(1,nobs)) + H_s%nval = 1 + H_s%vals = 0.0 +end if +if (.not. allocated(S_ns%vals)) then + allocate(S_ns%vals(1,nobs)) + S_ns%nval = 1 + S_ns%vals = 0.0 + end if +if (.not. allocated(u%vals)) then + allocate(u%vals(1,nobs)) + u%nval = 1 + u%vals = 0.0 +end if ! Apply adjoint obs operator do iobs = 1, nobs diff --git a/src/ufo/marine/insitutemperature/ObsInsituTemperatureTLAD.cc b/src/ufo/marine/insitutemperature/ObsInsituTemperatureTLAD.cc index 01c60bd25..d4d25d86b 100644 --- a/src/ufo/marine/insitutemperature/ObsInsituTemperatureTLAD.cc +++ b/src/ufo/marine/insitutemperature/ObsInsituTemperatureTLAD.cc @@ -27,7 +27,7 @@ static LinearObsOperatorMaker ObsInsituTemperatureTLAD::ObsInsituTemperatureTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOper_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOper_(0), varin_() { const std::vector vv{"sea_water_potential_temperature", "sea_water_salinity", @@ -48,7 +48,7 @@ ObsInsituTemperatureTLAD::~ObsInsituTemperatureTLAD() { void ObsInsituTemperatureTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_insitutemperature_tlad_settraj_f90(keyOper_, geovals.toFortran(), odb_); + ufo_insitutemperature_tlad_settraj_f90(keyOper_, geovals.toFortran(), obsspace()); oops::Log::trace() << "ObsInsituTemperatureTLAD: trajectory set" << std::endl; } @@ -56,7 +56,7 @@ void ObsInsituTemperatureTLAD::setTrajectory(const GeoVaLs & geovals, const ObsB void ObsInsituTemperatureTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_insitutemperature_simobs_tl_f90(keyOper_, geovals.toFortran(), odb_, + ufo_insitutemperature_simobs_tl_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsInsituTemperatureTLAD: TL observation operator run" << std::endl; } @@ -65,7 +65,7 @@ void ObsInsituTemperatureTLAD::simulateObsTL(const GeoVaLs & geovals, void ObsInsituTemperatureTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_insitutemperature_simobs_ad_f90(keyOper_, geovals.toFortran(), odb_, + ufo_insitutemperature_simobs_ad_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsInsituTemperatureTLAD: adjoint observation operator run" << std::endl; } diff --git a/src/ufo/marine/insitutemperature/ObsInsituTemperatureTLAD.h b/src/ufo/marine/insitutemperature/ObsInsituTemperatureTLAD.h index 2e5798ae3..5d65c50af 100644 --- a/src/ufo/marine/insitutemperature/ObsInsituTemperatureTLAD.h +++ b/src/ufo/marine/insitutemperature/ObsInsituTemperatureTLAD.h @@ -57,7 +57,6 @@ class ObsInsituTemperatureTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOper_; - const ioda::ObsSpace& odb_; std::unique_ptr varin_; }; diff --git a/src/ufo/marine/insitutemperature/ufo_insitutemperature_tlad_mod.F90 b/src/ufo/marine/insitutemperature/ufo_insitutemperature_tlad_mod.F90 index 56e388f5b..6a5963733 100644 --- a/src/ufo/marine/insitutemperature/ufo_insitutemperature_tlad_mod.F90 +++ b/src/ufo/marine/insitutemperature/ufo_insitutemperature_tlad_mod.F90 @@ -287,27 +287,35 @@ subroutine ufo_insitutemperature_simobs_ad(self, geovals, hofx, obss) ! check if sea layer thickness variable is in geovals get it and zero it out call ufo_geovals_get_var(geovals, var_ocn_lay_thick, dlayerthick) - + nlev = self%nval nlocs = self%nlocs - - if (.not. allocated(dtemp%vals)) allocate(dtemp%vals(nlev, size(hofx,1))) - if (.not. allocated(dsalt%vals)) allocate(dsalt%vals(nlev, size(hofx,1))) - if (.not. allocated(dlayerthick%vals)) allocate(dlayerthick%vals(nlev, size(hofx,1))) - ! Layer thickness is not a control variable: zero it out! - dlayerthick%vals=0.0 + if (.not. allocated(dtemp%vals)) then + allocate(dtemp%vals(nlev, size(hofx,1))) + dtemp%nval = nlev + dtemp%vals = 0.0 + end if + if (.not. allocated(dsalt%vals)) then + allocate(dsalt%vals(nlev, size(hofx,1))) + dsalt%nval = nlev + dsalt%vals = 0.0 + end if + if (.not. allocated(dlayerthick%vals)) then + allocate(dlayerthick%vals(nlev, size(hofx,1))) + dlayerthick%nval = nlev + dlayerthick%vals = 0.0 + ! Layer thickness is not a control variable: it stays zeroed out! + end if ! backward sea temperature profile obs operator - dtemp%vals = 0.0 - dsalt%vals = 0.0 do iobs = 1, size(hofx,1) if (hofx(iobs) /= missing) then lono = self%lono(iobs) lato = self%lato(iobs) deptho = self%deptho(iobs) - + ! Adjoint obs operator dtp = 0.0 dsp = 0.0 diff --git a/src/ufo/marine/marinevertinterp/ObsMarineVertInterpTLAD.cc b/src/ufo/marine/marinevertinterp/ObsMarineVertInterpTLAD.cc index 2660a5e7f..9567c60c3 100644 --- a/src/ufo/marine/marinevertinterp/ObsMarineVertInterpTLAD.cc +++ b/src/ufo/marine/marinevertinterp/ObsMarineVertInterpTLAD.cc @@ -24,7 +24,7 @@ static LinearObsOperatorMaker makerMarinevertinterpTL_( ObsMarineVertInterpTLAD::ObsMarineVertInterpTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOper_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOper_(0), varin_() { ufo_marinevertinterp_tlad_setup_f90(keyOper_, config, odb.obsvariables(), varin_); @@ -42,14 +42,14 @@ ObsMarineVertInterpTLAD::~ObsMarineVertInterpTLAD() { void ObsMarineVertInterpTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_marinevertinterp_tlad_settraj_f90(keyOper_, geovals.toFortran(), odb_); + ufo_marinevertinterp_tlad_settraj_f90(keyOper_, geovals.toFortran(), obsspace()); oops::Log::trace() << "ObsMarineVertInterpTLAD: trajectory set" << std::endl; } // ----------------------------------------------------------------------------- void ObsMarineVertInterpTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_marinevertinterp_simobs_tl_f90(keyOper_, geovals.toFortran(), odb_, + ufo_marinevertinterp_simobs_tl_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsMarineVertInterpTLAD: TL observation operator run" << std::endl; } @@ -57,7 +57,7 @@ void ObsMarineVertInterpTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVe // ----------------------------------------------------------------------------- void ObsMarineVertInterpTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_marinevertinterp_simobs_ad_f90(keyOper_, geovals.toFortran(), odb_, + ufo_marinevertinterp_simobs_ad_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsMarineVertInterpTLAD: adjoint observation operator run" << std::endl; } diff --git a/src/ufo/marine/marinevertinterp/ObsMarineVertInterpTLAD.h b/src/ufo/marine/marinevertinterp/ObsMarineVertInterpTLAD.h index f4f5fb311..a5625d596 100644 --- a/src/ufo/marine/marinevertinterp/ObsMarineVertInterpTLAD.h +++ b/src/ufo/marine/marinevertinterp/ObsMarineVertInterpTLAD.h @@ -56,7 +56,6 @@ class ObsMarineVertInterpTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOper_; - const ioda::ObsSpace& odb_; oops::Variables varin_; }; diff --git a/src/ufo/marine/marinevertinterp/ufo_marinevertinterp_tlad_mod.F90 b/src/ufo/marine/marinevertinterp/ufo_marinevertinterp_tlad_mod.F90 index f0d8a624f..6ec691ec1 100644 --- a/src/ufo/marine/marinevertinterp/ufo_marinevertinterp_tlad_mod.F90 +++ b/src/ufo/marine/marinevertinterp/ufo_marinevertinterp_tlad_mod.F90 @@ -223,11 +223,15 @@ subroutine ufo_marinevertinterp_simobs_ad(self, geovals, hofx, obss) nlev = self%nval nlocs = self%nlocs - - if (.not. allocated(dvar%vals)) allocate(dvar%vals(nlev, size(hofx,1))) + + if (.not. allocated(dvar%vals)) then + allocate(dvar%vals(nlev, size(hofx,1))) + dvar%nval = nlev + dvar%vals = 0.0 + end if + ! backward vertical interp - dvar%vals = 0.0 do iobs = 1, size(hofx,1) if (hofx(iobs) /= missing) then deptho = self%deptho(iobs) diff --git a/src/ufo/marine/seaicefraction/ObsSeaIceFractionTLAD.cc b/src/ufo/marine/seaicefraction/ObsSeaIceFractionTLAD.cc index bb58a9127..466e75dc7 100644 --- a/src/ufo/marine/seaicefraction/ObsSeaIceFractionTLAD.cc +++ b/src/ufo/marine/seaicefraction/ObsSeaIceFractionTLAD.cc @@ -27,7 +27,7 @@ static LinearObsOperatorMaker makerSeaIceFractionTL_("Sea ObsSeaIceFractionTLAD::ObsSeaIceFractionTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOper_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOper_(0), varin_() { const std::vector vv{"sea_ice_category_area_fraction"}; varin_.reset(new oops::Variables(vv)); @@ -48,7 +48,7 @@ ObsSeaIceFractionTLAD::~ObsSeaIceFractionTLAD() { void ObsSeaIceFractionTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_seaicelinear_settraj_f90(keyOper_, geovals.toFortran(), odb_); + ufo_seaicelinear_settraj_f90(keyOper_, geovals.toFortran(), obsspace()); oops::Log::trace() << "ObsSeaIceFractionTLAD: trajectory set" << std::endl; } @@ -71,7 +71,8 @@ void ObsSeaIceFractionTLAD::simulateObsTL(const GeoVaLs & gv, ioda::ObsVector & // ----------------------------------------------------------------------------- void ObsSeaIceFractionTLAD::simulateObsAD(GeoVaLs & gv, const ioda::ObsVector & ovec) const { - ufo_seaicelinear_alloc_ad_f90(keyOper_, gv.toFortran(), odb_, ovec.size(), ovec.toFortran()); + ufo_seaicelinear_alloc_ad_f90(keyOper_, gv.toFortran(), obsspace(), + ovec.size(), ovec.toFortran()); int nlocs = ovec.size(); int nlevs = gv.nlevs("sea_ice_category_area_fraction"); float miss = 0.0; diff --git a/src/ufo/marine/seaicefraction/ObsSeaIceFractionTLAD.h b/src/ufo/marine/seaicefraction/ObsSeaIceFractionTLAD.h index 8595ef243..ef5891c8c 100644 --- a/src/ufo/marine/seaicefraction/ObsSeaIceFractionTLAD.h +++ b/src/ufo/marine/seaicefraction/ObsSeaIceFractionTLAD.h @@ -57,7 +57,6 @@ class ObsSeaIceFractionTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOper_; - const ioda::ObsSpace& odb_; std::unique_ptr varin_; }; diff --git a/src/ufo/marine/seaicethickness/ObsSeaIceThicknessTLAD.cc b/src/ufo/marine/seaicethickness/ObsSeaIceThicknessTLAD.cc index 7673f0143..aa64f4300 100644 --- a/src/ufo/marine/seaicethickness/ObsSeaIceThicknessTLAD.cc +++ b/src/ufo/marine/seaicethickness/ObsSeaIceThicknessTLAD.cc @@ -26,7 +26,7 @@ static LinearObsOperatorMaker makerSeaIceThicknessTL_("S ObsSeaIceThicknessTLAD::ObsSeaIceThicknessTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOper_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOper_(0), varin_() { const std::vector vv{"sea_ice_category_area_fraction", "sea_ice_category_thickness"}; @@ -46,14 +46,14 @@ ObsSeaIceThicknessTLAD::~ObsSeaIceThicknessTLAD() { void ObsSeaIceThicknessTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics &) { - ufo_seaicethickness_tlad_settraj_f90(keyOper_, geovals.toFortran(), odb_); + ufo_seaicethickness_tlad_settraj_f90(keyOper_, geovals.toFortran(), obsspace()); oops::Log::trace() << "ObsSeaIceThicknessTLAD: trajectory set" << std::endl; } // ----------------------------------------------------------------------------- void ObsSeaIceThicknessTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_seaicethickness_simobs_tl_f90(keyOper_, geovals.toFortran(), odb_, + ufo_seaicethickness_simobs_tl_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsSeaIceThicknessTLAD: TL observation operator run" << std::endl; } @@ -61,7 +61,7 @@ void ObsSeaIceThicknessTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVec // ----------------------------------------------------------------------------- void ObsSeaIceThicknessTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_seaicethickness_simobs_ad_f90(keyOper_, geovals.toFortran(), odb_, + ufo_seaicethickness_simobs_ad_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsSeaIceThicknessTLAD: adjoint observation operator run" << std::endl; } diff --git a/src/ufo/marine/seaicethickness/ObsSeaIceThicknessTLAD.h b/src/ufo/marine/seaicethickness/ObsSeaIceThicknessTLAD.h index 53fec70ef..7ceae69b2 100644 --- a/src/ufo/marine/seaicethickness/ObsSeaIceThicknessTLAD.h +++ b/src/ufo/marine/seaicethickness/ObsSeaIceThicknessTLAD.h @@ -57,7 +57,6 @@ class ObsSeaIceThicknessTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOper_; - const ioda::ObsSpace& odb_; std::unique_ptr varin_; }; diff --git a/src/ufo/marine/seaicethickness/ufo_seaicethickness_mod.F90 b/src/ufo/marine/seaicethickness/ufo_seaicethickness_mod.F90 index 6213fafa6..c3dd05fbf 100644 --- a/src/ufo/marine/seaicethickness/ufo_seaicethickness_mod.F90 +++ b/src/ufo/marine/seaicethickness/ufo_seaicethickness_mod.F90 @@ -61,6 +61,7 @@ end subroutine ufo_seaicethickness_delete ! ------------------------------------------------------------------------------ subroutine ufo_seaicethickness_simobs(self, geovals, hofx, obss) +use ufo_utils_mod, only: cmp_strings implicit none class(ufo_seaicethickness), intent(in) :: self type(ufo_geovals), intent(in) :: geovals @@ -80,15 +81,15 @@ subroutine ufo_seaicethickness_simobs(self, geovals, hofx, obss) call abor1_ftn(err_msg) endif - if (trim(self%obsvars%variable(1)) == "sea_ice_freeboard") then + if (cmp_strings(self%obsvars%variable(1), "sea_ice_freeboard")) then rho_wiw = (self%rho_water-self%rho_ice)/self%rho_water - rho_wsw = (self%rho_water-self%rho_snow)/self%rho_water + rho_wsw = (-self%rho_snow)/self%rho_water endif ! check if sea ice fraction variable is in geovals and get it call ufo_geovals_get_var(geovals, var_seaicefrac, icefrac) ! check if snow thickness variable is in geovals and get it - if (trim(self%obsvars%variable(1)) == "sea_ice_freeboard") & + if (cmp_strings(self%obsvars%variable(1), "sea_ice_freeboard")) & call ufo_geovals_get_var(geovals, var_seaicesnowthick, snowthick) ! check if sea ice thickness variable is in geovals and get it call ufo_geovals_get_var(geovals, var_seaicethick, icethick) diff --git a/src/ufo/marine/seaicethickness/ufo_seaicethickness_tlad_mod.F90 b/src/ufo/marine/seaicethickness/ufo_seaicethickness_tlad_mod.F90 index 6db1eb8bf..76131b8d7 100644 --- a/src/ufo/marine/seaicethickness/ufo_seaicethickness_tlad_mod.F90 +++ b/src/ufo/marine/seaicethickness/ufo_seaicethickness_tlad_mod.F90 @@ -17,6 +17,7 @@ module ufo_seaicethickness_tlad_mod use obsspace_mod use missing_values_mod use oops_variables_mod + use ufo_utils_mod, only: cmp_strings implicit none private @@ -91,7 +92,7 @@ subroutine ufo_seaicethickness_tlad_settraj(self, geovals, obss) ! check if sea ice fraction variables is in geovals and get it call ufo_geovals_get_var(geovals, var_seaicefrac, icefrac) -if (trim(self%obsvars%variable(1)) == "sea_ice_freeboard") then +if (cmp_strings(self%obsvars%variable(1), "sea_ice_freeboard")) then call ufo_geovals_get_var(geovals, var_seaicesnowthick, snowthick) self%snowthick= snowthick endif @@ -135,9 +136,9 @@ subroutine ufo_seaicethickness_simobs_tl(self, geovals, hofx, obss) ! check if sea ice thickness variable is in geovals and get it call ufo_geovals_get_var(geovals, var_seaicethick, icethick_d) -if (trim(self%obsvars%variable(1)) == "sea_ice_freeboard") then +if (cmp_strings(self%obsvars%variable(1), "sea_ice_freeboard")) then rho_wiw = (self%rho_water-self%rho_ice)/self%rho_water - rho_wsw = (self%rho_water-self%rho_snow)/self%rho_water + rho_wsw = (-self%rho_snow)/self%rho_water endif ! sea ice thickness obs operator @@ -200,9 +201,9 @@ subroutine ufo_seaicethickness_simobs_ad(self, geovals, hofx, obss) call abor1_ftn(err_msg) endif -if (trim(self%obsvars%variable(1)) == "sea_ice_freeboard") then +if (cmp_strings(self%obsvars%variable(1), "sea_ice_freeboard")) then rho_wiw = (self%rho_water-self%rho_ice)/self%rho_water - rho_wsw = (self%rho_water-self%rho_snow)/self%rho_water + rho_wsw = (-self%rho_snow)/self%rho_water endif if (.not. geovals%linit ) geovals%linit=.true. @@ -212,23 +213,23 @@ subroutine ufo_seaicethickness_simobs_ad(self, geovals, hofx, obss) call ufo_geovals_get_var(geovals, var_seaicethick, icethick_d) ncat = self%icethick%nval -if (.not.(allocated(icefrac_d%vals) .or. .not. allocated(icethick_d%vals))) then - if (ncat < 1) then - write(err_msg,*) myname_, ' unknown number of categories' - call abor1_ftn(err_msg) - endif - if (.not. allocated(icefrac_d%vals)) allocate(icefrac_d%vals(ncat,size(hofx,1))) - if (.not. allocated(icethick_d%vals)) allocate(icethick_d%vals(ncat, size(hofx,1))) -end if +if (ncat < 1) then + write(err_msg,*) myname_, ' unknown number of categories' + call abor1_ftn(err_msg) +endif +if (.not. allocated(icefrac_d%vals)) then + icefrac_d%nval = ncat + allocate(icefrac_d%vals(ncat,size(hofx,1))) + icefrac_d%vals = 0.0 +endif +if (.not. allocated(icethick_d%vals)) then + icethick_d%nval = ncat + allocate(icethick_d%vals(ncat, size(hofx,1))) + icethick_d%vals = 0.0 +endif ! backward sea ice thickness obs operator -if (.not. allocated(icefrac_d%vals)) allocate(icefrac_d%vals(ncat,size(hofx,1))) -if (.not. allocated(icethick_d%vals)) allocate(icethick_d%vals(ncat, size(hofx,1))) - -icethick_d%vals = 0.0 -icefrac_d%vals = 0.0 - select case (trim(self%obsvars%variable(1))) case ("sea_ice_freeboard") do iobs = 1, size(hofx,1) diff --git a/src/ufo/marine/utils/ObsSeaIceLinear.interface.F90 b/src/ufo/marine/utils/ObsSeaIceLinear.interface.F90 index 9aa312ca6..3a1a877fa 100644 --- a/src/ufo/marine/utils/ObsSeaIceLinear.interface.F90 +++ b/src/ufo/marine/utils/ObsSeaIceLinear.interface.F90 @@ -117,7 +117,9 @@ subroutine ufo_seaicelinear_alloc_ad_c(c_key_self, c_key_geovals, c_obsspace, c_ write(err_msg,*)' unknown number of categories' call abor1_ftn(err_msg) endif + geoval%nval = self%ncat allocate(geoval%vals(self%ncat,size(c_hofx,1))) + geoval%vals = 0.0 end if end subroutine ufo_seaicelinear_alloc_ad_c diff --git a/src/ufo/obslocalization/CMakeLists.txt b/src/ufo/obslocalization/CMakeLists.txt new file mode 100644 index 000000000..a784130a4 --- /dev/null +++ b/src/ufo/obslocalization/CMakeLists.txt @@ -0,0 +1,20 @@ +# (C) Copyright 2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( obslocalization_files + ObsLocGC99.h + ObsLocSOAR.h + ObsLocSOARParameters.h + ObsLocalization.h + ObsLocParameters.cc + ObsLocParameters.h +) + +PREPEND( _p_obslocalization_files "obslocalization" ${obslocalization_files} ) + +set ( obslocalization_src_files + ${_p_obslocalization_files} + PARENT_SCOPE +) diff --git a/src/ufo/obslocalization/ObsLocGC99.h b/src/ufo/obslocalization/ObsLocGC99.h new file mode 100644 index 000000000..2504d506e --- /dev/null +++ b/src/ufo/obslocalization/ObsLocGC99.h @@ -0,0 +1,92 @@ +/* + * (C) Copyright 2020-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_OBSLOCALIZATION_OBSLOCGC99_H_ +#define UFO_OBSLOCALIZATION_OBSLOCGC99_H_ + +#include +#include + +#include "eckit/config/Configuration.h" + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/generic/gc99.h" + +#include "ufo/obslocalization/ObsLocalization.h" +#include "ufo/obslocalization/ObsLocParameters.h" + +namespace ufo { + +/// Horizontal Gaspari-Cohn observation space localization +template +class ObsLocGC99: public ufo::ObsLocalization { + typedef typename MODEL::GeometryIterator GeometryIterator_; + + public: + ObsLocGC99(const eckit::Configuration &, const ioda::ObsSpace &); + + /// compute localization and save localization values in \p obsvector and + /// localization flags (1: outside of localization; 0: inside localization area) + /// in \p outside + void computeLocalization(const GeometryIterator_ &, ioda::ObsDataVector & outside, + ioda::ObsVector & obsvector) const override; + + private: + void print(std::ostream &) const override; +}; +// ----------------------------------------------------------------------------- + +template +ObsLocGC99::ObsLocGC99(const eckit::Configuration & config, + const ioda::ObsSpace & obsspace): + ObsLocalization::ObsLocalization(config, obsspace) { + const ObsLocParameters & options = ObsLocalization::localizationOptions(); + oops::Log::debug() << "Gaspari-Cohn horizontal localization with " << options.lengthscale + << " lengthscale" << std::endl; +} + +// ----------------------------------------------------------------------------- + +template +void ObsLocGC99::computeLocalization(const GeometryIterator_ & i, + ioda::ObsDataVector & outside, + ioda::ObsVector & locvector) const { + oops::Log::trace() << "ObsLocGC99::computeLocalization" << std::endl; + + // do distance search and compute box-car locvector + ObsLocalization::computeLocalization(i, outside, locvector); + + // return refs to internals of ObsLocalization + const std::vector & localobs = ObsLocalization::localobs(); + const std::vector & horizontalObsdist = ObsLocalization::horizontalObsdist(); + const ObsLocParameters & options = ObsLocalization::localizationOptions(); + + const size_t nvars = locvector.nvars(); + for (size_t jlocal = 0; jlocal < localobs.size(); ++jlocal) { + double locFactor = oops::gc99(horizontalObsdist[jlocal] / options.lengthscale); + // obsdist is calculated at each location; need to update R for each variable + for (size_t jvar = 0; jvar < nvars; ++jvar) { + locvector[jvar + localobs[jlocal] * nvars] *= locFactor; + } + } +} + +// ----------------------------------------------------------------------------- + +template +void ObsLocGC99::print(std::ostream & os) const { + const ObsLocParameters & options = ObsLocalization::localizationOptions(); + os << "Gaspari-Cohn horizontal localization with " << options.lengthscale + << " lengthscale" << std::endl; +} + +} // namespace ufo + +#endif // UFO_OBSLOCALIZATION_OBSLOCGC99_H_ diff --git a/src/ufo/obslocalization/ObsLocParameters.cc b/src/ufo/obslocalization/ObsLocParameters.cc new file mode 100644 index 000000000..a41f4a9d3 --- /dev/null +++ b/src/ufo/obslocalization/ObsLocParameters.cc @@ -0,0 +1,22 @@ +/* + * (C) Copyright 2020-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include + +#include "ufo/obslocalization/ObsLocParameters.h" + +namespace ufo { + +constexpr char DistanceTypeParameterTraitsHelper::enumTypeName[]; +constexpr util::NamedEnumerator DistanceTypeParameterTraitsHelper::namedValues[]; + +constexpr char SearchMethodParameterTraitsHelper::enumTypeName[]; +constexpr util::NamedEnumerator SearchMethodParameterTraitsHelper::namedValues[]; + +constexpr double ObsLocParameters::radius_earth; + +} // namespace ufo diff --git a/src/ufo/obslocalization/ObsLocParameters.h b/src/ufo/obslocalization/ObsLocParameters.h new file mode 100644 index 000000000..22d2a5677 --- /dev/null +++ b/src/ufo/obslocalization/ObsLocParameters.h @@ -0,0 +1,112 @@ +/* + * (C) Copyright 2020-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_OBSLOCALIZATION_OBSLOCPARAMETERS_H_ +#define UFO_OBSLOCALIZATION_OBSLOCPARAMETERS_H_ + +#include +#include + +#include "eckit/exception/Exceptions.h" +#include "eckit/geometry/KPoint.h" +#include "eckit/geometry/Point2.h" +#include "eckit/geometry/UnitSphere.h" + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +namespace eckit { + class Configuration; +} + +namespace ufo { + +enum class DistanceType { + GEODESIC, CARTESIAN +}; + +enum class SearchMethod { + BRUTEFORCE, KDTREE +}; + +struct DistanceTypeParameterTraitsHelper { + typedef DistanceType EnumType; + static constexpr char enumTypeName[] = "DistanceType"; + static constexpr util::NamedEnumerator namedValues[] = { + { DistanceType::GEODESIC, "geodesic" }, + { DistanceType::CARTESIAN, "cartesian" } + }; +}; + +struct SearchMethodParameterTraitsHelper { + typedef SearchMethod EnumType; + static constexpr char enumTypeName[] = "SearchMethod"; + static constexpr util::NamedEnumerator namedValues[] = { + { SearchMethod::BRUTEFORCE, "brute_force" }, + { SearchMethod::KDTREE, "kd_tree" } + }; +}; + +} // namespace ufo + +namespace oops { + +/// Extraction of DistanceType parameters from config +template <> +struct ParameterTraits : + public EnumParameterTraits +{}; + + +/// Extraction of SearchMethod parameters from config +template <> +struct ParameterTraits : + public EnumParameterTraits +{}; + +} // namespace oops + +namespace ufo { + +/// \brief Options controlling local observations subsetting +class ObsLocParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsLocParameters, Parameters) + + public: + /// Localization lengthscale (find all obs within the distance from reference point) + oops::RequiredParameter lengthscale{"lengthscale", this}; + + /// Method for searching for nearest points: brute force or KD-tree + oops::Parameter searchMethod{"search method", SearchMethod::BRUTEFORCE, this}; + + /// Maximum number of obs + oops::OptionalParameter maxnobs{"max nobs", this}; + + /// Distance calculation type: geodesic (on sphere) or cartesian (euclidian) + /// Default: geodesic + oops::Parameter distanceType{"distance type", DistanceType::GEODESIC, this}; + + /// returns distance between points \p p1 and \p p2, depending on the + /// distance calculation type distanceType + double distance(const eckit::geometry::Point2 & p1, const eckit::geometry::Point2 & p2) const { + if (distanceType == DistanceType::GEODESIC) { + return eckit::geometry::Sphere::distance(radius_earth, p1, p2); + } else { + ASSERT(distanceType == DistanceType::CARTESIAN); + return p1.distance(p2); + } + } + + // Earth radius in m + static constexpr double radius_earth = 6.371e6; +}; + +} // namespace ufo + +#endif // UFO_OBSLOCALIZATION_OBSLOCPARAMETERS_H_ diff --git a/src/ufo/obslocalization/ObsLocSOAR.h b/src/ufo/obslocalization/ObsLocSOAR.h new file mode 100644 index 000000000..58326e4bb --- /dev/null +++ b/src/ufo/obslocalization/ObsLocSOAR.h @@ -0,0 +1,91 @@ +/* + * (C) Copyright 2020-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_OBSLOCALIZATION_OBSLOCSOAR_H_ +#define UFO_OBSLOCALIZATION_OBSLOCSOAR_H_ + +#include +#include + +#include "eckit/config/Configuration.h" + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/generic/soar.h" + +#include "ufo/obslocalization/ObsLocalization.h" +#include "ufo/obslocalization/ObsLocSOARParameters.h" + +namespace ufo { + +/// Horizontal SOAR observation space localization +template +class ObsLocSOAR: public ufo::ObsLocalization { + typedef typename MODEL::GeometryIterator GeometryIterator_; + + public: + ObsLocSOAR(const eckit::Configuration &, const ioda::ObsSpace &); + + /// compute localization and save localization values in \p obsvector and + /// localization flags (1: outside of localization; 0: inside localization area) + /// in \p outside + void computeLocalization(const GeometryIterator_ &, ioda::ObsDataVector & outside, + ioda::ObsVector & obsvector) const override; + + private: + void print(std::ostream &) const override; + + ObsLocSOARParameters options_; +}; +// ----------------------------------------------------------------------------- + +template +ObsLocSOAR::ObsLocSOAR(const eckit::Configuration & config, + const ioda::ObsSpace & obsspace): + ObsLocalization::ObsLocalization(config, obsspace) { + options_.deserialize(config); + oops::Log::debug()<< "SOAR horizontal localization with " << options_.SOARexpDecayH + << " soar decay" << std::endl; +} +// ----------------------------------------------------------------------------- + +template +void ObsLocSOAR::computeLocalization(const GeometryIterator_ & i, + ioda::ObsDataVector & outside, + ioda::ObsVector & locvector) const { + oops::Log::trace() << "ObsLocSOAR::computeLocalization" << std::endl; + // do distance search and compute box-car locvector + ObsLocalization::computeLocalization(i, outside, locvector); + + // return refs to internals of ObsLocalization + const std::vector & localobs = ObsLocalization::localobs(); + const std::vector & horizontalObsdist = ObsLocalization::horizontalObsdist(); + + const double SOARexpDecayH = options_.SOARexpDecayH; + const size_t nvars = locvector.nvars(); + for (size_t jlocal = 0; jlocal < localobs.size(); ++jlocal) { + double locFactor = oops::soar(horizontalObsdist[jlocal]*SOARexpDecayH); + // obsdist is calculated at each location; need to update R for each variable + for (size_t jvar = 0; jvar < nvars; ++jvar) { + locvector[jvar + localobs[jlocal] * nvars] *= locFactor; + } + } +} + +// ----------------------------------------------------------------------------- + +template +void ObsLocSOAR::print(std::ostream & os) const { + os << "SOAR horizontal localization with " << options_.SOARexpDecayH + << " soar decay" << std::endl; +} + +} // namespace ufo + +#endif // UFO_OBSLOCALIZATION_OBSLOCSOAR_H_ diff --git a/src/ufo/obslocalization/ObsLocSOARParameters.h b/src/ufo/obslocalization/ObsLocSOARParameters.h new file mode 100644 index 000000000..aeec33533 --- /dev/null +++ b/src/ufo/obslocalization/ObsLocSOARParameters.h @@ -0,0 +1,30 @@ +/* + * (C) Copyright 2021-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_OBSLOCALIZATION_OBSLOCSOARPARAMETERS_H_ +#define UFO_OBSLOCALIZATION_OBSLOCSOARPARAMETERS_H_ + +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/obslocalization/ObsLocParameters.h" + +namespace ufo { + +/// \brief Options controlling SOAR obs localization. Inherits +/// options from general horizontal obs localization. +class ObsLocSOARParameters : public ObsLocParameters { + OOPS_CONCRETE_PARAMETERS(ObsLocSOARParameters, ObsLocParameters) + + public: + /// The SOAR function decay parameter + oops::RequiredParameter SOARexpDecayH{"soar horizontal decay", this}; +}; + +} // namespace ufo + +#endif // UFO_OBSLOCALIZATION_OBSLOCSOARPARAMETERS_H_ diff --git a/src/ufo/obslocalization/ObsLocalization.h b/src/ufo/obslocalization/ObsLocalization.h new file mode 100644 index 000000000..9fff9b85d --- /dev/null +++ b/src/ufo/obslocalization/ObsLocalization.h @@ -0,0 +1,235 @@ +/* + * (C) Copyright 2020-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_OBSLOCALIZATION_OBSLOCALIZATION_H_ +#define UFO_OBSLOCALIZATION_OBSLOCALIZATION_H_ + +#include +#include +#include +#include +#include +#include + +#include "atlas/util/Earth.h" + +#include "eckit/config/Configuration.h" +#include "eckit/container/KDTree.h" +#include "eckit/geometry/Point2.h" +#include "eckit/geometry/Point3.h" +#include "eckit/geometry/UnitSphere.h" + +#include "ioda/ObsDataVector.h" +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/base/ObsLocalizationBase.h" + +#include "ufo/obslocalization/ObsLocParameters.h" +#include "ufo/ObsTraits.h" + +namespace ufo { + +/// Horizontal Box car observation space localization +template +class ObsLocalization: public oops::ObsLocalizationBase { + typedef typename MODEL::GeometryIterator GeometryIterator_; + + public: + struct TreeTrait { + typedef eckit::geometry::Point3 Point; + typedef double Payload; + }; + typedef eckit::KDTreeMemory KDTree; + ObsLocalization(const eckit::Configuration &, const ioda::ObsSpace &); + + /// compute localization and save localization values in \p obsvector and + /// localization flags (1: outside of localization; 0: inside localization area) + /// in \p outside + void computeLocalization(const GeometryIterator_ &, ioda::ObsDataVector & outside, + ioda::ObsVector & obsvector) const override; + + const std::vector & localobs() const {return localobs_;} + const std::vector & horizontalObsdist() const {return obsdist_;} + const ObsLocParameters & localizationOptions() const {return options_;} + + private: + ObsLocParameters options_; + mutable std::vector obsdist_; + mutable std::vector localobs_; + + void print(std::ostream &) const override; + + /// KD-tree for searching for local obs + std::unique_ptr kd_; + + std::vector lats_; + std::vector lons_; + + /// TODO(travis) distribution name is needed for temporary fix, should be removed eventually + std::string distName_; +}; + +// ----------------------------------------------------------------------------- + +/*! + * \details Creates a KDTree class member that can be used for searching for local obs + */ +template +ObsLocalization::ObsLocalization(const eckit::Configuration & config, + const ioda::ObsSpace & obsspace) + : lats_(obsspace.nlocs()), lons_(obsspace.nlocs()) +{ + options_.deserialize(config); + + // check that this distribution supports local obs space + // TODO(travis) this has been moved to computeLocalization as a quick fix for a bug. + distName_ = obsspace.distribution()->name(); + + const size_t nlocs = obsspace.nlocs(); + // Get latitudes and longitudes of all observations. + obsspace.get_db("MetaData", "longitude", lons_); + obsspace.get_db("MetaData", "latitude", lats_); + + if (options_.searchMethod == SearchMethod::KDTREE) { + kd_ = std::unique_ptr ( new KDTree() ); + // Define points list from lat/lon values + typedef typename KDTree::PointType Point; + std::vector points; + for (unsigned int i = 0; i < nlocs; i++) { + eckit::geometry::Point2 lonlat(lons_[i], lats_[i]); + Point xyz = Point(); + // FIXME: get geometry from yaml, for now assume spherical Earth radius. + atlas::util::Earth::convertSphericalToCartesian(lonlat, xyz); + double index = static_cast(i); + typename KDTree::Value v(xyz, index); + points.push_back(v); + } + // Create KDTree class member from points list. + kd_->build(points.begin(), points.end()); + } +} + +// ----------------------------------------------------------------------------- + +template +void ObsLocalization::computeLocalization(const GeometryIterator_ & i, + ioda::ObsDataVector & outside, + ioda::ObsVector & locvector) const { + oops::Log::trace() << "ObsLocalization::computeLocalization" << std::endl; + + // check that this distribution supports local obs space + // TODO(travis) this should be in the constructor, but currently + // breaks LETKF when using a split observer/solver + if ( distName_ != "Halo" && distName_ != "InefficientDistribution" ) { + std::string message = "Can not use ObsLocalization with distribution=" + distName_; + throw eckit::BadParameter(message); + } + + // clear arrays before proceeding + localobs_.clear(); + obsdist_.clear(); + + eckit::geometry::Point2 refPoint = *i; + size_t nlocs = lons_.size(); + if ( options_.searchMethod == SearchMethod::BRUTEFORCE ) { + oops::Log::trace() << "Local obs searching via brute force." << std::endl; + + for (unsigned int jj = 0; jj < nlocs; ++jj) { + eckit::geometry::Point2 searchPoint(lons_[jj], lats_[jj]); + double localDist = options_.distance(refPoint, searchPoint); + if ( localDist < options_.lengthscale ) { + localobs_.push_back(jj); + obsdist_.push_back(localDist); + } + } + const boost::optional & maxnobs = options_.maxnobs; + if ( (maxnobs != boost::none) && (localobs_.size() > *maxnobs ) ) { + for (unsigned int jj = 0; jj < localobs_.size(); ++jj) { + oops::Log::debug() << "Before sort [i, d]: " << localobs_[jj] + << " , " << obsdist_[jj] << std::endl; + } + // Construct a temporary paired vector to do the sorting + std::vector> localObsIndDistPair; + for (unsigned int jj = 0; jj < obsdist_.size(); ++jj) { + localObsIndDistPair.push_back(std::make_pair(localobs_[jj], obsdist_[jj])); + } + + // Use a lambda function to implement an ascending sort. + sort(localObsIndDistPair.begin(), localObsIndDistPair.end(), + [](const std::pair & p1, + const std::pair & p2){ + return(p1.second < p2.second); + }); + + // Unpair the sorted pair vector + for (unsigned int jj = 0; jj < obsdist_.size(); ++jj) { + localobs_[jj] = localObsIndDistPair[jj].first; + obsdist_[jj] = localObsIndDistPair[jj].second; + } + + // Truncate to maxNobs length + localobs_.resize(*maxnobs); + obsdist_.resize(*maxnobs); + } + } else if (nlocs > 0) { + // Check (nlocs > 0) is needed, + // otherwise, it will cause ASERT check fail in kdtree.findInSphere, and hang. + + oops::Log::trace() << "Local obs searching via KDTree" << std::endl; + + if ( options_.distanceType == DistanceType::CARTESIAN) + ABORT("ObsLocalization:: search method must be 'brute_force' when using 'cartesian' distance"); + + // Using the radius of the earth + eckit::geometry::Point3 refPoint3D; + atlas::util::Earth::convertSphericalToCartesian(refPoint, refPoint3D); + double alpha = (options_.lengthscale / options_.radius_earth)/ 2.0; // angle in radians + double chordLength = 2.0*options_.radius_earth * sin(alpha); // search radius in 3D space + + auto closePoints = kd_->findInSphere(refPoint3D, chordLength); + + // put closePoints back into localobs_ and obsdist + for (unsigned int jloc = 0; jloc < closePoints.size(); ++jloc) { + localobs_.push_back(closePoints[jloc].payload()); // observation + obsdist_.push_back(closePoints[jloc].distance()); // distance + } + + // The obs are sorted in the kdtree call + const boost::optional & maxnobs = options_.maxnobs; + if ( (maxnobs != boost::none) && (localobs_.size() > *maxnobs ) ) { + // Truncate to maxNobs length + localobs_.resize(*maxnobs); + obsdist_.resize(*maxnobs); + } + } + for (size_t jloc = 0; jloc < outside.nlocs(); ++jloc) { + for (size_t jvar = 0; jvar < outside.nvars(); ++jvar) { + outside[jvar][jloc] = 1; + } + } + const size_t nvars = locvector.nvars(); + for (size_t jlocal = 0; jlocal < localobs_.size(); ++jlocal) { + // obsdist is calculated at each location; need to update R for each variable + for (size_t jvar = 0; jvar < nvars; ++jvar) { + outside[jvar][localobs_[jlocal]] = 0; + locvector[jvar + localobs_[jlocal] * nvars] = 1.0; + } + } +} + +// ----------------------------------------------------------------------------- + +template +void ObsLocalization::print(std::ostream & os) const { + os << "ObsLocalization (box car) horizontal localization with " << options_.lengthscale + << " lengthscale" << std::endl; +} + +} // namespace ufo + +#endif // UFO_OBSLOCALIZATION_OBSLOCALIZATION_H_ diff --git a/src/ufo/predictors/CMakeLists.txt b/src/ufo/predictors/CMakeLists.txt index 0677159db..275c5eff5 100644 --- a/src/ufo/predictors/CMakeLists.txt +++ b/src/ufo/predictors/CMakeLists.txt @@ -6,18 +6,28 @@ set ( predictor_files PredictorBase.h PredictorBase.cc + CloudLiquidWater.h + CloudLiquidWater.cc Constant.h Constant.cc CosineOfLatitudeTimesOrbitNode.h CosineOfLatitudeTimesOrbitNode.cc Emissivity.h Emissivity.cc + InterpolateDataFromFile.h + InterpolateDataFromFile.cc LapseRate.h LapseRate.cc + Legendre.h + Legendre.cc + OrbitalAngle.h + OrbitalAngle.cc ScanAngle.h ScanAngle.cc SineOfLatitude.h SineOfLatitude.cc + Thickness.h + Thickness.cc ) PREPEND( _p_predictor_files "predictors" ${predictor_files} ) diff --git a/src/ufo/predictors/CloudLiquidWater.cc b/src/ufo/predictors/CloudLiquidWater.cc new file mode 100644 index 000000000..980f0faa8 --- /dev/null +++ b/src/ufo/predictors/CloudLiquidWater.cc @@ -0,0 +1,110 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include + +#include "eckit/exception/Exceptions.h" + +#include "ioda/ObsSpace.h" + +#include "oops/util/missingValues.h" + +#include "ufo/filters/obsfunctions/CLWRetMW_SSMIS.h" +#include "ufo/GeoVaLs.h" +#include "ufo/predictors/CloudLiquidWater.h" +#include "ufo/utils/Constants.h" +namespace ufo { + +static PredictorMaker + makerFuncCloudLiquidWater_("cloud_liquid_water"); + +// ----------------------------------------------------------------------------- + +CloudLiquidWater::CloudLiquidWater(const eckit::Configuration & conf, const oops::Variables & vars) + : PredictorBase(conf, vars) { + // Initialize options + options_.deserialize(conf.getSubConfiguration("options")); + const std::string &satellite = options_.satellite.value(); + + // Currently the code is designed only for SSMIS brightness temperatures from + // channels 12 through 18, but a different satellite could use a different list of + // channels and frequencies requiring a different block of input checks. + if (satellite == "SSMIS") { + ASSERT(options_.ch19h.value() != boost::none && options_.ch19v.value() != boost::none && + options_.ch22v.value() != boost::none && options_.ch37h.value() != boost::none && + options_.ch37v.value() != boost::none && options_.ch91v.value() != boost::none && + options_.ch91h.value() != boost::none); + + channels_ = {options_.ch19h.value().get(), options_.ch19v.value().get(), + options_.ch22v.value().get(), options_.ch37h.value().get(), + options_.ch37v.value().get(), options_.ch91v.value().get(), + options_.ch91h.value().get()}; + + ASSERT(options_.ch19h.value().get() != 0 && options_.ch19v.value().get() != 0 && + options_.ch22v.value().get() != 0 && options_.ch37h.value().get() != 0 && + options_.ch37v.value().get() != 0 && options_.ch91v.value().get() != 0 && + options_.ch91h.value().get() != 0 && channels_.size() == 7); + + } else { + std::string errString = "Currently only SSMIS satellite is supported."; + oops::Log::error() << errString; + throw eckit::BadValue(errString); + } +} + +// ----------------------------------------------------------------------------- + +void CloudLiquidWater::compute(const ioda::ObsSpace & odb, + const GeoVaLs & geovals, + const ObsDiagnostics &, + ioda::ObsVector & out) const { + // Get required parameters + const std::string &vargrp = options_.varGroup.value(); + const std::size_t nlocs = out.nlocs(); + const std::size_t nvars = out.nvars(); + + const float fmiss = util::missingValue(fmiss); + const double dmiss = util::missingValue(dmiss); + + // From the obs database, grab all the brightness temperatures of channels. + std::vector bt19h(nlocs), bt19v(nlocs), bt22v(nlocs), bt37h(nlocs), + bt37v(nlocs), bt91v(nlocs), bt91h(nlocs); + odb.get_db(vargrp, "brightness_temperature_" + std::to_string(channels_[0]), bt19h); + odb.get_db(vargrp, "brightness_temperature_" + std::to_string(channels_[1]), bt19v); + odb.get_db(vargrp, "brightness_temperature_" + std::to_string(channels_[2]), bt22v); + odb.get_db(vargrp, "brightness_temperature_" + std::to_string(channels_[3]), bt37h); + odb.get_db(vargrp, "brightness_temperature_" + std::to_string(channels_[4]), bt37v); + odb.get_db(vargrp, "brightness_temperature_" + std::to_string(channels_[5]), bt91v); + odb.get_db(vargrp, "brightness_temperature_" + std::to_string(channels_[6]), bt91h); + + std::vector szas(nlocs); + odb.get_db("MetaData", "sensor_zenith_angle", szas); + + std::vector water_frac(nlocs, 0.0); + geovals.get(water_frac, "water_area_fraction"); + + // Compute cloud liquid water amount + std::vector clw(nlocs); + CLWRetMW_SSMIS::cloudLiquidWater(bt19h, bt19v, bt22v, bt37h, bt37v, bt91v, bt91h, + water_frac, clw); + + for (std::size_t iloc = 0; iloc < nlocs; ++iloc) { + for (std::size_t jvar = 0; jvar < nvars; ++jvar) { + float cossza = cos(Constants::deg2rad * szas[iloc]); + if (clw[iloc] == fmiss) { + out[iloc*nvars + jvar] = dmiss; + } else { + out[iloc*nvars + jvar] = static_cast(clw[iloc]*cossza*cossza); + } + } + } +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/predictors/CloudLiquidWater.h b/src/ufo/predictors/CloudLiquidWater.h new file mode 100644 index 000000000..799fe5680 --- /dev/null +++ b/src/ufo/predictors/CloudLiquidWater.h @@ -0,0 +1,74 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PREDICTORS_CLOUDLIQUIDWATER_H_ +#define UFO_PREDICTORS_CLOUDLIQUIDWATER_H_ + +#include +#include + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/predictors/PredictorBase.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + +/// +/// \brief Option to override varGroup default of ObsValue for all channels with ObsBias +/// +class CloudLiquidWaterParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(CloudLiquidWaterParameters, Parameters) + + public: + /// We must specify a satellite reference name such as SSMIS to know which channels to expect. + oops::RequiredParameter satellite{"satellite", this}; + /// In case we need to override the ObsValue group name with another optional group name. + oops::Parameter varGroup{"varGroup", "ObsValue", this}; + /// List below is solely for SSMIS data, but a different list of channel numbers could be + /// added for a different satellite platform in the future. + oops::OptionalParameter ch19h{"ch19h", this}; + oops::OptionalParameter ch19v{"ch19v", this}; + oops::OptionalParameter ch22v{"ch22v", this}; + oops::OptionalParameter ch37h{"ch37h", this}; + oops::OptionalParameter ch37v{"ch37v", this}; + oops::OptionalParameter ch91h{"ch91h", this}; + oops::OptionalParameter ch91v{"ch91v", this}; +}; + +// ----------------------------------------------------------------------------- + +class CloudLiquidWater : public PredictorBase { + public: + CloudLiquidWater(const eckit::Configuration &, const oops::Variables &); + ~CloudLiquidWater() {} + + void compute(const ioda::ObsSpace &, + const GeoVaLs &, + const ObsDiagnostics &, + ioda::ObsVector &) const override; + + private: + CloudLiquidWaterParameters options_; + std::vector channels_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_PREDICTORS_CLOUDLIQUIDWATER_H_ diff --git a/src/ufo/predictors/Constant.cc b/src/ufo/predictors/Constant.cc index 052878f03..7e35b360b 100644 --- a/src/ufo/predictors/Constant.cc +++ b/src/ufo/predictors/Constant.cc @@ -17,8 +17,8 @@ static PredictorMaker makerFuncConstant_("constant"); // ----------------------------------------------------------------------------- -Constant::Constant(const eckit::Configuration & conf, const std::vector & jobs) - : PredictorBase(conf, jobs) { +Constant::Constant(const eckit::Configuration & conf, const oops::Variables & vars) + : PredictorBase(conf, vars) { } // ----------------------------------------------------------------------------- @@ -27,15 +27,11 @@ void Constant::compute(const ioda::ObsSpace & odb, const GeoVaLs &, const ObsDiagnostics &, ioda::ObsVector & out) const { - const std::size_t nlocs = odb.nlocs(); - - // assure shape of out - ASSERT(out.nlocs() == nlocs); - - const std::size_t njobs = jobs_.size(); - for (std::size_t jl = 0; jl < nlocs; ++jl) { - for (std::size_t jb = 0; jb < njobs; ++jb) { - out[jl*njobs+jb] = 1.0; + const std::size_t nlocs = out.nlocs(); + const std::size_t nvars = out.nvars(); + for (std::size_t jloc = 0; jloc < nlocs; ++jloc) { + for (std::size_t jvar = 0; jvar < nvars; ++jvar) { + out[jloc*nvars+jvar] = 1.0; } } } diff --git a/src/ufo/predictors/Constant.h b/src/ufo/predictors/Constant.h index 7a48a54e6..036a586d6 100644 --- a/src/ufo/predictors/Constant.h +++ b/src/ufo/predictors/Constant.h @@ -8,14 +8,16 @@ #ifndef UFO_PREDICTORS_CONSTANT_H_ #define UFO_PREDICTORS_CONSTANT_H_ -#include - #include "ufo/predictors/PredictorBase.h" namespace eckit { class Configuration; } +namespace oops { + class Variables; +} + namespace ioda { class ObsSpace; } @@ -26,8 +28,7 @@ namespace ufo { class Constant : public PredictorBase { public: - Constant(const eckit::Configuration &, const std::vector &); - ~Constant() {} + Constant(const eckit::Configuration &, const oops::Variables &); void compute(const ioda::ObsSpace &, const GeoVaLs &, diff --git a/src/ufo/predictors/CosineOfLatitudeTimesOrbitNode.cc b/src/ufo/predictors/CosineOfLatitudeTimesOrbitNode.cc index da13370b2..a41172956 100644 --- a/src/ufo/predictors/CosineOfLatitudeTimesOrbitNode.cc +++ b/src/ufo/predictors/CosineOfLatitudeTimesOrbitNode.cc @@ -7,6 +7,8 @@ #include "ufo/predictors/CosineOfLatitudeTimesOrbitNode.h" +#include + #include "ioda/ObsSpace.h" #include "ufo/utils/Constants.h" @@ -19,11 +21,11 @@ static PredictorMaker // ----------------------------------------------------------------------------- CosineOfLatitudeTimesOrbitNode::CosineOfLatitudeTimesOrbitNode( - const eckit::Configuration & conf, const std::vector & jobs) - : PredictorBase(conf, jobs) { + const eckit::Configuration & conf, const oops::Variables & vars) + : PredictorBase(conf, vars) { // override the preconditioner from options - if (conf.has("predictor.options")) - precond_ = conf.getDouble("predictor.options.preconditioner"); + if (conf.has("options")) + precond_ = conf.getDouble("options.preconditioner"); } // ----------------------------------------------------------------------------- @@ -31,10 +33,8 @@ void CosineOfLatitudeTimesOrbitNode::compute(const ioda::ObsSpace & odb, const GeoVaLs &, const ObsDiagnostics &, ioda::ObsVector & out) const { - const std::size_t nlocs = odb.nlocs(); - - // assure shape of out - ASSERT(out.nlocs() == nlocs); + const std::size_t nlocs = out.nlocs(); + const std::size_t nvars = out.nvars(); // retrieve the sensor view angle std::vector cenlat(nlocs, 0.0); @@ -42,10 +42,9 @@ void CosineOfLatitudeTimesOrbitNode::compute(const ioda::ObsSpace & odb, odb.get_db("MetaData", "latitude", cenlat); odb.get_db("MetaData", "sensor_azimuth_angle", node); - const std::size_t njobs = jobs_.size(); - for (std::size_t jl = 0; jl < nlocs; ++jl) { - for (std::size_t jb = 0; jb < njobs; ++jb) { - out[jl*njobs+jb] = node[jl] * cos(cenlat[jl] * Constants::deg2rad); + for (std::size_t jloc = 0; jloc < nlocs; ++jloc) { + for (std::size_t jvar = 0; jvar < nvars; ++jvar) { + out[jloc*nvars+jvar] = node[jloc] * cos(cenlat[jloc] * Constants::deg2rad); } } } diff --git a/src/ufo/predictors/CosineOfLatitudeTimesOrbitNode.h b/src/ufo/predictors/CosineOfLatitudeTimesOrbitNode.h index ab51605db..2e80c85de 100644 --- a/src/ufo/predictors/CosineOfLatitudeTimesOrbitNode.h +++ b/src/ufo/predictors/CosineOfLatitudeTimesOrbitNode.h @@ -8,16 +8,16 @@ #ifndef UFO_PREDICTORS_COSINEOFLATITUDETIMESORBITNODE_H_ #define UFO_PREDICTORS_COSINEOFLATITUDETIMESORBITNODE_H_ -#include - -#include - #include "ufo/predictors/PredictorBase.h" namespace eckit { class Configuration; } +namespace oops { + class Variables; +} + namespace ioda { class ObsSpace; } @@ -28,8 +28,7 @@ namespace ufo { class CosineOfLatitudeTimesOrbitNode : public PredictorBase { public: - CosineOfLatitudeTimesOrbitNode(const eckit::Configuration &, const std::vector &); - ~CosineOfLatitudeTimesOrbitNode() {} + CosineOfLatitudeTimesOrbitNode(const eckit::Configuration &, const oops::Variables &); void compute(const ioda::ObsSpace &, const GeoVaLs &, diff --git a/src/ufo/predictors/Emissivity.cc b/src/ufo/predictors/Emissivity.cc index 5eaef7b8e..bd876b1f2 100644 --- a/src/ufo/predictors/Emissivity.cc +++ b/src/ufo/predictors/Emissivity.cc @@ -6,6 +6,7 @@ */ #include +#include #include "ufo/predictors/Emissivity.h" @@ -24,12 +25,13 @@ static PredictorMaker makerFuncEmissivity_("emissivity"); // ----------------------------------------------------------------------------- -Emissivity::Emissivity(const eckit::Configuration & conf, const std::vector & jobs) - : PredictorBase(conf, jobs) { +Emissivity::Emissivity(const eckit::Configuration & conf, const oops::Variables & vars) + : PredictorBase(conf, vars) { // required variables geovars_ += oops::Variables({"water_area_fraction"}); - if (jobs.size() > 0) { - hdiags_ += oops::Variables({"brightness_temperature_jacobian_surface_emissivity"}, jobs); + if (vars.size() > 0) { + hdiags_ += oops::Variables({"brightness_temperature_jacobian_surface_emissivity"}, + vars.channels()); } else { oops::Log::error() << "Channels size is ZERO !" << std::endl; ABORT("Channels size is ZERO !"); @@ -42,23 +44,21 @@ void Emissivity::compute(const ioda::ObsSpace & odb, const GeoVaLs & geovals, const ObsDiagnostics & ydiags, ioda::ObsVector & out) const { - const std::size_t njobs = jobs_.size(); - const std::size_t nlocs = odb.nlocs(); + const std::size_t nvars = out.nvars(); + const std::size_t nlocs = out.nlocs(); - // assure shape of out - ASSERT(out.nlocs() == nlocs); - - std::vector pred(nlocs, 0.0); + std::vector pred(nlocs, 0.0); std::vector h2o_frac(nlocs, 0.0); geovals.get(h2o_frac, "water_area_fraction"); std::string hdiags; out.zero(); - for (std::size_t jb = 0; jb < njobs; ++jb) { - hdiags = "brightness_temperature_jacobian_surface_emissivity_" + std::to_string(jobs_[jb]); + for (std::size_t jvar = 0; jvar < nvars; ++jvar) { + hdiags = "brightness_temperature_jacobian_surface_emissivity_" + + std::to_string(vars_.channels()[jvar]); ydiags.get(pred, hdiags); - for (std::size_t jl = 0; jl < nlocs; ++jl) { - if (h2o_frac[jl] < 0.99 && std::fabs(pred[jl]) > 0.001) { - out[jl*njobs+jb] = pred[jl]; + for (std::size_t jloc = 0; jloc < nlocs; ++jloc) { + if (h2o_frac[jloc] < 0.99 && std::fabs(pred[jloc]) > 0.001) { + out[jloc*nvars+jvar] = pred[jloc]; } } } diff --git a/src/ufo/predictors/Emissivity.h b/src/ufo/predictors/Emissivity.h index 3cee7311d..11e8ff21d 100644 --- a/src/ufo/predictors/Emissivity.h +++ b/src/ufo/predictors/Emissivity.h @@ -8,14 +8,16 @@ #ifndef UFO_PREDICTORS_EMISSIVITY_H_ #define UFO_PREDICTORS_EMISSIVITY_H_ -#include - #include "ufo/predictors/PredictorBase.h" namespace eckit { class Configuration; } +namespace oops { + class Variables; +} + namespace ioda { class ObsSpace; } @@ -26,8 +28,7 @@ namespace ufo { class Emissivity : public PredictorBase { public: - Emissivity(const eckit::Configuration &, const std::vector &); - ~Emissivity() {} + Emissivity(const eckit::Configuration &, const oops::Variables &); void compute(const ioda::ObsSpace &, const GeoVaLs &, diff --git a/src/ufo/predictors/InterpolateDataFromFile.cc b/src/ufo/predictors/InterpolateDataFromFile.cc new file mode 100644 index 000000000..a4f748e45 --- /dev/null +++ b/src/ufo/predictors/InterpolateDataFromFile.cc @@ -0,0 +1,106 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/predictors/InterpolateDataFromFile.h" + +#include +#include +#include + +#include + +#include "eckit/exception/Exceptions.h" +#include "ioda/ObsSpace.h" +#include "oops/util/AssociativeContainers.h" +#include "ufo/filters/ObsFilterData.h" + +namespace ufo { + +namespace { + +/// \brief Store all components of `input` in `output`. +void save(const ioda::ObsDataVector &input, ioda::ObsVector &output) { + const float fmiss = util::missingValue(fmiss); + const double dmiss = util::missingValue(dmiss); + const size_t numLocs = input.nlocs(); + const size_t numInputVars = input.nvars(); + const size_t numOutputVars = output.nvars(); + for (size_t inputVarIndex = 0; inputVarIndex < numInputVars; ++inputVarIndex) { + const ioda::ObsDataRow &inputVarValues = input[inputVarIndex]; + const size_t outputVarIndex = output.varnames().find(input.varnames()[inputVarIndex]); + for (size_t locIndex = 0; locIndex < numLocs; ++locIndex) { + const float value = inputVarValues[locIndex]; + output[locIndex * numOutputVars + outputVarIndex] = + (value == fmiss) ? dmiss : static_cast(value); + } + } +} + +std::set getVariableNamesWithoutChannels(const oops::Variables &vars) { + if (vars.channels().empty()) { + return std::set(vars.variables().begin(), vars.variables().end()); + } else { + // We assume there's only a single multi-channel variable + ASSERT(vars.channels().size() == vars.variables().size()); + std::string channellessVariable = vars.variables().front(); + channellessVariable.resize(channellessVariable.find_last_of('_')); + return std::set{channellessVariable}; + } +} + +} // namespace + +static PredictorMaker maker("interpolate_data_from_file"); + +InterpolateDataFromFile::InterpolateDataFromFile(const eckit::Configuration & conf, + const oops::Variables & vars) + : PredictorBase(conf, vars) { + InterpolateDataFromFileParameters params; + eckit::LocalConfiguration optionsConf(conf, "options"); + params.validateAndDeserialize(optionsConf); + + const std::set channellessVariables = getVariableNamesWithoutChannels(vars_); + + for (const VariableCorrectionParameters & varParams : params.correctedVariables.value()) { + if (!oops::contains(channellessVariables, varParams.name)) + throw eckit::UserError("'" + varParams.name.value() + + "' is not in the list of bias-corrected variables", Here()); + eckit::LocalConfiguration varConfig = varParams.details.toConfiguration(); + varConfig.set("group", "ObsBias"); + obsFunctions_[varParams.name] = boost::make_unique(varConfig); + } + + for (const auto &varAndObsFunction : obsFunctions_) { + const DrawValueFromFile &obsFunction = *varAndObsFunction.second; + const ufo::Variables &requiredVariables = obsFunction.requiredVariables(); + geovars_ += requiredVariables.allFromGroup("GeoVaLs").toOopsVariables(); + hdiags_ += requiredVariables.allFromGroup("ObsDiag").toOopsVariables(); + } +} + +void InterpolateDataFromFile::compute(const ioda::ObsSpace & /*odb*/, + const GeoVaLs & geovals, + const ObsDiagnostics & obsdiags, + ioda::ObsVector & out) const { + ObsFilterData obsFilterData(out.space()); + obsFilterData.associate(geovals); + obsFilterData.associate(obsdiags); + + out.zero(); + + for (const auto &varAndObsFunction : obsFunctions_) { + const std::string &varName = varAndObsFunction.first; + const DrawValueFromFile &obsFunction = *varAndObsFunction.second; + + oops::Variables currentVars({varName}, vars_.channels()); + ioda::ObsDataVector obsFunctionResult(out.space(), currentVars); + obsFunction.compute(obsFilterData, obsFunctionResult); + save(obsFunctionResult, out); + } +} + +} // namespace ufo diff --git a/src/ufo/predictors/InterpolateDataFromFile.h b/src/ufo/predictors/InterpolateDataFromFile.h new file mode 100644 index 000000000..5de258278 --- /dev/null +++ b/src/ufo/predictors/InterpolateDataFromFile.h @@ -0,0 +1,92 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PREDICTORS_INTERPOLATEDATAFROMFILE_H_ +#define UFO_PREDICTORS_INTERPOLATEDATAFROMFILE_H_ + +#include +#include +#include +#include + +#include "oops/util/parameters/Parameters.h" +#include "ufo/filters/obsfunctions/DrawValueFromFile.h" +#include "ufo/predictors/PredictorBase.h" + +namespace ufo { + +class VariableCorrectionParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(VariableCorrectionParameters, Parameters) + + public: + /// Name of the variable to be bias-corrected. + oops::RequiredParameter name{"name", this}; + + /// Options controlling bias correction of this variable. + DrawValueFromFileParametersWithoutGroup details{this}; +}; + +/// \brief Parameters recognized in the `options` section of the Configuration passed to +/// the InterpolateDataFromFile constructor. +class InterpolateDataFromFileParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(InterpolateDataFromFileParameters, Parameters) + + public: + oops::Parameter> correctedVariables{ + "corrected variables", {}, this}; +}; + +/// \brief A predictor returning values produced by the DrawValueFromFile ObsFunction. +/// +/// Let's start with a simple example. Suppose this predictor is configured with the following +/// YAML snippet: +/// +/// \code{.yaml} +/// name: interpolate_data_from_file +/// options: +/// corrected variables: +/// - name: air_temperature +/// file: Data/ufo/testinput_tier_1/air_temperature_bias.nc4 +/// interpolation: +/// - name: station_id@MetaData +/// method: exact +/// \endcode +/// +/// and the `air_temperature_bias.nc4` file contains a 1D array `air_temperature@ObsBias` indexed +/// by a `station_id@MetaData` coordinate. The DrawValueFromFile ObsFunction will then load this +/// file and for each location produce the element of the `air_temperature@ObsBias` array +/// corresponding to the element of the `station_id@MetaData` array matching the value of the +/// `station_id@MetaData` ObsSpace variable at that location. This will also be the value produced +/// by this predictor. +/// +/// The predictor will produce zeros for all bias-corrected variables missing from the `corrected +/// variables` list. +/// +/// It is possible to make the bias correction dependent on more than one ObsSpace variable and to +/// use a different matching method than `exact`. For more details, see the documentation of +/// DrawValueFromFile and DataExtractor. +class InterpolateDataFromFile : public PredictorBase { + public: + InterpolateDataFromFile(const eckit::Configuration &, const oops::Variables &); + + void compute(const ioda::ObsSpace &, const GeoVaLs &, + const ObsDiagnostics &, ioda::ObsVector &) const override; + + private: + /// `obsFunctions_[varName]` is the ObsFunction that will calculate the predictions for variable + /// `varName`. + // The map is storing unique_ptrs to make it possible to compile this code with GCC 4.8.5, + // whose STL implementation is affected by LWG 2397 + // (http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2397), later resolved by + // amending the C++11 standard as described in N4387 + // (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4387.html). + std::map> obsFunctions_; +}; + +} // namespace ufo + +#endif // UFO_PREDICTORS_INTERPOLATEDATAFROMFILE_H_ diff --git a/src/ufo/predictors/LapseRate.cc b/src/ufo/predictors/LapseRate.cc index 05d9e2ca6..464a30ebf 100644 --- a/src/ufo/predictors/LapseRate.cc +++ b/src/ufo/predictors/LapseRate.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include "ufo/predictors/LapseRate.h" @@ -25,12 +26,12 @@ static PredictorMaker makerFuncLapseRate_("lapse_rate"); // ----------------------------------------------------------------------------- -LapseRate::LapseRate(const eckit::Configuration & conf, const std::vector & jobs) - : PredictorBase(conf, jobs), order_(1) +LapseRate::LapseRate(const eckit::Configuration & conf, const oops::Variables & vars) + : PredictorBase(conf, vars), order_(1) { // get the order if it is provided in options - if (conf.has("predictor.options.order")) { - conf.get("predictor.options.order", order_); + if (conf.has("options.order")) { + conf.get("options.order", order_); // override the predictor name for differentiable name() = name() + "_order_" + std::to_string(order_); @@ -40,8 +41,8 @@ LapseRate::LapseRate(const eckit::Configuration & conf, const std::vector & geovars_ += oops::Variables({"air_temperature", "air_pressure", "average_surface_temperature_within_field_of_view"}); - if (jobs.size() > 0) { - hdiags_ += oops::Variables({"transmittances_of_atmosphere_layer"}, jobs); + if (vars.size() > 0) { + hdiags_ += oops::Variables({"transmittances_of_atmosphere_layer"}, vars.channels()); } else { oops::Log::error() << "Channels size is ZERO !" << std::endl; ABORT("Channels size is ZERO !"); @@ -49,8 +50,8 @@ LapseRate::LapseRate(const eckit::Configuration & conf, const std::vector & // This is a very preliminary method, please revisit // more flexibilites are needed - if (conf.has("predictor.options.tlapse")) { - const std::string tlapse_file = conf.getString("predictor.options.tlapse"); + if (conf.has("options.tlapse")) { + const std::string tlapse_file = conf.getString("options.tlapse"); std::ifstream infile(tlapse_file); std::string nusis; // sensor/instrument/satellite int nuchan; // channel number @@ -81,11 +82,8 @@ void LapseRate::compute(const ioda::ObsSpace & odb, const GeoVaLs & geovals, const ObsDiagnostics & ydiags, ioda::ObsVector & out) const { - const std::size_t njobs = jobs_.size(); - const std::size_t nlocs = odb.nlocs(); - - // assure shape of out - ASSERT(out.nlocs() == nlocs); + const std::size_t nvars = out.nvars(); + const std::size_t nlocs = out.nlocs(); // common vectors storage std::vector pred(nlocs, 0.0); @@ -99,8 +97,8 @@ void LapseRate::compute(const ioda::ObsSpace & odb, std::vector> tmpvar; std::string hdiags; - for (std::size_t jb = 0; jb < njobs; ++jb) { - hdiags = "transmittances_of_atmosphere_layer_" + std::to_string(jobs_[jb]); + for (std::size_t jvar = 0; jvar < nvars; ++jvar) { + hdiags = "transmittances_of_atmosphere_layer_" + std::to_string(vars_.channels()[jvar]); tmpvar.clear(); for (std::size_t js = 0; js < ydiags.nlevs(hdiags); ++js) { ydiags.get(pred, hdiags, js+1); @@ -119,26 +117,28 @@ void LapseRate::compute(const ioda::ObsSpace & odb, nlevs = geovals.nlevs("air_pressure"); float tlapchn; - // sort out the tlapmean based on jobs + // sort out the tlapmean based on vars std::vector tlap; - for (std::size_t jb = 0; jb < njobs; ++jb) { - auto it = tlapmean_.find(jobs_[jb]); + for (std::size_t jvar = 0; jvar < nvars; ++jvar) { + auto it = tlapmean_.find(vars_.channels()[jvar]); if (it != tlapmean_.end()) { tlap.push_back(it->second); } else { - oops::Log::error() << "Could not locate tlapemean for channel: " << jobs_[jb] << std::endl; + oops::Log::error() << "Could not locate tlapemean for channel: " << + vars_.channels()[jvar] << std::endl; ABORT("Could not locate tlapemean value"); } } - for (std::size_t jl = 0; jl < nlocs; ++jl) { - for (std::size_t jb = 0; jb < njobs; ++jb) { - tlapchn = (ptau5[jb][nlevs-2][jl]-ptau5[jb][nlevs-1][jl])*(tsavg5[jl]-tvp[nlevs-2][jl]); + for (std::size_t jloc = 0; jloc < nlocs; ++jloc) { + for (std::size_t jvar = 0; jvar < nvars; ++jvar) { + tlapchn = (ptau5[jvar][nlevs-2][jloc]-ptau5[jvar][nlevs-1][jloc])* + (tsavg5[jloc]-tvp[nlevs-2][jloc]); for (std::size_t k = 1; k < nlevs-1; ++k) { - tlapchn = tlapchn+(ptau5[jb][nlevs-k-2][jl]-ptau5[jb][nlevs-k-1][jl])* - (tvp[nlevs-k][jl]-tvp[nlevs-k-2][jl]); + tlapchn = tlapchn+(ptau5[jvar][nlevs-k-2][jloc]-ptau5[jvar][nlevs-k-1][jloc])* + (tvp[nlevs-k][jloc]-tvp[nlevs-k-2][jloc]); } - out[jl*njobs+jb] = pow((tlapchn - tlap[jb]), order_); + out[jloc*nvars+jvar] = pow((tlapchn - tlap[jvar]), order_); } } } diff --git a/src/ufo/predictors/LapseRate.h b/src/ufo/predictors/LapseRate.h index b7a0d9f41..d6a8a3110 100644 --- a/src/ufo/predictors/LapseRate.h +++ b/src/ufo/predictors/LapseRate.h @@ -9,9 +9,6 @@ #define UFO_PREDICTORS_LAPSERATE_H_ #include -#include - -#include "eckit/config/LocalConfiguration.h" #include "ufo/predictors/PredictorBase.h" @@ -19,6 +16,10 @@ namespace eckit { class Configuration; } +namespace oops { + class Variables; +} + namespace ioda { class ObsSpace; } @@ -29,8 +30,7 @@ namespace ufo { class LapseRate : public PredictorBase { public: - LapseRate(const eckit::Configuration &, const std::vector &); - ~LapseRate() {} + LapseRate(const eckit::Configuration &, const oops::Variables &); void compute(const ioda::ObsSpace &, const GeoVaLs &, diff --git a/src/ufo/predictors/Legendre.cc b/src/ufo/predictors/Legendre.cc new file mode 100644 index 000000000..1a47be4da --- /dev/null +++ b/src/ufo/predictors/Legendre.cc @@ -0,0 +1,70 @@ +/* + * (C) Copyright 2020 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include "ioda/ObsSpace.h" +#include "oops/util/Logger.h" +#include "ufo/predictors/Legendre.h" +#include "ufo/utils/Constants.h" + +namespace ufo { + +static PredictorMaker makerFuncLegendre_("Legendre"); + +// ----------------------------------------------------------------------------- + +Legendre::Legendre(const eckit::Configuration & conf, const oops::Variables & vars) + : PredictorBase(conf, vars), order_(1), nscan_(-99) { + // get the order if it is provided in options + if (conf.has("options.order")) { + conf.get("options.order", order_); + + // override the predictor name to distinguish between Legendre predictors of different orders + name() = name() + "_" + std::to_string(order_); + } + conf.get("number of scan positions", nscan_); +} + +// ----------------------------------------------------------------------------- + +void Legendre::compute(const ioda::ObsSpace & odb, + const GeoVaLs &, + const ObsDiagnostics &, + ioda::ObsVector & out) const { + const std::size_t nlocs = odb.nlocs(); + + // assure shape of out + ASSERT(out.nlocs() == nlocs); + + // retrieve the sensor scan position and total number of scan positions (const for inst type) + std::vector scan_position(nlocs, 0); + std::vector LegPoly(order_+1, 0); + + odb.get_db("MetaData", "scan_position", scan_position); + const std::size_t nvars = vars_.size(); + for (std::size_t jl = 0; jl < nlocs; ++jl) { + double xscan{-1.0 + 2.0 * (scan_position[jl] - 1) / (nscan_ - 1)}; + // Transformed variable for the scan position in the range -1 to 1. + // Calculate Legendre Polynomial for current scan position + LegPoly[0] = 1.0; + LegPoly[1] = xscan; + for (std::size_t iorder=1; iorder < order_; ++iorder) { + LegPoly[iorder+1] = ((2*iorder+1)*xscan*LegPoly[iorder] + -iorder*LegPoly[iorder-1])/(iorder+1); + } + for (std::size_t iorder=0; iorder < order_+1; ++iorder) { + LegPoly[iorder] = sqrt(2*iorder+1)*LegPoly[iorder]; + } + for (std::size_t jb = 0; jb < nvars; ++jb) { + out[jl*nvars+jb] = LegPoly[order_]; + } + } +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/predictors/Legendre.h b/src/ufo/predictors/Legendre.h new file mode 100644 index 000000000..a2907def5 --- /dev/null +++ b/src/ufo/predictors/Legendre.h @@ -0,0 +1,55 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PREDICTORS_LEGENDRE_H_ +#define UFO_PREDICTORS_LEGENDRE_H_ + +#include +#include "ufo/predictors/PredictorBase.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + +// ----------------------------------------------------------------------------- +/** + *This Legendre predictor is for fitting residual errors between the ends of the + *scan extremes; thus, the scan position is rescaled between -1 and 1 (xscan variable). + *Legendre polynomials are calculated with recurrence relations. Polynomials can be + *produced for an order (as addressed by input parameter of that name). The data must + *contain scan positions in the variable "scan_position@MetaData" and the number of scan + *positons in"sensor_numb_scan_position@MetaData". Both of these are vectors with length + *nlocs in the current implementation. + */ + + +class Legendre : public PredictorBase { + public: + Legendre(const eckit::Configuration &, const oops::Variables &); + ~Legendre() {} + + void compute(const ioda::ObsSpace &, + const GeoVaLs &, + const ObsDiagnostics &, + ioda::ObsVector &) const override; + + private: + int order_; + int nscan_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_PREDICTORS_LEGENDRE_H_ diff --git a/src/ufo/predictors/OrbitalAngle.cc b/src/ufo/predictors/OrbitalAngle.cc new file mode 100644 index 000000000..a5dd2acd7 --- /dev/null +++ b/src/ufo/predictors/OrbitalAngle.cc @@ -0,0 +1,69 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include "ioda/ObsSpace.h" +#include "oops/util/Logger.h" +#include "ufo/predictors/OrbitalAngle.h" +#include "ufo/utils/Constants.h" + +namespace ufo { + +static PredictorMaker makerFuncOrbitalAngle_("orbital_angle"); + +// ----------------------------------------------------------------------------- + +OrbitalAngle::OrbitalAngle(const eckit::Configuration & conf, const oops::Variables & vars) + : PredictorBase(conf, vars), order_(1) { + // get the order if it is provided in options + conf.get("options.order", order_); + conf.get("options.component", component_); + + // override the predictor name to distinguish between Orbital angle predictors of + // different orders as well as the two components, sine and cosine. + name() = name() + "_" + std::to_string(order_)+ "_" + component_; +} + +// ----------------------------------------------------------------------------- + +void OrbitalAngle::compute(const ioda::ObsSpace & odb, + const GeoVaLs &, + const ObsDiagnostics &, + ioda::ObsVector & out) const { + const size_t nlocs = out.nlocs(); + const size_t nvars = out.nvars(); + + // retrieve the sensor orbital angle + std::vector orbital_angle(nlocs, 0.0); + odb.get_db("MetaData", "satellite_orbital_angle", orbital_angle); + + ASSERT(component_ == "cos" || component_ == "sin"); + switch (component_ == "cos") + { + case true: + for (std::size_t jl = 0; jl < nlocs; ++jl) { + double cos_oa{ std::cos(orbital_angle[jl]*order_*Constants::deg2rad)}; + for (std::size_t jb = 0; jb < nvars; ++jb) { + out[jl*nvars+jb] = cos_oa; + } + } + break; + case false: + for (std::size_t jl = 0; jl < nlocs; ++jl) { + double sin_oa{ std::sin(orbital_angle[jl]*order_*Constants::deg2rad)}; + for (std::size_t jb = 0; jb < nvars; ++jb) { + out[jl*nvars+jb] = sin_oa; + } + } + break; + } +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/predictors/OrbitalAngle.h b/src/ufo/predictors/OrbitalAngle.h new file mode 100644 index 000000000..1ddb8724d --- /dev/null +++ b/src/ufo/predictors/OrbitalAngle.h @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PREDICTORS_ORBITALANGLE_H_ +#define UFO_PREDICTORS_ORBITALANGLE_H_ +#include +#include +#include "ufo/predictors/PredictorBase.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + +// ----------------------------------------------------------------------------- +/** + *This orbital angle predictor is used to fit residual errors as a function of satellite + *orbital angle using a Fourier series. The data must contain this orbital angle time + *series in the variable "satellite_orbital_angle@MetaData". Two member variables are + *used to store the order of the term in the series being calculated (order_) and the + *Fourier component (cos or sin), respectively. These are read from the yaml configuration + *file. + */ + +class OrbitalAngle : public PredictorBase { + public: + OrbitalAngle(const eckit::Configuration &, const oops::Variables &); + + void compute(const ioda::ObsSpace &, + const GeoVaLs &, + const ObsDiagnostics &, + ioda::ObsVector &) const override; + + private: + int order_; + std::string component_; // has two valid values: "cos" and "sin" +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_PREDICTORS_ORBITALANGLE_H_ diff --git a/src/ufo/predictors/PredictorBase.cc b/src/ufo/predictors/PredictorBase.cc index b5e9a257d..05dffb927 100644 --- a/src/ufo/predictors/PredictorBase.cc +++ b/src/ufo/predictors/PredictorBase.cc @@ -18,9 +18,9 @@ namespace ufo { // ----------------------------------------------------------------------------- -PredictorBase::PredictorBase(const eckit::Configuration & conf, const std::vector & jobs) - : func_name_(conf.getString("predictor.name")), - geovars_(), hdiags_(), jobs_(jobs) { +PredictorBase::PredictorBase(const eckit::Configuration & conf, const oops::Variables & vars) + : func_name_(conf.getString("name")), + geovars_(), hdiags_(), vars_(vars) { } // ----------------------------------------------------------------------------- @@ -37,9 +37,9 @@ PredictorFactory::PredictorFactory(const std::string & name) { // ----------------------------------------------------------------------------- PredictorBase * PredictorFactory::create(const eckit::Configuration & conf, - const std::vector & jobs) { + const oops::Variables & vars) { oops::Log::trace() << "PredictorBase::create starting" << std::endl; - const std::string name = conf.getString("predictor.name"); + const std::string name = conf.getString("name"); if (!predictorExists(name)) { oops::Log::error() << name << " does not exist in ufo::PredictorFactory." << std::endl; @@ -47,7 +47,7 @@ PredictorBase * PredictorFactory::create(const eckit::Configuration & conf, } typename std::map::iterator jloc = getMakers().find(name); - PredictorBase * ptr = jloc->second->make(conf, jobs); + PredictorBase * ptr = jloc->second->make(conf, vars); oops::Log::trace() << "PredictorBase::create done" << std::endl; return ptr; } diff --git a/src/ufo/predictors/PredictorBase.h b/src/ufo/predictors/PredictorBase.h index 7f3527c15..a16d607f0 100644 --- a/src/ufo/predictors/PredictorBase.h +++ b/src/ufo/predictors/PredictorBase.h @@ -9,6 +9,7 @@ #define UFO_PREDICTORS_PREDICTORBASE_H_ #include +#include #include #include @@ -37,8 +38,8 @@ namespace ufo { class PredictorBase : private boost::noncopyable { public: - explicit PredictorBase(const eckit::Configuration &, const std::vector &); - virtual ~PredictorBase() {} + explicit PredictorBase(const eckit::Configuration &, const oops::Variables &); + virtual ~PredictorBase() = default; /// compute the predictor virtual void compute(const ioda::ObsSpace &, @@ -57,7 +58,7 @@ class PredictorBase : private boost::noncopyable { const std::string & name() const {return func_name_;} protected: - const std::vector jobs_; ///< jobs(channels) + oops::Variables vars_; ///< variables that will be bias-corrected using this predictor oops::Variables geovars_; ///< required GeoVaLs oops::Variables hdiags_; ///< required ObsDiagnostics @@ -65,18 +66,20 @@ class PredictorBase : private boost::noncopyable { std::string func_name_; ///< predictor name }; +typedef std::vector> Predictors; + // ----------------------------------------------------------------------------- /// Predictor Factory class PredictorFactory { public: - static PredictorBase * create(const eckit::Configuration &, const std::vector &); + static PredictorBase * create(const eckit::Configuration &, const oops::Variables &); virtual ~PredictorFactory() = default; static bool predictorExists(const std::string &); protected: explicit PredictorFactory(const std::string &); private: - virtual PredictorBase * make(const eckit::Configuration &, const std::vector &) = 0; + virtual PredictorBase * make(const eckit::Configuration &, const oops::Variables &) = 0; static std::map < std::string, PredictorFactory * > & getMakers() { static std::map < std::string, PredictorFactory * > makers_; return makers_; @@ -87,8 +90,8 @@ class PredictorFactory { template class PredictorMaker : public PredictorFactory { - virtual PredictorBase * make(const eckit::Configuration & conf, const std::vector & jobs) - { return new T(conf, jobs); } + virtual PredictorBase * make(const eckit::Configuration & conf, const oops::Variables & vars) + { return new T(conf, vars); } public: explicit PredictorMaker(const std::string & name) : PredictorFactory(name) {} diff --git a/src/ufo/predictors/ScanAngle.cc b/src/ufo/predictors/ScanAngle.cc index 49f2c2c70..e794d667d 100644 --- a/src/ufo/predictors/ScanAngle.cc +++ b/src/ufo/predictors/ScanAngle.cc @@ -6,6 +6,7 @@ */ #include +#include #include "ufo/predictors/ScanAngle.h" @@ -22,15 +23,19 @@ static PredictorMaker makerFuncScanAngle_("scan_angle"); // ----------------------------------------------------------------------------- -ScanAngle::ScanAngle(const eckit::Configuration & conf, const std::vector & jobs) - : PredictorBase(conf, jobs), order_(1) { +ScanAngle::ScanAngle(const eckit::Configuration & conf, const oops::Variables & vars) + : PredictorBase(conf, vars), order_(1) { // get the order if it is provided in options - if (conf.has("predictor.options.order")) { - conf.get("predictor.options.order", order_); + if (conf.has("options.order")) { + conf.get("options.order", order_); // override the predictor name for differentiable name() = name() + "_order_" + std::to_string(order_); } + + if (conf.has("options.var_name")) { + conf.get("options.var_name", var_name_); + } } // ----------------------------------------------------------------------------- @@ -39,19 +44,20 @@ void ScanAngle::compute(const ioda::ObsSpace & odb, const GeoVaLs &, const ObsDiagnostics &, ioda::ObsVector & out) const { - const std::size_t nlocs = odb.nlocs(); - - // assure shape of out - ASSERT(out.nlocs() == nlocs); + const size_t nlocs = out.nlocs(); + const size_t nvars = out.nvars(); // retrieve the sensor view angle std::vector view_angle(nlocs, 0.0); - odb.get_db("MetaData", "sensor_view_angle", view_angle); + if ( var_name_.empty() ) { + odb.get_db("MetaData", "sensor_view_angle", view_angle); + } else { + odb.get_db("MetaData", var_name_, view_angle); + } - const std::size_t njobs = jobs_.size(); - for (std::size_t jl = 0; jl < nlocs; ++jl) { - for (std::size_t jb = 0; jb < njobs; ++jb) { - out[jl*njobs+jb] = pow(view_angle[jl] * Constants::deg2rad, order_); + for (std::size_t jloc = 0; jloc < nlocs; ++jloc) { + for (std::size_t jvar = 0; jvar < nvars; ++jvar) { + out[jloc*nvars+jvar] = pow(view_angle[jloc] * Constants::deg2rad, order_); } } } diff --git a/src/ufo/predictors/ScanAngle.h b/src/ufo/predictors/ScanAngle.h index 939966986..a35fa9e4e 100644 --- a/src/ufo/predictors/ScanAngle.h +++ b/src/ufo/predictors/ScanAngle.h @@ -7,15 +7,17 @@ #ifndef UFO_PREDICTORS_SCANANGLE_H_ #define UFO_PREDICTORS_SCANANGLE_H_ - -#include - +#include #include "ufo/predictors/PredictorBase.h" namespace eckit { class Configuration; } +namespace oops { + class Variables; +} + namespace ioda { class ObsSpace; } @@ -26,8 +28,7 @@ namespace ufo { class ScanAngle : public PredictorBase { public: - ScanAngle(const eckit::Configuration &, const std::vector &); - ~ScanAngle() {} + ScanAngle(const eckit::Configuration &, const oops::Variables &); void compute(const ioda::ObsSpace &, const GeoVaLs &, @@ -36,6 +37,7 @@ class ScanAngle : public PredictorBase { private: int order_; + std::string var_name_; }; // ----------------------------------------------------------------------------- diff --git a/src/ufo/predictors/SineOfLatitude.cc b/src/ufo/predictors/SineOfLatitude.cc index f66dc1d17..b2b6e7a55 100644 --- a/src/ufo/predictors/SineOfLatitude.cc +++ b/src/ufo/predictors/SineOfLatitude.cc @@ -5,10 +5,10 @@ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#include - #include "ufo/predictors/SineOfLatitude.h" +#include + #include "ioda/ObsSpace.h" #include "ufo/utils/Constants.h" @@ -20,8 +20,8 @@ static PredictorMaker // ----------------------------------------------------------------------------- -SineOfLatitude::SineOfLatitude(const eckit::Configuration & conf, const std::vector & jobs) - : PredictorBase(conf, jobs) { +SineOfLatitude::SineOfLatitude(const eckit::Configuration & conf, const oops::Variables & vars) + : PredictorBase(conf, vars) { } // ----------------------------------------------------------------------------- @@ -30,19 +30,16 @@ void SineOfLatitude::compute(const ioda::ObsSpace & odb, const GeoVaLs &, const ObsDiagnostics &, ioda::ObsVector & out) const { - const std::size_t nlocs = odb.nlocs(); - - // assure shape of out - ASSERT(out.nlocs() == nlocs); + const std::size_t nlocs = out.nlocs(); + const std::size_t nvars = out.nvars(); // retrieve the sensor view angle std::vector cenlat(nlocs, 0.0); odb.get_db("MetaData", "latitude", cenlat); - const std::size_t njobs = jobs_.size(); - for (std::size_t jl = 0; jl < nlocs; ++jl) { - for (std::size_t jb = 0; jb < njobs; ++jb) { - out[jl*njobs+jb] = sin(cenlat[jl] * Constants::deg2rad); + for (std::size_t jloc = 0; jloc < nlocs; ++jloc) { + for (std::size_t jvar = 0; jvar < nvars; ++jvar) { + out[jloc*nvars+jvar] = sin(cenlat[jloc] * Constants::deg2rad); } } } diff --git a/src/ufo/predictors/SineOfLatitude.h b/src/ufo/predictors/SineOfLatitude.h index 940429da2..da925a3e6 100644 --- a/src/ufo/predictors/SineOfLatitude.h +++ b/src/ufo/predictors/SineOfLatitude.h @@ -8,14 +8,16 @@ #ifndef UFO_PREDICTORS_SINEOFLATITUDE_H_ #define UFO_PREDICTORS_SINEOFLATITUDE_H_ -#include - #include "ufo/predictors/PredictorBase.h" namespace eckit { class Configuration; } +namespace oops { + class Variables; +} + namespace ioda { class ObsSpace; } @@ -26,8 +28,7 @@ namespace ufo { class SineOfLatitude : public PredictorBase { public: - SineOfLatitude(const eckit::Configuration &, const std::vector &); - ~SineOfLatitude() {} + SineOfLatitude(const eckit::Configuration &, const oops::Variables &); void compute(const ioda::ObsSpace &, const GeoVaLs &, diff --git a/src/ufo/predictors/Thickness.cc b/src/ufo/predictors/Thickness.cc new file mode 100644 index 000000000..a93fcd9f3 --- /dev/null +++ b/src/ufo/predictors/Thickness.cc @@ -0,0 +1,128 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include "ioda/ObsSpace.h" +#include "ufo/GeoVaLs.h" +#include "ufo/predictors/Thickness.h" +#include "ufo/utils/Constants.h" + +namespace ufo { + +static PredictorMaker makerFuncThickness_("thickness"); + +// ----------------------------------------------------------------------------- + +Thickness::Thickness(const eckit::Configuration & conf, const oops::Variables & vars) + : PredictorBase(conf, vars) { + // required variables + geovars_ += oops::Variables({"air_temperature", + "air_pressure"}); + // required options + eckit::LocalConfiguration bconf(conf, "options"); + parameters_.validateAndDeserialize(bconf); + + // override the predictor name to distinguish between + // thickness predictors at different pressure layers + name() = name() + "_" + std::to_string(static_cast(parameters_.layerBase.value()/100)) + + '_' + std::to_string(static_cast(parameters_.layerTop.value()/100)) + + "hPa"; +} + +// ----------------------------------------------------------------------------- + +void Thickness::compute(const ioda::ObsSpace & odb, + const GeoVaLs & geovals, + const ObsDiagnostics &, + ioda::ObsVector & out) const { + const std::size_t nlocs = odb.nlocs(); + const std::size_t nvars = out.nvars(); + + // assure shape of out + ASSERT(out.nlocs() == nlocs); + + const int t_levs = geovals.nlevs("air_temperature"); + const int p_levs = geovals.nlevs("air_pressure"); + std::vector p_prof(t_levs, 0.0); + std::vector t_prof(p_levs, 0.0); + std::vector thick(odb.nlocs(), 0.0); + + const double p_high = parameters_.layerTop.value(); + const double p_low = parameters_.layerBase.value(); + const double pred_mean = parameters_.mean.value(); + const double pred_std_inv = 1.0/parameters_.stDev.value();; + + for (std::size_t jl = 0; jl < nlocs; ++jl) { + geovals.getAtLocation(p_prof, "air_pressure", jl); + geovals.getAtLocation(t_prof, "air_temperature", jl); + std::reverse(p_prof.begin(), p_prof.end()); + std::reverse(t_prof.begin(), t_prof.end()); + + // Check that layer top is within pressure levels + if (p_high > p_prof.back()) { + oops::Log::error() << "layer top is greater than largest model pressure level" << std::endl; + throw eckit::BadValue("layer top is greater than largest model pressure level", Here()); + } + + auto lower = std::lower_bound(p_prof.begin(), p_prof.end(), p_high); + int i = lower - p_prof.begin(); + + // find the thickness of the top fraction layer. + double dp = p_prof[i] - p_high; + double f = dp/(p_prof[i] - p_prof[i-1]); + double p_av = p_prof[i] - 0.5*dp; + double t_av = 0.5*((2.0-f)*t_prof[i]+ f*t_prof[i-1]); + thick[jl] = t_av*(dp/p_av); + i += 1; + + // For all the levels in the desired thickness band + while (p_prof[i] < p_low && i < p_levs - 1) { + dp = p_prof[i] - p_prof[i-1]; + p_av = p_prof[i] - 0.5*dp; + t_av = 0.5*(t_prof[i] + t_prof[i-1]); + thick[jl] += t_av*(dp/p_av); + i += 1; + } + + // When pressure level below thickness band or at last level + if (p_prof[i] >= p_low) { + dp = p_low - p_prof[i-1]; + f = dp/(p_prof[i] - p_prof[i-1]); + p_av = p_low - 0.5*dp; + t_av = 0.5*((f)*t_prof[i]+ (2.0-f)*t_prof[i-1]); + thick[jl] += t_prof[i-1]*(dp/p_av); + // Note that the line replicates OPS which contains a bug + // the eqaution should be thick[jl] += t_av*(dp/p_av); + } else { + // Add thickness to last model level + dp = p_prof[i] - p_prof[i-1]; + p_av = p_prof[i] - 0.5*dp; + t_av = 0.5*(t_prof[i] + t_prof[i-1]); + thick[jl] += t_av*(dp/p_av); + // Then assume constant temperature until p_low + dp = p_low - p_prof[i]; + p_av = p_low - 0.5*dp; + t_av = t_prof[i]; + thick[jl] += t_av*(dp/p_av); + } + } + + const double km_per_m = 1e-3; + const double dry_air_gas_const = 287.0; // Constants::rd not used for compatibility with OPS + + for (std::size_t jl = 0; jl < nlocs; ++jl) { + for (std::size_t jb = 0; jb < nvars; ++jb) { + out[jl*nvars+jb] = (thick[jl] + *(dry_air_gas_const/Constants::grav)*km_per_m - pred_mean) + *pred_std_inv; + } + } +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/predictors/Thickness.h b/src/ufo/predictors/Thickness.h new file mode 100644 index 000000000..984d8ba7e --- /dev/null +++ b/src/ufo/predictors/Thickness.h @@ -0,0 +1,72 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PREDICTORS_THICKNESS_H_ +#define UFO_PREDICTORS_THICKNESS_H_ + +#include +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "ufo/predictors/PredictorBase.h" + + +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + +/// Parameters controlling the thickness predictor. +class ThicknessParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ThicknessParameters, Parameters) + + public: + /// Pressure value (Pa) at the top of the required thickness layer + oops::RequiredParameter layerTop{"layer top", this}; + + /// Pressure value (Pa) at the bottom of the required thickness layer + oops::RequiredParameter layerBase{"layer base", this}; + + /// Climatological mean of predictor + oops::RequiredParameter mean{"mean", this}; + + /// Climatological standard deviation of predictor + oops::RequiredParameter stDev{"standard deviation", this}; +}; + +// ----------------------------------------------------------------------------- + +/** + *This thickness predictor calculates the thickness of a specified pressure level interval. + *The thickness is calculated as the difference between the geopotential heights at two pressure + *levels. This requires the integration of the temperature with respect to log pressure. + *The thicknesses are based on geovals temperature and pressure profiles + */ + + +class Thickness : public PredictorBase { + public: + Thickness(const eckit::Configuration &, const oops::Variables &); + + void compute(const ioda::ObsSpace &, + const GeoVaLs &, + const ObsDiagnostics &, + ioda::ObsVector &) const override; + + private: + ThicknessParameters parameters_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_PREDICTORS_THICKNESS_H_ diff --git a/src/ufo/profile/CMakeLists.txt b/src/ufo/profile/CMakeLists.txt index a4a835409..cc3470978 100644 --- a/src/ufo/profile/CMakeLists.txt +++ b/src/ufo/profile/CMakeLists.txt @@ -8,6 +8,21 @@ set ( profile_files DataHandlerParameters.h EntireSampleDataHandler.cc EntireSampleDataHandler.h + ModelHeightCalculator.cc + ModelHeightCalculator.h + ModelParameters.h + ObsProfileAverage.cc + ObsProfileAverage.h + ObsProfileAverageParameters.h + ProfileAveragePressure.cc + ProfileAveragePressure.h + ProfileAverageRelativeHumidity.cc + ProfileAverageRelativeHumidity.h + ProfileAverageTemperature.cc + ProfileAverageTemperature.h + ProfileAverageWindSpeed.cc + ProfileAverageWindSpeed.h + ProfileCheckBasic.cc ProfileChecker.cc ProfileChecker.h ProfileCheckBackgroundGeopotentialHeight.cc @@ -38,16 +53,31 @@ set ( profile_files ProfileCheckTime.h ProfileCheckUInterp.cc ProfileCheckUInterp.h + ProfileCheckUInterpAlternative.cc + ProfileCheckUInterpAlternative.h ProfileCheckUnstableLayer.cc ProfileCheckUnstableLayer.h ProfileCheckValidator.cc ProfileCheckValidator.h ProfileDataHandler.cc ProfileDataHandler.h + ProfileDataHolder.cc + ProfileDataHolder.h ProfileIndices.cc ProfileIndices.h + ProfilePressure.cc + ProfilePressure.h + ProfileSondeFlags.cc + ProfileSondeFlags.h ProfileStandardLevels.cc ProfileStandardLevels.h + ProfileVerticalAveraging.cc + ProfileVerticalAveraging.h + ProfileVerticalInterpolation.cc + ProfileVerticalInterpolation.h + ProfileWindProfilerFlags.cc + ProfileWindProfilerFlags.h + VariableNames.cc VariableNames.h ) diff --git a/src/ufo/profile/DataHandlerParameters.h b/src/ufo/profile/DataHandlerParameters.h index 4d3d5a615..ac7dd4cbd 100644 --- a/src/ufo/profile/DataHandlerParameters.h +++ b/src/ufo/profile/DataHandlerParameters.h @@ -11,9 +11,12 @@ #include #include +#include "oops/util/parameters/OptionalParameter.h" #include "oops/util/parameters/Parameter.h" #include "oops/util/parameters/Parameters.h" +#include "ufo/profile/ModelParameters.h" + namespace eckit { class Configuration; } @@ -23,7 +26,7 @@ namespace ufo { /// \brief Options controlling the operation of the EntireSampleDataHandler /// and ProfileDataHandler classes. class DataHandlerParameters : public oops::Parameters { - OOPS_ABSTRACT_PARAMETERS(DataHandlerParameters, Parameters) + OOPS_CONCRETE_PARAMETERS(DataHandlerParameters, Parameters) public: // functions /// Determine whether a variable group is optional or not. @@ -41,9 +44,18 @@ namespace ufo { { size_t entriesPerProfile = 0; // Variables with one entry per profile. - if (std::find(groups_singlevalue.value().begin(), groups_singlevalue.value().end(), groupname) + if (std::find(groups_singlevalue.value().begin(), + groups_singlevalue.value().end(), groupname) != groups_singlevalue.value().end()) { entriesPerProfile = 1; + } else if (std::find(groups_modellevels.value().begin(), + groups_modellevels.value().end(), groupname) + != groups_modellevels.value().end()) { + entriesPerProfile = ModParameters.numModelLevels(); + } else if (std::find(groups_modelrholevels.value().begin(), + groups_modelrholevels.value().end(), groupname) + != groups_modelrholevels.value().end()) { + entriesPerProfile = ModParameters.numModelLevels_rho(); } return entriesPerProfile; } @@ -56,10 +68,34 @@ namespace ufo { /// Groups of variables which have one value per profile. oops::Parameter> groups_singlevalue - {"groups_singlevalue", {"Counters"}, this}; + {"groups_singlevalue", {}, this}; + + /// Groups of variables which are on model levels. + oops::Parameter> groups_modellevels + {"groups_modellevels", + {"ModelLevelsDerivedValue", "ModelLevelsQCFlags"}, this}; + + /// Groups of variables which are on model rho levels. + oops::Parameter> groups_modelrholevels + {"groups_modelrholevels", + {"ModelRhoLevelsDerivedValue", "ModelRhoLevelsFlags"}, this}; /// Number of errors, accumulated over checks, that cause the observation to have failed. oops::Parameter nErrorsFail {"nErrorsFail", 1, this}; + + /// Maximum number of profile levels to be processed (a legacy of the OPS code). + /// No maximum is assigned if this parameter is not specified. + oops::OptionalParameter maxlev {"maxlev", this}; + + /// If not sorting observations, ensure number of profiles is consistent + oops::Parameter ValidateTotalNumProf {"ValidateTotalNumProf", true, this}; + + /// Output filename for saving derived values on model levels. + oops::Parameter ModelLevelsDerivedValuesFilename + {"ModelLevelsDerivedValuesFilename", "ModelLevelsDerivedValues.nc4", this}; + + /// Parameters related to the model. + ModelParameters ModParameters{this}; }; } // namespace ufo diff --git a/src/ufo/profile/EntireSampleDataHandler.cc b/src/ufo/profile/EntireSampleDataHandler.cc index 2863189f2..19b121f13 100644 --- a/src/ufo/profile/EntireSampleDataHandler.cc +++ b/src/ufo/profile/EntireSampleDataHandler.cc @@ -19,7 +19,7 @@ namespace ufo { void EntireSampleDataHandler::writeQuantitiesToObsdb() { - // Write out all variables in the QCFlags and Corrections groups. + // Write out all variables in particular groups. for (const auto& it_data : entireSampleData_) { std::string fullname = it_data.first; std::string varname; @@ -28,7 +28,11 @@ namespace ufo { if (groupname == "QCFlags") { putDataVector(fullname, get(fullname)); - } else if (groupname == "Corrections") { + } else if (groupname == "Corrections" || + groupname == "DerivedValue") { + // todo(ctgh): Add ModelLevelsDerivedValue, ModelRhoLevelsDerivedValue, + // ModelLevelsFlags and ModelRhoLevelsFlags to this list when + // it is possible to save variables with different nlocs. putDataVector(fullname, get(fullname)); } } @@ -38,4 +42,28 @@ namespace ufo { get(ufo::VariableNames::counter_NumAnyErrors); putDataVector(ufo::VariableNames::counter_NumAnyErrors, NumAnyErrors); } + + int EntireSampleDataHandler::defaultValue(const std::vector &vec, + const std::string &groupname) + { + if (groupname == "Counters") + return 0; + else + return missingValueInt; + } + + float EntireSampleDataHandler::defaultValue(const std::vector &vec, + const std::string &groupname) + { + if (groupname == "Corrections") + return 0.0f; + else + return missingValueFloat; + } + + std::string EntireSampleDataHandler::defaultValue(const std::vector &vec, + const std::string &groupname) + { + return missingValueString; + } } // namespace ufo diff --git a/src/ufo/profile/EntireSampleDataHandler.h b/src/ufo/profile/EntireSampleDataHandler.h index 885e73afe..fefc2fe66 100644 --- a/src/ufo/profile/EntireSampleDataHandler.h +++ b/src/ufo/profile/EntireSampleDataHandler.h @@ -24,6 +24,8 @@ #include "ioda/ObsDataVector.h" #include "ioda/ObsSpace.h" +#include "oops/util/missingValues.h" + #include "ufo/profile/DataHandlerParameters.h" #include "ufo/utils/metoffice/MetOfficeQCFlags.h" @@ -59,46 +61,33 @@ namespace ufo { std::string varname; std::string groupname; ufo::splitVarGroup(fullname, varname, groupname); - bool optional = options_.getOptional(groupname); - size_t entriesPerProfile = options_.getEntriesPerProfile(groupname); + const bool optional = options_.getOptional(groupname); + const size_t entriesPerProfile = options_.getEntriesPerProfile(groupname); std::vector vec_all; // Vector storing data for entire sample. - if (entireSampleData_.find(fullname) != entireSampleData_.end()) { + auto it_entireSampleData = entireSampleData_.find(fullname); + if (it_entireSampleData != entireSampleData_.end()) { // If the vector is already present, return it. // If the type T is incorrect then boost::get will return an exception. // Provide additional information if that occurs. try { - return boost::get> (entireSampleData_[fullname]); + return boost::get> (it_entireSampleData->second); } catch (boost::bad_get) { - throw eckit::BadParameter("Template parameter passed to boost::get " - "probably has the wrong type", Here()); + throw eckit::BadParameter("Template parameter passed to boost::get for " + + fullname + " probably has the wrong type", Here()); } } else if (obsdb_.has(groupname, varname) || optional) { // Initially fill the vector with the default value for the type T. if (entriesPerProfile == 0) { - vec_all.assign(obsdb_.nlocs(), defaultValue(vec_all)); + vec_all.assign(obsdb_.nlocs(), defaultValue(vec_all, groupname)); } else { - vec_all.assign(entriesPerProfile * obsdb_.nrecs(), defaultValue(vec_all)); + vec_all.assign(entriesPerProfile * obsdb_.nrecs(), defaultValue(vec_all, groupname)); } // Retrieve variable from the obsdb if present, overwriting the default value. if (obsdb_.has(groupname, varname)) obsdb_.get_db(groupname, varname, vec_all); } - // If the vector contains entirely missing values, clear it. - T missingValue; // Missing value for type T. - if (std::is_same::value) - missingValue = util::missingValue(1); - else if (std::is_same::value) - missingValue = util::missingValue(1.0f); - bool allMissing = true; // Signifies all elements in the vector are missing. - for (size_t idx = 0; allMissing && idx < vec_all.size(); ++idx) - allMissing = vec_all[idx] == missingValue; - if (allMissing) { - oops::Log::debug() << "All elements of " << fullname << " are missing" << std::endl; - vec_all.clear(); - } - - // Add vector to map (even if it is empty). + // Add vector to map. entireSampleData_.emplace(fullname, std::move(vec_all)); return boost::get> (entireSampleData_[fullname]); } @@ -108,7 +97,31 @@ namespace ufo { /// configurable list if requred. void writeQuantitiesToObsdb(); - private: + /// Initialise vector in the entire sample for a variable that is not currently + /// stored. Fill the vector with the default value for the data type. + template + void initialiseVector(const std::string fullname) + { + auto it_entireSampleData = entireSampleData_.find(fullname); + if (it_entireSampleData == entireSampleData_.end() || + (it_entireSampleData != entireSampleData_.end() && + get(fullname).size() == 0)) { + std::string varname; + std::string groupname; + ufo::splitVarGroup(fullname, varname, groupname); + const size_t entriesPerProfile = options_.getEntriesPerProfile(groupname); + std::vector vec_all; // Vector storing data for entire sample. + if (entriesPerProfile == 0) { + vec_all.assign(obsdb_.nlocs(), defaultValue(vec_all, groupname)); + } else { + vec_all.assign(entriesPerProfile * obsdb_.nrecs(), + defaultValue(vec_all, groupname)); + } + entireSampleData_[fullname] = vec_all; + } + } + + private: // functions /// Put entire data vector on obsdb. template void putDataVector(const std::string &fullname, @@ -123,6 +136,7 @@ namespace ufo { obsdb_.put_db(groupname, varname, datavec); } + private: // variables /// Observation database. ioda::ObsSpace &obsdb_; @@ -130,17 +144,26 @@ namespace ufo { const DataHandlerParameters &options_; /// Default value used to fill vector of integers. - int defaultValue(const std::vector &vec) {return 0;} + int defaultValue(const std::vector &vec, const std::string &groupname); /// Default value used to fill vector of floats. - float defaultValue(const std::vector &vec) {return 0.0f;} + float defaultValue(const std::vector &vec, const std::string &groupname); /// Default value used to fill vector of strings. - std::string defaultValue(const std::vector &vec) {return "";} + std::string defaultValue(const std::vector &vec, const std::string &groupname); /// Container of each variable in the entire data set. std::unordered_map , std::vector , std::vector >> entireSampleData_; + + /// Missing value (int) + const int missingValueInt = util::missingValue(missingValueInt); + + /// Missing value (float) + const float missingValueFloat = util::missingValue(missingValueFloat); + + /// Missing value (string) + const std::string missingValueString = util::missingValue(missingValueString); }; } // namespace ufo diff --git a/src/ufo/profile/ModelHeightCalculator.cc b/src/ufo/profile/ModelHeightCalculator.cc new file mode 100644 index 000000000..928715f74 --- /dev/null +++ b/src/ufo/profile/ModelHeightCalculator.cc @@ -0,0 +1,60 @@ +/* + * (C) Copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "oops/util/CompareNVectors.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" +#include "oops/util/PropertiesOfNVectors.h" + +#include "ufo/profile/ModelHeightCalculator.h" + +namespace ufo { + void CalculateModelHeight(const ModelParameters &options, + const float orogGeoVaLs, + std::vector &zRhoGeoVaLs, + std::vector &zThetaGeoVaLs) + { + const std::vector &etaThetaGeoVaLs = options.etaTheta; + const std::vector &etaRhoGeoVaLs = options.etaRho; + + if (!oops::allVectorsSameNonZeroSize(etaThetaGeoVaLs, + etaRhoGeoVaLs)) + { + oops::Log::warning() << "At least one vector is the wrong size. " + << "Model height calculation will not be performed." << std::endl; + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(etaThetaGeoVaLs, + etaRhoGeoVaLs) + << std::endl; + return; + } + + const float missingValueFloat = util::missingValue(missingValueFloat); + const float zModelTop = options.zModelTop; + const int firstConstantRhoLevel = options.firstConstantRhoLevel; + const size_t NumModLevels = etaThetaGeoVaLs.size(); + + zRhoGeoVaLs.assign(NumModLevels + 1, missingValueFloat); + zThetaGeoVaLs.assign(NumModLevels, missingValueFloat); + + // Calculate heights of rho and theta levels using terrain-following height coordinate. + for (int jlev = 0; jlev < NumModLevels; ++jlev) { + zRhoGeoVaLs[jlev] = etaRhoGeoVaLs[jlev] * zModelTop; + zThetaGeoVaLs[jlev] = etaThetaGeoVaLs[jlev] * zModelTop; + } + // Calculate height of extra rho level. + zRhoGeoVaLs[NumModLevels] = + zThetaGeoVaLs[NumModLevels - 1] * 2.0 - zRhoGeoVaLs[NumModLevels - 1]; + // Quadratic method used to account for local orography. + for (int k = 0; k < firstConstantRhoLevel; ++k) { + zRhoGeoVaLs[k] += orogGeoVaLs * + std::pow(1.0 - etaRhoGeoVaLs[k] / etaRhoGeoVaLs[firstConstantRhoLevel], 2.0); + zThetaGeoVaLs[k] += orogGeoVaLs * + std::pow(1.0 - etaThetaGeoVaLs[k] / etaRhoGeoVaLs[firstConstantRhoLevel], 2.0); + } + } +} // namespace ufo diff --git a/src/ufo/profile/ModelHeightCalculator.h b/src/ufo/profile/ModelHeightCalculator.h new file mode 100644 index 000000000..157a6d842 --- /dev/null +++ b/src/ufo/profile/ModelHeightCalculator.h @@ -0,0 +1,30 @@ +/* + * (C) Copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_MODELHEIGHTCALCULATOR_H_ +#define UFO_PROFILE_MODELHEIGHTCALCULATOR_H_ + +#include + +#include "ufo/profile/ModelParameters.h" + +namespace ufo { + /// \brief Calculate model heights on rho and theta levels. + /// The calculation uses the terrain-following height coordinate (eta) and + /// the local orography. + /// + /// \param[in] options: configuration options related to GeoVaLs. + /// \param[in] orogGeoVaLs: orography GeoVaLs. + /// \param[out] zRhoGeoVaLs: model heights on rho levels. + /// \param[out] zThetaGeoVaLs: model heights on theta levels. + void CalculateModelHeight(const ModelParameters &options, + const float orogGeoVaLs, + std::vector &zRhoGeoVaLs, + std::vector &zThetaGeoVaLs); +} // namespace ufo + +#endif // UFO_PROFILE_MODELHEIGHTCALCULATOR_H_ diff --git a/src/ufo/profile/ModelParameters.h b/src/ufo/profile/ModelParameters.h new file mode 100644 index 000000000..ff27dc42e --- /dev/null +++ b/src/ufo/profile/ModelParameters.h @@ -0,0 +1,77 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_MODELPARAMETERS_H_ +#define UFO_PROFILE_MODELPARAMETERS_H_ + +#include +#include + +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" + +namespace eckit { + class Configuration; +} + +namespace ufo { + + /// \brief Options related to GeoVaLs used in the profile QC code. + class ModelParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ModelParameters, Parameters) + + public: + /// Height of the upper boundary of the highest model layer. + oops::Parameter zModelTop {"zModelTop", 80000.0, this}; + + /// First model rho level at which there is no geographical variation in the + /// height. + oops::Parameter firstConstantRhoLevel {"firstConstantRhoLevel", 49, this}; + + /// Values of terrain-following height coordinate (eta) on theta levels. + oops::Parameter> etaTheta {"etaTheta", + {0.00025, 0.0006667, 0.00125, 0.002, 0.0029167, 0.004, + 0.00525, 0.0066667, 0.00825, 0.01, 0.0119167, 0.014, 0.01625, 0.0186667, + 0.02125, 0.024, 0.0269167, 0.03, 0.03325, 0.0366667, 0.04025, 0.044, + 0.0479167, 0.052, 0.05625, 0.0606667, 0.06525, 0.07, 0.0749167, 0.08, + 0.08525, 0.0906668, 0.0962505, 0.1020017, 0.1079213, 0.1140113, + 0.1202745, 0.1267154, 0.1333406, 0.1401592, 0.1471838, 0.1544313, + 0.1619238, 0.1696895, 0.1777643, 0.1861929, 0.1950307, 0.2043451, + 0.2142178, 0.2247466, 0.236048, 0.2482597, 0.2615432, 0.2760868, + 0.2921094, 0.3098631, 0.3296378, 0.3517651, 0.3766222, 0.4046373, + 0.4362943, 0.4721379, 0.5127798, 0.5589045, 0.6112759, 0.6707432, + 0.73825, 0.8148403, 0.9016668, 1}, + this}; + + /// Value of terrain-following height coordinate (eta) on rho levels. + oops::Parameter> etaRho {"etaRho", + {0.000125, 0.0004583, 0.0009583, 0.001625, 0.0024583, + 0.0034583, 0.004625, 0.0059583, 0.0074583, 0.009125, 0.0109583, + 0.0129583, 0.015125, 0.0174583, 0.0199583, 0.022625, 0.0254583, + 0.0284583, 0.031625, 0.0349583, 0.0384583, 0.042125, 0.0459583, + 0.0499583, 0.054125, 0.0584584, 0.0629583, 0.067625, 0.0724583, + 0.0774583, 0.082625, 0.0879584, 0.0934586, 0.0991261, 0.1049615, + 0.1109663, 0.1171429, 0.123495, 0.130028, 0.1367499, 0.1436715, + 0.1508076, 0.1581776, 0.1658067, 0.1737269, 0.1819786, 0.1906118, + 0.1996879, 0.2092815, 0.2194822, 0.2303973, 0.2421538, 0.2549014, + 0.268815, 0.2840981, 0.3009862, 0.3197505, 0.3407014, 0.3641936, + 0.3906297, 0.4204658, 0.4542161, 0.4924589, 0.5358422, 0.5850902, + 0.6410096, 0.7044966, 0.7765451, 0.8582535, 0.9508334}, + this}; + + /// Number of model theta levels. + size_t numModelLevels() const {return etaTheta.value().size();} + + /// Number of model rho levels. + /// Assume there is one more rho level than there are theta levels. + size_t numModelLevels_rho() const {return etaTheta.value().size() + 1;} + }; +} // namespace ufo + +#endif // UFO_PROFILE_MODELPARAMETERS_H_ + diff --git a/src/ufo/profile/ObsProfileAverage.cc b/src/ufo/profile/ObsProfileAverage.cc new file mode 100644 index 000000000..ce2e5edfe --- /dev/null +++ b/src/ufo/profile/ObsProfileAverage.cc @@ -0,0 +1,208 @@ +/* + * (C) Copyright 2021 UK Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/profile/ObsProfileAverage.h" + +#include +#include +#include +#include + +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/FloatCompare.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/Locations.h" +#include "ufo/ObsDiagnostics.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static ObsOperatorMaker obsProfileAverageMaker_("ProfileAverage"); +// ----------------------------------------------------------------------------- + +ObsProfileAverage::ObsProfileAverage(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : ObsOperatorBase(odb, config), odb_(odb) +{ + oops::Log::trace() << "ObsProfileAverage constructor starting" << std::endl; + + // Ensure observations have been grouped into profiles. + if (odb_.obs_group_vars().empty()) + throw eckit::UserError("Group variables configuration is empty", Here()); + + // Check the ObsSpace has been extended. If this is not the case + // then it will not be possible to access profiles in the original and + // extended sections of the ObsSpace. + if (!odb_.has("MetaData", "extended_obs_space")) + throw eckit::UserError("The extended obs space has not been produced", Here()); + + // Check the observed pressure is present. + if (!odb_.has("MetaData", "air_pressure")) + throw eckit::UserError("air_pressure@MetaData not present", Here()); + + // Set up configuration options. + options_.validateAndDeserialize(config); + + // Add air_pressure_levels to the list of variables used in this operator. + // todo(ctgh): obtain the required simulated variables in a future PR. + requiredVars_ += oops::Variables({"air_pressure_levels"}); + + // If required, set up vectors for OPS comparison. + if (options_.compareWithOPS.value()) + setUpAuxiliaryReferenceVariables(); + + oops::Log::trace() << "ObsProfileAverage constructor finished" << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsProfileAverage::~ObsProfileAverage() { + oops::Log::trace() << "ObsProfileAverage destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsProfileAverage::simulateObs(const GeoVaLs & gv, ioda::ObsVector & ovec, + ObsDiagnostics & ydiags) const { + oops::Log::trace() << "ObsProfileAverage: simulateObs entered" << std::endl; + + const float missing = util::missingValue(missing); + + // Get variable indicating which section of the ObsSpace a profile is in. + std::vector extended_obs_space(odb_.nlocs()); + odb_.get_db("MetaData", "extended_obs_space", extended_obs_space); + + // Get observed pressure. + std::vector pressure_obs(odb_.nlocs()); + odb_.get_db("MetaData", "air_pressure", pressure_obs); + + // Get correspondence between record numbers and indices in the total sample. + const std::vector &recnums = odb_.recidx_all_recnums(); + // Number of profiles in the original ObsSpace. + const std::size_t nprofs = recnums.size() / 2; + + // Loop over profiles. + for (std::size_t jprof = 0; jprof < nprofs; ++jprof) { + oops::Log::debug() << "Profile " << (jprof + 1) << " / " << nprofs << std::endl; + + // Get locations of profile in the original ObsSpace and + // the corresponding profile in the extended ObsSpace. + // Assuming the extended ObsSpace has been configured correctly, which is + // checked above, the profile in the extended ObsSpace is always located + // nprofs positions further on than the profile in the original ObsSpace. + const std::vector &locsOriginal = odb_.recidx_vector(recnums[jprof]); + const std::vector &locsExtended = odb_.recidx_vector(recnums[jprof + nprofs]); + + // Observed pressures for this profile. + std::vector pObs; + for (const std::size_t loc : locsOriginal) + pObs.push_back(pressure_obs[loc]); + + // Set up GeoVaLs and H(x) vectors. + // Number of levels for air_pressure_levels. + const std::size_t nlevs = gv.nlevs("air_pressure_levels"); + // Vector storing location for each level along the slant path. + // Initially the first location in the profile is used everywhere. + std::vector slant_path_location(nlevs, 0); + // Vector used to store different pressure GeoVaLs. + std::vector pressure_gv(nlevs); + // Loop over model levels and find intersection of profile with model layer boundary. + for (std::size_t mlev = 0; mlev < nlevs; ++mlev) { + for (int iter = 0; iter < options_.numIntersectionIterations.value(); ++iter) { + for (std::size_t jloc = slant_path_location[mlev]; jloc < pObs.size(); ++jloc) { + gv.getAtLocation(pressure_gv, "air_pressure_levels", jloc); + // If pressure has not been recorded, move to the next level. + if (pObs[jloc] == missing) continue; + // Break from the loop if the observed pressure is lower than + // the pressure of this model level. + if (pObs[jloc] <= pressure_gv[mlev]) break; + // Record the value of this location at this level and all above. + // This ensures that missing values are dealt with correctly. + for (std::size_t mlevcolumn = mlev; mlevcolumn < nlevs; ++mlevcolumn) + slant_path_location[mlevcolumn] = jloc; + } + } + } + + // Fill slanted pressure vector. + // todo(ctgh): fill other simulated variables in a future PR. + std::vector slant_pressure; + for (std::size_t mlev = 0; mlev < nlevs; ++mlev) { + const std::size_t jloc = slant_path_location[mlev]; + gv.getAtLocation(pressure_gv, "air_pressure_levels", jloc); + slant_pressure.push_back(pressure_gv[mlev]); + } + + // Fill H(x) in the extended ObsSpace. + // todo(ctgh): do this in a future PR. + // for (std::size_t jloc = 0; jloc < locsExtended.size(); ++jloc) { + // for (std::size_t jvar = 0; jvar < ovec.nvars(); ++jvar) { + // ovec[(jloc + locsExtended.front()) * ovec.nvars() + jvar] = slant_variable[jloc]; + // } + // } + + // If required, compare slant path locations and slant pressure with OPS. + if (options_.compareWithOPS.value()) + compareAuxiliaryVariables(locsExtended, + slant_path_location, + slant_pressure); + } + oops::Log::trace() << "ObsProfileAverage: simulateObs exit " << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsProfileAverage::setUpAuxiliaryReferenceVariables() { + if (!(odb_.has("MetOfficeHofX", "slant_path_location") && + odb_.has("MetOfficeHofX", "slant_pressure"))) + throw eckit::UserError("At least one reference variable is not present", Here()); + // Get reference values of the slant path locations and pressures. + slant_path_location_ref_.resize(odb_.nlocs()); + slant_pressure_ref_.resize(odb_.nlocs()); + odb_.get_db("MetOfficeHofX", "slant_path_location", slant_path_location_ref_); + odb_.get_db("MetOfficeHofX", "slant_pressure", slant_pressure_ref_); +} + +// ----------------------------------------------------------------------------- + +void ObsProfileAverage::compareAuxiliaryVariables +(const std::vector &locsExtended, + const std::vector &slant_path_location, + const std::vector &slant_pressure) const { + std::vector slant_path_location_ref_profile; + std::vector slant_pressure_ref_profile; + for (const std::size_t loc : locsExtended) { + slant_path_location_ref_profile.push_back(slant_path_location_ref_[loc]); + slant_pressure_ref_profile.push_back(slant_pressure_ref_[loc]); + } + for (std::size_t jloccomp = 0; jloccomp < locsExtended.size(); ++jloccomp) { + if (slant_path_location[jloccomp] != slant_path_location_ref_profile[jloccomp]) + throw eckit::BadValue("Mismatch for slant_path_location, jloccomp = " + + jloccomp, Here()); + if (!oops::is_close_relative(slant_pressure[jloccomp], + slant_pressure_ref_profile[jloccomp], + 1e-9f)) + throw eckit::BadValue("Mismatch for slant_pressure, jloccomp = " + + jloccomp, Here()); + } +} + +// ----------------------------------------------------------------------------- + +void ObsProfileAverage::print(std::ostream & os) const { + os << "ObsProfileAverage operator" << std::endl; + os << "config = " << options_ << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/profile/ObsProfileAverage.h b/src/ufo/profile/ObsProfileAverage.h new file mode 100644 index 000000000..092578787 --- /dev/null +++ b/src/ufo/profile/ObsProfileAverage.h @@ -0,0 +1,118 @@ +/* + * (C) Copyright 2021 UK Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_OBSPROFILEAVERAGE_H_ +#define UFO_PROFILE_OBSPROFILEAVERAGE_H_ + +#include +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" + +#include "ufo/ObsOperatorBase.h" + +#include "ufo/profile/ObsProfileAverageParameters.h" + +/// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class Locations; + class ObsDiagnostics; + +/// \brief Observation operator for profile averaging. +/// +/// This observation operator produces H(x) vectors which correspond to vertically-averaged +/// profiles. The algorithm determines the locations at which reported-level profiles +/// intersect each model pressure level (based on the air_pressure_levels GeoVaL). +/// This is done by stepping through the observation locations from the lowest-altitude value +/// upwards. For each model level, the location of the observation whose pressure is larger than, +/// and closest to, the model pressure is recorded. +/// +/// If there are no observations in a model level, which can occur for (e.g.) sondes reporting in +/// low-frequency TAC format, the location corresponding to the last filled level is used. +/// (If there are some model levels closer to the surface than the lowest-altitude observation, +/// the location of the lowest observation is used for these levels.) +/// +/// This procedure is iterated multiple times in order to account for the fact that model pressures +/// can be slanted close to the Earth's surface. +/// The number of iterations is configured with the \p numIntersectionIterations parameter. +/// +/// Having obtained the profile boundaries, values of model pressure and any simulated variables +/// are obtained as in the locations that were determined in the procedure above. +/// This produces a single column of model values which are used as the H(x) variable. +/// In essence, this operator converts a set of GeoVaLs to what is referred to as a 'CX column' +/// in OPS terminology. +/// +/// In order for this operator to work correctly the ObsSpace must have been extended as in +/// the following yaml snippet: +/// +/// - obs space: +/// extension: +/// average profiles onto model levels: 71 +/// +/// (where 71 can be replaced by the length of the air_pressure_levels GeoVaL). +/// The H(x) values are placed in the extended section of the ObsSpace. +/// Note that, unlike what may be expected for an observation operator, averaging of the model +/// values across each layer is not performed; a single model value is used in each case. +/// This follows what is used in OPS. Alternatives could be considered in the future. +/// +/// A comparison with OPS is be performed if the option \p compareWithOPS is set to true. +/// This checks values of the locations and pressure values associated with the slant path. +/// All other comparisons are performed with the standard 'vector ref' option in the yaml file. +class ObsProfileAverage : public ObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsProfileAverage";} + + ObsProfileAverage(const ioda::ObsSpace &, const eckit::Configuration &); + ~ObsProfileAverage() override; + + void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; + + const oops::Variables & requiredVars() const override { return requiredVars_; } + + private: + /// Set up auxiliary reference variables that are used for comparison with OPS. + void setUpAuxiliaryReferenceVariables(); + + /// Compare auxiliary reference variables with those obtained in OPS. + void compareAuxiliaryVariables(const std::vector &locsExtended, + const std::vector &slant_path_location, + const std::vector &slant_pressure) const; + + void print(std::ostream &) const override; + + private: + const ioda::ObsSpace& odb_; + oops::Variables requiredVars_; + + /// Options for this observation operator. + ObsProfileAverageParameters options_; + + /// Reference values of slant path locations. + std::vector slant_path_location_ref_; + + /// Reference values of slant path pressures. + std::vector slant_pressure_ref_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_PROFILE_OBSPROFILEAVERAGE_H_ diff --git a/src/ufo/profile/ObsProfileAverageParameters.h b/src/ufo/profile/ObsProfileAverageParameters.h new file mode 100644 index 000000000..ef70599c8 --- /dev/null +++ b/src/ufo/profile/ObsProfileAverageParameters.h @@ -0,0 +1,41 @@ +/* + * (C) Copyright 2021 UK Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_OBSPROFILEAVERAGEPARAMETERS_H_ +#define UFO_PROFILE_OBSPROFILEAVERAGEPARAMETERS_H_ + +#include +#include + +#include "oops/util/parameters/NumericConstraints.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + +#include "ufo/profile/DataHandlerParameters.h" + +namespace ufo { + +/// Configuration options recognized by the average profile operator. +class ObsProfileAverageParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsProfileAverageParameters, Parameters) + + public: + /// Operator name. In future will be moved to a base class for parameters of all ObsOperators. + oops::OptionalParameter name{"name", this}; + + /// Number of iterations that are used to find the intersection between + /// the observed profile and each model level. + oops::Parameter numIntersectionIterations + {"numIntersectionIterations", 3, this, {oops::minConstraint(1)}}; + + /// Perform comparisons of auxiliary variables with OPS? + oops::Parameter compareWithOPS{"compareWithOPS", false, this}; +}; + +} // namespace ufo +#endif // UFO_PROFILE_OBSPROFILEAVERAGEPARAMETERS_H_ diff --git a/src/ufo/profile/ProfileAveragePressure.cc b/src/ufo/profile/ProfileAveragePressure.cc new file mode 100644 index 000000000..bab9e8dad --- /dev/null +++ b/src/ufo/profile/ProfileAveragePressure.cc @@ -0,0 +1,232 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include +#include + +#include "ufo/profile/ProfileAveragePressure.h" + +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/ProfileDataHolder.h" + +#include "ufo/utils/metoffice/MetOfficeObservationIDs.h" + +namespace ufo { + + static ProfileCheckMaker + makerProfileAveragePressure_("AveragePressure"); + + ProfileAveragePressure::ProfileAveragePressure + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) + {} + + void ProfileAveragePressure::logPressure(const std::vector &pressures, + std::vector &logP) + { + logP = pressures; + std::transform(logP.begin(), logP.end(), logP.begin(), + [this](float PLev){return PLev > 0 ? std::log(PLev) : missingValueFloat;}); + } + + void ProfileAveragePressure::ExnerPressure(const std::vector &pressures, + std::vector &ExnerP) + { + ExnerP = pressures; + std::transform(ExnerP.begin(), ExnerP.end(), ExnerP.begin(), + [this](float PLev){return PLev > 0 ? + std::pow(PLev / ufo::Constants::pref, ufo::Constants::rd_over_cp) : + missingValueFloat;}); + } + + void ProfileAveragePressure::bigPressureGaps(const std::vector &pressures, + const int ObsType, + std::vector &bigPgaps) + { + bigPgaps = pressures; + const float GapSize = std::log(10.0f); + const float GapFactor = ObsType == ufo::MetOfficeObsIDs::AtmosphericProfile::WindProf ? + options_.AvgP_WinProGapFactor.value() * GapSize : + options_.AvgP_SondeGapFactor.value() * GapSize; + const float GapLogPDiffMin = options_.AvgP_GapLogPDiffMin.value(); + // Subtracting log(100.0) from PLev effectively converts the pressure to hPa. + std::transform(bigPgaps.begin(), bigPgaps.end(), bigPgaps.begin(), + [this, GapFactor, GapLogPDiffMin](float PLev){return PLev > 0.0 ? + GapFactor / std::max(std::log(PLev) - std::log(100.0f), GapLogPDiffMin) : + missingValueFloat;}); + } + + void ProfileAveragePressure::runCheck(ProfileDataHandler &profileDataHandler) + { + oops::Log::debug() << " Pressure transformation" << std::endl; + + // Produce vector of profiles containing data for the pressure transformation. + std::vector variableNamesFloat = + {ufo::VariableNames::obs_air_pressure, + ufo::VariableNames::LogP_derived, + ufo::VariableNames::bigPgaps_derived, + ufo::VariableNames::modellevels_logP_rho_derived, + ufo::VariableNames::modellevels_logP_derived, + ufo::VariableNames::modellevels_ExnerP_rho_derived, + ufo::VariableNames::modellevels_ExnerP_derived}; + std::vector variableNamesGeoVaLs = + {ufo::VariableNames::geovals_pressure, + ufo::VariableNames::geovals_pressure_rho}; + + if (options_.compareWithOPS.value()) { + variableNamesFloat.insert + (variableNamesFloat.end(), + {"OPS_" + std::string(ufo::VariableNames::modellevels_logP_rho_derived), + "OPS_" + std::string(ufo::VariableNames::modellevels_logP_derived), + "OPS_" + std::string(ufo::VariableNames::modellevels_ExnerP_rho_derived), + "OPS_" + std::string(ufo::VariableNames::modellevels_ExnerP_derived)}); + variableNamesGeoVaLs.insert + (variableNamesGeoVaLs.end(), + {ufo::VariableNames::geovals_logP_rho, + ufo::VariableNames::geovals_logP, + ufo::VariableNames::geovals_ExnerP_rho, + ufo::VariableNames::geovals_ExnerP}); + } + + std::vector profiles = + profileDataHandler.produceProfileVector + ({ufo::VariableNames::qcflags_observation_report, + ufo::VariableNames::ObsType, + ufo::VariableNames::extended_obs_space}, + variableNamesFloat, + {}, + variableNamesGeoVaLs, + {}); + + // Run pressure transformation on each profile in the original ObsSpace, + // saving transformed output to the equivalent extended profile. + const size_t halfnprofs = profileDataHandler.getObsdb().nrecs() / 2; + for (size_t jprof = 0; jprof < halfnprofs; ++jprof) { + oops::Log::debug() << " Profile " << (jprof + 1) << " / " << halfnprofs << std::endl; + auto& profileOriginal = profiles[jprof]; + auto& profileExtended = profiles[jprof + halfnprofs]; + runCheckOnProfiles(profileOriginal, profileExtended); + } + + // Fill validation information if required. + if (options_.compareWithOPS.value()) { + for (size_t jprof = 0; jprof < halfnprofs * 2; ++jprof) + fillValidationData(profiles[jprof]); + } + + // Update data handler with profile information. + oops::Log::debug() << " Updating data handler" << std::endl; + profileDataHandler.updateAllProfiles(profiles); + } + + void ProfileAveragePressure::runCheckOnProfiles(ProfileDataHolder &profileOriginal, + ProfileDataHolder &profileExtended) + { + // Check the two profiles are in the correct section of the ObsSpace. + profileOriginal.checkObsSpaceSection(ufo::ObsSpaceSection::Original); + profileExtended.checkObsSpaceSection(ufo::ObsSpaceSection::Extended); + + const size_t numProfileLevels = profileOriginal.getNumProfileLevels(); + const std::vector &pressures = + profileOriginal.get(ufo::VariableNames::obs_air_pressure); + const std::vector &ObsType = + profileOriginal.get(ufo::VariableNames::ObsType); + const std::vector &ReportFlags = + profileOriginal.get(ufo::VariableNames::qcflags_observation_report); + + if (!oops::allVectorsSameNonZeroSize(pressures, ObsType, ReportFlags)) { + std::stringstream errorMessage; + errorMessage << "At least one vector is the wrong size. " + << "Pressure transformation will not be performed." << std::endl; + errorMessage << "Vector sizes: " + << oops::listOfVectorSizes(pressures, ObsType, ReportFlags) + << std::endl; + throw eckit::BadValue(errorMessage.str(), Here()); + } + + // Initialise vectors on reported levels. + std::vector logP(numProfileLevels, missingValueFloat); // log(pressure) + std::vector bigPgaps(numProfileLevels, missingValueFloat); // Big gaps in pressure + + // Initialise vectors on model rho levels. + const size_t numModelLevels_rho = options_.DHParameters.ModParameters.numModelLevels_rho(); + std::vector geovals_logP_rho(numModelLevels_rho, missingValueFloat); // log(pressure) + std::vector geovals_ExnerP_rho + (numModelLevels_rho, missingValueFloat); // Exner pressure + + // Initialise vectors on model theta levels. + const size_t numModelLevels = options_.DHParameters.ModParameters.numModelLevels(); + std::vector geovals_logP(numModelLevels, missingValueFloat); // log(pressure) + std::vector geovals_ExnerP(numModelLevels, missingValueFloat); // Exner pressure + + // Determine transformed pressures, unless: + // - there are zero or one reported levels in the profile, or + // - certain QC flags have previously been set. + if (numProfileLevels > 1 && + !(ReportFlags[0] & ufo::MetOfficeQCFlags::WholeObReport::FinalRejectReport || + ReportFlags[0] & ufo::MetOfficeQCFlags::WholeObReport::OutOfAreaReport)) { + // Determine log(P) on reported levels. + logPressure(pressures, logP); + + // Determine size of big gaps for each reported level. + bigPressureGaps(pressures, ObsType[0], bigPgaps); + + // Calculate log(P) and Exner pressure for GeoVaLs on rho levels. + const std::vector &geovals_pressure_rho = + profileOriginal.getGeoVaLVector(ufo::VariableNames::geovals_pressure_rho); + logPressure(geovals_pressure_rho, geovals_logP_rho); + ExnerPressure(geovals_pressure_rho, geovals_ExnerP_rho); + + // Calculate log(P) and Exner pressure for GeoVaLs on theta levels. + const std::vector &geovals_pressure = + profileOriginal.getGeoVaLVector(ufo::VariableNames::geovals_pressure); + // Require these GeoVaLs to be present (unlike the case on rho levels). + if (geovals_pressure.empty()) + throw eckit::BadValue("geovals_pressure is empty", Here()); + logPressure(geovals_pressure, geovals_logP); + ExnerPressure(geovals_pressure, geovals_ExnerP); + } + + // Store the transformed pressures on reported levels. + profileOriginal.set(ufo::VariableNames::LogP_derived, std::move(logP)); + profileOriginal.set(ufo::VariableNames::bigPgaps_derived, std::move(bigPgaps)); + + // Store the transformed pressures on model levels. + profileExtended.set(ufo::VariableNames::modellevels_logP_rho_derived, + std::move(geovals_logP_rho)); + profileExtended.set(ufo::VariableNames::modellevels_logP_derived, + std::move(geovals_logP)); + profileExtended.set(ufo::VariableNames::modellevels_ExnerP_rho_derived, + std::move(geovals_ExnerP_rho)); + profileExtended.set(ufo::VariableNames::modellevels_ExnerP_derived, + std::move(geovals_ExnerP)); + } + + void ProfileAveragePressure::fillValidationData(ProfileDataHolder &profile) + { + // Retrieve, then save, the OPS versions of the transformed pressures on model levels. + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_logP_rho_derived), + std::move(profile.getGeoVaLVector + (ufo::VariableNames::geovals_logP_rho))); + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_logP_derived), + std::move(profile.getGeoVaLVector + (ufo::VariableNames::geovals_logP))); + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_ExnerP_rho_derived), + std::move(profile.getGeoVaLVector + (ufo::VariableNames::geovals_ExnerP_rho))); + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_ExnerP_derived), + std::move(profile.getGeoVaLVector + (ufo::VariableNames::geovals_ExnerP))); + } + +} // namespace ufo diff --git a/src/ufo/profile/ProfileAveragePressure.h b/src/ufo/profile/ProfileAveragePressure.h new file mode 100644 index 000000000..403116791 --- /dev/null +++ b/src/ufo/profile/ProfileAveragePressure.h @@ -0,0 +1,77 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILEAVERAGEPRESSURE_H_ +#define UFO_PROFILE_PROFILEAVERAGEPRESSURE_H_ + +#include +#include +#include + +#include "ufo/profile/ProfileCheckBase.h" + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + class ProfileConsistencyCheckParameters; + class ProfileDataHandler; + class ProfileDataHolder; +} + +namespace ufo { + + /// \brief Profile QC: apply various transformations to observed and model pressures. + /// The transformed pressures are used in subsequent profile averaging routines. + class ProfileAveragePressure : public ProfileCheckBase { + public: + explicit ProfileAveragePressure(const ProfileConsistencyCheckParameters &options); + + /// Run check on all profiles. + void runCheck(ProfileDataHandler &profileDataHandler) override; + + /// Fill variables in validator. + void fillValidationData(ProfileDataHolder &profileDataHolder); + + /// Run this check on the entire sample? + bool runOnEntireSample() override {return true;} + + /// List of names of required GeoVaLs. + oops::Variables getGeoVaLNames() override { + return oops::Variables({ufo::VariableNames::geovals_pressure, + ufo::VariableNames::geovals_pressure_rho});} + + /// List of names of GeoVaLs used in check validation. + oops::Variables getValidationGeoVaLNames() override { + return oops::Variables({ufo::VariableNames::geovals_logP, + ufo::VariableNames::geovals_ExnerP, + ufo::VariableNames::geovals_logP_rho, + ufo::VariableNames::geovals_ExnerP_rho});} + + private: // functions + /// Calculate log(pressure). + void logPressure(const std::vector &pressures, + std::vector &logP); + + /// Calculate Exner pressure. + void ExnerPressure(const std::vector &pressures, + std::vector &ExnerP); + + /// Calculate big gap for each pressure. + void bigPressureGaps(const std::vector &pressures, + const int ObsType, + std::vector &bigPgaps); + + /// Run check on a profile in the original ObsSpace and + /// put the averaged data into the corresponding profile in the extended ObsSpace. + void runCheckOnProfiles(ProfileDataHolder &profileOriginal, + ProfileDataHolder &profileExtended); + }; +} // namespace ufo + +#endif // UFO_PROFILE_PROFILEAVERAGEPRESSURE_H_ diff --git a/src/ufo/profile/ProfileAverageRelativeHumidity.cc b/src/ufo/profile/ProfileAverageRelativeHumidity.cc new file mode 100644 index 000000000..da9b450d9 --- /dev/null +++ b/src/ufo/profile/ProfileAverageRelativeHumidity.cc @@ -0,0 +1,271 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include +#include + +#include "ufo/profile/ProfileAverageRelativeHumidity.h" +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/ProfileDataHolder.h" +#include "ufo/profile/ProfileVerticalAveraging.h" + +#include "ufo/utils/metoffice/MetOfficeObservationIDs.h" + +namespace ufo { + + static ProfileCheckMaker + makerProfileAverageRelativeHumidity_("AverageRelativeHumidity"); + + ProfileAverageRelativeHumidity::ProfileAverageRelativeHumidity + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) + {} + + void ProfileAverageRelativeHumidity::runCheck(ProfileDataHandler &profileDataHandler) + { + oops::Log::debug() << " Relative humidity averaging" << std::endl; + + // Produce vector of profiles containing data for the wind speed averaging. + std::vector variableNamesInt = + {ufo::VariableNames::qcflags_relative_humidity, + ufo::VariableNames::counter_NumGapsRH, + ufo::VariableNames::extended_obs_space, + ufo::VariableNames::modellevels_average_relative_humidity_qcflags, + ufo::VariableNames::InstrType}; + std::vector variableNamesFloat = + {ufo::VariableNames::obs_relative_humidity, + ufo::VariableNames::pgebd_relative_humidity, + ufo::VariableNames::LogP_derived, + ufo::VariableNames::bigPgaps_derived, + ufo::VariableNames::modellevels_logP_derived, + ufo::VariableNames::modellevels_logP_rho_derived, + ufo::VariableNames::modellevels_average_air_temperature_derived, + ufo::VariableNames::modellevels_average_relative_humidity_derived}; + std::vector variableNamesGeoVaLs = + {ufo::VariableNames::geovals_relative_humidity}; + + if (options_.compareWithOPS.value()) { + variableNamesInt.insert + (variableNamesInt.end(), + {"OPS_" + + std::string(ufo::VariableNames::modellevels_average_relative_humidity_qcflags)}); + variableNamesFloat.insert + (variableNamesFloat.end(), + {"OPS_" + + std::string(ufo::VariableNames::modellevels_average_relative_humidity_derived)}); + variableNamesGeoVaLs.insert + (variableNamesGeoVaLs.end(), + {ufo::VariableNames::geovals_average_relative_humidity, + ufo::VariableNames::geovals_average_relative_humidity_qcflags}); + } + + std::vector profiles = + profileDataHandler.produceProfileVector + (variableNamesInt, + variableNamesFloat, + {}, + variableNamesGeoVaLs, + {}); + + // Run relative humidity averaging on each profile in the original ObsSpace, + // saving averaged output to the equivalent extended profile. + const size_t halfnprofs = profileDataHandler.getObsdb().nrecs() / 2; + for (size_t jprof = 0; jprof < halfnprofs; ++jprof) { + oops::Log::debug() << " Profile " << (jprof + 1) << " / " << halfnprofs << std::endl; + auto& profileOriginal = profiles[jprof]; + auto& profileExtended = profiles[jprof + halfnprofs]; + runCheckOnProfiles(profileOriginal, profileExtended); + } + + // Fill validation information if required. + if (options_.compareWithOPS.value()) { + oops::Log::debug() << " Filling validation data" << std::endl; + for (size_t jprof = 0; jprof < halfnprofs * 2; ++jprof) { + fillValidationData(profiles[jprof]); + } + } + + // Update data handler with profile information. + oops::Log::debug() << " Updating data handler" << std::endl; + profileDataHandler.updateAllProfiles(profiles); + } + + void ProfileAverageRelativeHumidity::runCheckOnProfiles(ProfileDataHolder &profileOriginal, + ProfileDataHolder &profileExtended) + { + // Check the two profiles are in the correct section of the ObsSpace. + profileOriginal.checkObsSpaceSection(ufo::ObsSpaceSection::Original); + profileExtended.checkObsSpaceSection(ufo::ObsSpaceSection::Extended); + + const size_t numProfileLevels = profileOriginal.getNumProfileLevels(); + // Do not perform averaging if there is just one reported level. + if (numProfileLevels <= 1) + return; + + const std::vector &rhObs = + profileOriginal.get(ufo::VariableNames::obs_relative_humidity); + std::vector &rhPGEBd = + profileOriginal.get(ufo::VariableNames::pgebd_relative_humidity); + std::vector &rhFlags = + profileOriginal.get(ufo::VariableNames::qcflags_relative_humidity); + // Optional: vector of sonde instrument types. + const std::vector &InstrType = + profileOriginal.get(ufo::VariableNames::InstrType); + std::vector &NumGapsRH = + profileOriginal.get(ufo::VariableNames::counter_NumGapsRH); + + if (!oops::allVectorsSameNonZeroSize(rhObs, rhPGEBd, rhFlags)) { + oops::Log::warning() << "At least one vector is the wrong size. " + << "Relative humidity averaging will not be performed." << std::endl; + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(rhObs, + rhPGEBd, + rhFlags) + << std::endl; + // todo(ctgh): Revisit this (and other routines in which a similar choice has been made) + // when the organisation of the input data becomes clearer. + return; + } + + // Obtain GeoVaLs. + std::vector &geovals_relative_humidity = + profileOriginal.getGeoVaLVector(ufo::VariableNames::geovals_relative_humidity); + if (geovals_relative_humidity.empty()) + throw eckit::BadValue("GeoVaLs vector is empty.", Here()); + + // Obtain vectors that were produced in the AveragePressure routine. + const std::vector &LogPA = + profileExtended.get(ufo::VariableNames::modellevels_logP_rho_derived); + const std::vector &LogPB = + profileExtended.get(ufo::VariableNames::modellevels_logP_derived); + const std::vector &RepLogP = + profileOriginal.get(ufo::VariableNames::LogP_derived); + const std::vector &BigGap = + profileOriginal.get(ufo::VariableNames::bigPgaps_derived); + if (LogPA.empty() || LogPB.empty() || + !oops::allVectorsSameNonZeroSize(RepLogP, BigGap)) { + std::stringstream errorMessage; + errorMessage << "At least one model-level vector is the wrong size. " + << "Ensure that the AveragePressure routine has been run before this one." + << std::endl; + errorMessage << "Vector sizes: " + << oops::listOfVectorSizes(LogPA, LogPB, + RepLogP, BigGap) + << std::endl; + throw eckit::BadValue(errorMessage.str(), Here()); + } + + // Flag reported value if the probability of gross error is too large. + // Values which have been flagged here, or previously, are not used in the averaging routines. + for (size_t jlev = 0; jlev < numProfileLevels; ++jlev) + if (rhPGEBd[jlev] > options_.AvgRH_PGEskip.value()) + rhFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + + // Interpolate Sonde RH onto model levels? The alternative is to perform a vertical average. + const bool RHinterp = options_.AvgRH_Interp; + + // Select model values of log(pressure) to use depending on whether averaging or + // interpolation is required. + const std::vector &LogPmodel = RHinterp ? LogPB : LogPA; + + // Average observed relative humidities onto model levels. + int NumGaps = 0; // Number of large gaps in reported profile. + std::vector rhModObs; + std::vector rhFlagsModObs; + // Minimum fraction of a model layer that must have been covered (in the vertical coordinate) + // by observed values in order for averaging onto that layer to be performed. + const float SondeDZFraction = options_.AvgRH_SondeDZFraction.value(); + calculateVerticalAverage(rhFlags, + rhObs, + RepLogP, + BigGap, + LogPmodel, + SondeDZFraction, + RHinterp ? + ProfileAveraging::Method::Interpolation : + ProfileAveraging::Method::Averaging, + rhFlagsModObs, + rhModObs, + NumGaps); + + // Increment relative humidity gap counter if necessary. + if (NumGaps > 0) NumGapsRH[0]++; + + // If the model values of RH are missing, set the equivalent observed values to missing. + for (size_t mlev = 0; mlev < geovals_relative_humidity.size(); ++mlev) + if (geovals_relative_humidity[mlev] == missingValueFloat) + rhModObs[mlev] = missingValueFloat; + + // If the temperature averaging was previously performed, + // potentially reject further relative humidity observations. + const std::vector &tModObs = + profileExtended.get + (ufo::VariableNames::modellevels_average_air_temperature_derived); + if (!tModObs.empty()) { + // Reject the average relative humidity if the equivalent averaged temperature + // is less than a particular threshold. + // There is a default value for the temperature threshold which can be overridden + // by instrument-dependent values. + float SondeRHminT = options_.AvgRH_AvgTThreshold.value(); + // Overwrite the value if the instrument is a particular type. + if (!InstrType.empty()) { + const int profileInstr = InstrType[0]; + const auto &InstrTThresholds = options_.AvgRH_InstrTThresholds.value(); + if (InstrTThresholds.find(profileInstr) != InstrTThresholds.end()) + SondeRHminT = InstrTThresholds.at(profileInstr); + } + + // Reject sonde RH? + bool RejectRH = false; + for (int mlev = 0; mlev < tModObs.size(); ++mlev) { + // Once RejectRH is true for one level it is true for the + // remaining levels in the profile. This is the OPS behaviour. + if (tModObs[mlev] != missingValueFloat && + tModObs[mlev] <= ufo::Constants::t0c + SondeRHminT) + RejectRH = true; + if (RejectRH) + rhFlagsModObs[mlev] |= ufo::MetOfficeQCFlags::Elem::PermRejectFlag; + } + } + + // Store the relative humidity averaged onto model levels. + profileExtended.set + (ufo::VariableNames::modellevels_average_relative_humidity_derived, std::move(rhModObs)); + + // Store the QC flags associated with the relative humidity averaging. + profileExtended.set + (ufo::VariableNames::modellevels_average_relative_humidity_qcflags, std::move(rhFlagsModObs)); + } + + void ProfileAverageRelativeHumidity::fillValidationData(ProfileDataHolder &profile) + { + // Retrieve, then save, the OPS versions of relative humidity averaged onto model levels, + // and the QC flags associated with the averaging process. + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_average_relative_humidity_derived), + std::move(profile.getGeoVaLVector + (ufo::VariableNames::geovals_average_relative_humidity))); + + // The QC flags are stored as floats but are converted to integers here. + // Due to the loss of precision, 5 must be added to the missing value. + const std::vector & average_relative_humidity_qcflags_float = + profile.getGeoVaLVector + (ufo::VariableNames::geovals_average_relative_humidity_qcflags); + std::vector average_relative_humidity_qcflags_int + (average_relative_humidity_qcflags_float.begin(), + average_relative_humidity_qcflags_float.end()); + std::replace(average_relative_humidity_qcflags_int.begin(), + average_relative_humidity_qcflags_int.end(), + -2147483648L, + -2147483643L); + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_average_relative_humidity_qcflags), + std::move(average_relative_humidity_qcflags_int)); + } +} // namespace ufo diff --git a/src/ufo/profile/ProfileAverageRelativeHumidity.h b/src/ufo/profile/ProfileAverageRelativeHumidity.h new file mode 100644 index 000000000..3b12a46cc --- /dev/null +++ b/src/ufo/profile/ProfileAverageRelativeHumidity.h @@ -0,0 +1,76 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILEAVERAGERELATIVEHUMIDITY_H_ +#define UFO_PROFILE_PROFILEAVERAGERELATIVEHUMIDITY_H_ + +#include +#include +#include + +#include "ufo/profile/ProfileCheckBase.h" +#include "ufo/profile/ProfileCheckValidator.h" +#include "ufo/profile/ProfileDataHandler.h" + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + class ProfileConsistencyCheckParameters; +} + +namespace ufo { + + /// \brief Profile QC: average relative humidity observations onto model levels. + /// + /// Vectors produced by the AveragePressure routine must be present + /// otherwise the exception eckit::BadValue will be thrown. + /// + /// By default, relative humidities are interpolated onto model layer boundaries rather than + /// averaged across layers in order to avoid unwanted smoothing. + /// This behaviour can be controlled with the \p AvgRH_Interp option. + /// + /// The interpolated/averaged relative humidity values are rejected at any layer where + /// the averaged temperature value is less than or equal to the threshold \p AvgRH_AvgTThreshold. + /// This threshold can be modified to an instrument-dependent value with the + /// parameter \p AvgRH_InstrTThresholds, which is a map between WMO sonde instrument codes + /// and the associated temperature thresholds. + class ProfileAverageRelativeHumidity : public ProfileCheckBase { + public: + explicit ProfileAverageRelativeHumidity(const ProfileConsistencyCheckParameters &options); + + /// Average relative humidity observations onto model levels and store the results. + /// \throws eckit::BadValue if vectors produced by the AveragePressure routine + /// are not present. + void runCheck(ProfileDataHandler &profileDataHandler) override; + + /// Fill variables in validator (for comparison with OPS output). + void fillValidationData(ProfileDataHolder &profileDataHolder); + + /// Run this check on the entire sample? + bool runOnEntireSample() override {return true;} + + /// List of names of required GeoVaLs. + oops::Variables getGeoVaLNames() override { + return oops::Variables({ufo::VariableNames::geovals_relative_humidity});} + + /// List of names of GeoVaLs used in check validation. + oops::Variables getValidationGeoVaLNames() override { + return oops::Variables({ufo::VariableNames::geovals_average_relative_humidity, + ufo::VariableNames::geovals_average_relative_humidity_qcflags + });} + + private: + /// Run check on a profile in the original ObsSpace and + /// put the averaged data into the corresponding profile in the extended ObsSpace. + void runCheckOnProfiles(ProfileDataHolder &profileOriginal, + ProfileDataHolder &profileExtended); + }; +} // namespace ufo + +#endif // UFO_PROFILE_PROFILEAVERAGERELATIVEHUMIDITY_H_ diff --git a/src/ufo/profile/ProfileAverageTemperature.cc b/src/ufo/profile/ProfileAverageTemperature.cc new file mode 100644 index 000000000..a551d066a --- /dev/null +++ b/src/ufo/profile/ProfileAverageTemperature.cc @@ -0,0 +1,354 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include +#include + +#include "ufo/profile/ProfileAverageTemperature.h" +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/ProfileDataHolder.h" +#include "ufo/profile/ProfileVerticalAveraging.h" + +#include "ufo/utils/metoffice/MetOfficeObservationIDs.h" + +namespace ufo { + + static ProfileCheckMaker + makerProfileAverageTemperature_("AverageTemperature"); + + ProfileAverageTemperature::ProfileAverageTemperature + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) + {} + + void ProfileAverageTemperature::runCheck(ProfileDataHandler &profileDataHandler) + { + oops::Log::debug() << " Temperature averaging" << std::endl; + + // Produce vector of profiles containing data for the temperature averaging. + std::vector variableNamesInt = + {ufo::VariableNames::qcflags_air_temperature, + ufo::VariableNames::qcflags_relative_humidity, + ufo::VariableNames::counter_NumGapsT, + ufo::VariableNames::extended_obs_space, + ufo::VariableNames::modellevels_average_air_temperature_qcflags}; + std::vector variableNamesFloat = + {ufo::VariableNames::obs_air_temperature, + ufo::VariableNames::obscorrection_air_temperature, + ufo::VariableNames::pgebd_air_temperature, + ufo::VariableNames::LogP_derived, + ufo::VariableNames::bigPgaps_derived, + ufo::VariableNames::modellevels_ExnerP_rho_derived, + ufo::VariableNames::modellevels_ExnerP_derived, + ufo::VariableNames::modellevels_logP_rho_derived, + ufo::VariableNames::modellevels_logP_derived, + ufo::VariableNames::modellevels_air_temperature_derived, + ufo::VariableNames::modellevels_average_air_temperature_derived}; + std::vector variableNamesGeoVaLs = + {ufo::VariableNames::geovals_potential_temperature}; + + if (options_.compareWithOPS.value()) { + variableNamesInt.insert + (variableNamesInt.end(), + {"OPS_" + std::string(ufo::VariableNames::modellevels_average_air_temperature_qcflags)}); + variableNamesFloat.insert + (variableNamesFloat.end(), + {"OPS_" + std::string(ufo::VariableNames::modellevels_air_temperature_derived), + "OPS_" + + std::string(ufo::VariableNames::modellevels_average_air_temperature_derived)}); + variableNamesGeoVaLs.insert + (variableNamesGeoVaLs.end(), + {ufo::VariableNames::geovals_air_temperature, + ufo::VariableNames::geovals_average_air_temperature, + ufo::VariableNames::geovals_average_air_temperature_qcflags}); + } + + std::vector profiles = + profileDataHandler.produceProfileVector + (variableNamesInt, + variableNamesFloat, + {}, + variableNamesGeoVaLs, + {}); + + // Run temperature averaging on each profile in the original ObsSpace, + // saving averaged output to the equivalent extended profile. + const size_t halfnprofs = profileDataHandler.getObsdb().nrecs() / 2; + for (size_t jprof = 0; jprof < halfnprofs; ++jprof) { + oops::Log::debug() << " Profile " << (jprof + 1) << " / " << halfnprofs << std::endl; + auto& profileOriginal = profiles[jprof]; + auto& profileExtended = profiles[jprof + halfnprofs]; + runCheckOnProfiles(profileOriginal, profileExtended); + } + + // Fill validation information if required. + if (options_.compareWithOPS.value()) { + oops::Log::debug() << " Filling validation data" << std::endl; + for (size_t jprof = 0; jprof < halfnprofs * 2; ++jprof) { + fillValidationData(profiles[jprof]); + } + } + + // Update data handler with profile information. + oops::Log::debug() << " Updating data handler" << std::endl; + profileDataHandler.updateAllProfiles(profiles); + } + + void ProfileAverageTemperature::runCheckOnProfiles(ProfileDataHolder &profileOriginal, + ProfileDataHolder &profileExtended) + { + // Check the two profiles are in the correct section of the ObsSpace. + profileOriginal.checkObsSpaceSection(ufo::ObsSpaceSection::Original); + profileExtended.checkObsSpaceSection(ufo::ObsSpaceSection::Extended); + + const size_t numProfileLevels = profileOriginal.getNumProfileLevels(); + // Do not perform averaging if there is just one reported level. + if (numProfileLevels <= 1) + return; + + const std::vector &tObs = + profileOriginal.get(ufo::VariableNames::obs_air_temperature); + const std::vector &tPGEBd = + profileOriginal.get(ufo::VariableNames::pgebd_air_temperature); + std::vector &tFlags = + profileOriginal.get(ufo::VariableNames::qcflags_air_temperature); + std::vector &rhFlags = + profileOriginal.get(ufo::VariableNames::qcflags_relative_humidity); + const std::vector &tObsCorrection = + profileOriginal.get(ufo::VariableNames::obscorrection_air_temperature); + std::vector &NumGapsT = + profileOriginal.get(ufo::VariableNames::counter_NumGapsT); + + if (!oops::allVectorsSameNonZeroSize(tObs, tPGEBd, tFlags, rhFlags, tObsCorrection)) { + oops::Log::warning() << "At least one vector is the wrong size. " + << "Temperature averaging will not be performed." << std::endl; + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(tObs, tPGEBd, tFlags, rhFlags, tObsCorrection) + << std::endl; + // Do not throw an exception here because some sondes do not have temperature measurements. + // All model-level variables are mandatory and an exception will be thrown if they are + // not present. + // todo(ctgh): Revisit this (and other routines in which a similar choice has been made) + // when the organisation of the input data becomes clearer. + return; + } + + // Obtain GeoVaLs potential temperature. + const std::vector &potempGeoVaLs = + profileOriginal.getGeoVaLVector(ufo::VariableNames::geovals_potential_temperature); + if (potempGeoVaLs.empty()) + throw eckit::BadValue("Potential temperature GeoVaLs vector is empty.", Here()); + const size_t numModelLevels = potempGeoVaLs.size(); + + // Obtain vectors that were produced in the AveragePressure routine. + const std::vector &ExnerPA = + profileExtended.get(ufo::VariableNames::modellevels_ExnerP_rho_derived); + const std::vector &ExnerPB = + profileExtended.get(ufo::VariableNames::modellevels_ExnerP_derived); + const std::vector &LogPA = + profileExtended.get(ufo::VariableNames::modellevels_logP_rho_derived); + const std::vector &LogPB = + profileExtended.get(ufo::VariableNames::modellevels_logP_derived); + const std::vector &RepLogP = + profileOriginal.get(ufo::VariableNames::LogP_derived); + const std::vector &BigGap = + profileOriginal.get(ufo::VariableNames::bigPgaps_derived); + + if (!oops::allVectorsSameNonZeroSize(ExnerPA, LogPA) || + !oops::allVectorsSameNonZeroSize(ExnerPB, LogPB) || + !oops::allVectorsSameNonZeroSize(RepLogP, BigGap)) { + std::stringstream errorMessage; + errorMessage << "At least one model-level vector is the wrong size. " + << "Ensure that the AveragePressure routine has been run before this one." + << std::endl; + errorMessage << "Vector sizes: " + << oops::listOfVectorSizes(ExnerPA, LogPA, + ExnerPB, LogPB, + RepLogP, BigGap) + << std::endl; + throw eckit::BadValue(errorMessage.str(), Here()); + } + + // Apply any corrections to observed temperature. + std::vector tObsFinal; + correctVector(tObs, tObsCorrection, tObsFinal); + + // Flag reported value if the probability of gross error is too large. + // Values which have been flagged here, or previously, are not used in the averaging routines. + for (size_t jlev = 0; jlev < numProfileLevels; ++jlev) { + if (tPGEBd[jlev] > options_.AvgT_PGEskip.value()) { + tFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + // NB the relative humidity flags are modified in this routine and also + // in the routine that performs RH averaging. + rhFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + } + } + + // Average observed temperatures onto model levels. + int NumGaps = 0; // Number of large gaps in reported profile. + std::vector tModObs; // T observations averaged onto model levels. + std::vector tFlagsModObs; // Flags associated with the averaging procedure. + std::vector LogP_Min; // Min log(pressure) used in layer average. + std::vector LogP_Max; // Max log(pressure) used in layer average. + // Minimum fraction of a model layer that must have been covered (in the vertical coordinate) + // by observed values in order for averaging onto that layer to be performed. + const float SondeDZFraction = options_.AvgT_SondeDZFraction.value(); + calculateVerticalAverage(tFlags, + tObsFinal, + RepLogP, + BigGap, + LogPA, + SondeDZFraction, + ProfileAveraging::Method::Averaging, + tFlagsModObs, + tModObs, + NumGaps, + &LogP_Max, + &LogP_Min); + + // Increment temperature gap counter if necessary. + if (NumGaps > 0) NumGapsT[0]++; + + // Convert model potential temperature to temperature. + // The model temperature is used in the partial layer corrections below + // and in subsequent checks on model levels. + std::vector tModBkg = potempGeoVaLs; + std::transform(ExnerPB.begin(), ExnerPB.end(), tModBkg.begin(), tModBkg.begin(), + [this](float ExnerPBLev, float potempLev) + {return potempLev != missingValueFloat && ExnerPBLev != missingValueFloat ? + potempLev * ExnerPBLev : missingValueFloat;}); + + // Recalculate average temperature by taking the thickness of the model layers into account. + // This procedure uses the values of LogP_Max and LogP_Min that were computed in the + // calculateVerticalAverage routine. + // This procedure computes a potential temperature O-B increment using linear interpolation + // of temperature between the layer boundaries. + // This increment is added to the background value to produce the averaged observation value. + const double logPref = std::log(ufo::Constants::pref); + for (int JLev = 0; JLev < numModelLevels; ++JLev) { + if (tModObs[JLev] == missingValueFloat || + LogP_Max[JLev] == missingValueFloat || + LogP_Min[JLev] == missingValueFloat || + LogP_Max[JLev] == LogP_Min[JLev]) + continue; + // Difference between between the maximum and minimum values of log(pressure) + // that were obtained when performing the temperature averaging for this layer. + const double DLogP = LogP_Max[JLev] - LogP_Min[JLev]; + if (JLev < numModelLevels - 1) { // The current level is below the highest model level. + // Check whether this model layer is less than 99.5% full, in which case partial layer + // processing is used. + if (DLogP < 0.995 * (LogPA[JLev] - LogPA[JLev + 1])) { + // Lower model level used in the processing. + const int MLev1 = std::max(JLev - 1, 0); + // Upper model level used in the processing. + const int MLev2 = std::min(JLev + 1, static_cast(numModelLevels) - 1); + // If any of the required quantities are missing, + // set the averaged temperature to the missing value. + if (tModBkg[MLev1] == missingValueFloat || + tModBkg[MLev2] == missingValueFloat || + tModBkg[JLev] == missingValueFloat || + LogPB[MLev2] == missingValueFloat || + LogPA[JLev] == missingValueFloat || + LogPA[JLev + 1] == missingValueFloat) { + tModObs[JLev] = missingValueFloat; + } else { + // DExner is guaranteed to be nonzero thanks to the requirement + // that LogP_Max is not equal to LogP_Min. + const double DExner = // Difference between Exner pressures. + std::exp((LogP_Max[JLev] - logPref) * ufo::Constants::rd_over_cp) - + std::exp((LogP_Min[JLev] - logPref) * ufo::Constants::rd_over_cp); + // Compute potential temperature. + const double potemp = ufo::Constants::rd_over_cp * tModObs[JLev] * DLogP / DExner; + // Model temperature at level JLev. + const double TLev = + potempGeoVaLs[JLev] * (ExnerPA[JLev] - ExnerPA[JLev + 1]) / + (ufo::Constants::rd_over_cp * (LogPA[JLev] - LogPA[JLev + 1])); + // Log(P) at model layer midpoint in terms of Log(P). + const double ModLogP_mid = 0.5 * (LogPA[JLev] + LogPA[JLev + 1]); + // Model temperature gradient. + const double TGrad = (tModBkg[MLev1] - tModBkg[MLev2]) / (LogPB[MLev1] - LogPB[MLev2]); + // Model temperature at level P_Max, computed using midpoint and gradient. + const double TMax = TLev + (LogP_Min[JLev] - ModLogP_mid) * TGrad; + // Model temperature at level P_Min, computed using midpoint and gradient. + const double TMin = TLev + (LogP_Max[JLev] - ModLogP_mid) * TGrad; + // Model potential temperature for P_Max to P_Min. + const double potempBk = + ufo::Constants::rd_over_cp * (TMax + TMin) * DLogP / (2.0 * DExner); + // Temperature increment for partial layer. + const double Tinc = (potemp - potempBk) * ExnerPB[JLev]; + // Update averaged temperature with increment. + tModObs[JLev] = tModBkg[JLev] + Tinc; + } + } else { + // This model layer has been fully covered. + // Determine difference between Exner pressures using model values. + const double DExner = ExnerPA[JLev] - ExnerPA[JLev + 1]; + // Compute potential temperature. + const double potemp = + ufo::Constants::rd_over_cp * tModObs[JLev] * (LogPA[JLev] - LogPA[JLev + 1]) / DExner; + // Convert potential temperature back to temperature. + tModObs[JLev] = potemp * ExnerPB[JLev]; + } + } else { + // Highest level to be processed. + const double DExner = + std::exp((LogP_Max[JLev] - logPref) * ufo::Constants::rd_over_cp) - + std::exp((LogP_Min[JLev] - logPref) * ufo::Constants::rd_over_cp); + // Compute potential temperature. + const double potemp = ufo::Constants::rd_over_cp * tModObs[JLev] * DLogP / DExner; + // Convert potential temperature back to temperature. + tModObs[JLev] = potemp * ExnerPB[JLev]; + } + } + + // Store the model temperature. + profileExtended.set + (ufo::VariableNames::modellevels_air_temperature_derived, std::move(tModBkg)); + + // Store the temperature averaged onto model levels. + profileExtended.set + (ufo::VariableNames::modellevels_average_air_temperature_derived, std::move(tModObs)); + + // Store the QC flags associated with temperature averaging. + profileExtended.set + (ufo::VariableNames::modellevels_average_air_temperature_qcflags, std::move(tFlagsModObs)); + } + + void ProfileAverageTemperature::fillValidationData(ProfileDataHolder &profile) + { + // Retrieve, then save, the OPS versions of model temperature, + // temperature averaged onto model levels, + // and the QC flags associated with the averaging process. + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_air_temperature_derived), + std::move(profile.getGeoVaLVector + (ufo::VariableNames::geovals_air_temperature))); + + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_average_air_temperature_derived), + std::move(profile.getGeoVaLVector + (ufo::VariableNames::geovals_average_air_temperature))); + + // The QC flags are stored as floats but are converted to integers here. + // Due to the loss of precision, 5 must be added to the missing value. + const std::vector & average_air_temperature_qcflags_float = + profile.getGeoVaLVector + (ufo::VariableNames::geovals_average_air_temperature_qcflags); + std::vector average_air_temperature_qcflags_int + (average_air_temperature_qcflags_float.begin(), + average_air_temperature_qcflags_float.end()); + std::replace(average_air_temperature_qcflags_int.begin(), + average_air_temperature_qcflags_int.end(), + -2147483648L, + -2147483643L); + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_average_air_temperature_qcflags), + std::move(average_air_temperature_qcflags_int)); + } +} // namespace ufo diff --git a/src/ufo/profile/ProfileAverageTemperature.h b/src/ufo/profile/ProfileAverageTemperature.h new file mode 100644 index 000000000..3728415f5 --- /dev/null +++ b/src/ufo/profile/ProfileAverageTemperature.h @@ -0,0 +1,78 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILEAVERAGETEMPERATURE_H_ +#define UFO_PROFILE_PROFILEAVERAGETEMPERATURE_H_ + +#include +#include +#include + +#include "ufo/profile/ProfileCheckBase.h" +#include "ufo/profile/ProfileCheckValidator.h" +#include "ufo/profile/ProfileDataHandler.h" + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + class ProfileConsistencyCheckParameters; +} + +namespace ufo { + + /// \brief Profile QC: average temperature observations onto model levels. + /// + /// Vectors produced by the AveragePressure routine must be present + /// otherwise the exception eckit::BadValue will be thrown. + /// + /// The vertical processing of temperature is based on calculating the thickness + /// of the model layers (rather than just averaging the temperatures). + /// The potential temperature in each layer is converted to temperature + /// by multiplying by the Exner pressure. + /// + /// When the model layer is not completely covered by observations, + /// a potential temperature observation-minus-background increment is computed + /// using linear interpolation of temperature between the layer boundaries. + /// This increment is added to the background value to produce + /// the averaged observation value. + class ProfileAverageTemperature : public ProfileCheckBase { + public: + explicit ProfileAverageTemperature(const ProfileConsistencyCheckParameters &options); + + /// Average temperature observations onto model levels and store the results. + /// \throws eckit::BadValue if vectors produced by the AveragePressure routine + /// are not present. + void runCheck(ProfileDataHandler &profileDataHandler) override; + + /// Fill variables in validator (for comparison with OPS output). + void fillValidationData(ProfileDataHolder &profileDataHolder); + + /// Run this check on the entire sample? + bool runOnEntireSample() override {return true;} + + /// List of names of required GeoVaLs. + oops::Variables getGeoVaLNames() override { + return oops::Variables({ufo::VariableNames::geovals_potential_temperature});} + + /// List of names of GeoVaLs used in check validation. + oops::Variables getValidationGeoVaLNames() override { + return oops::Variables({ufo::VariableNames::geovals_air_temperature, + ufo::VariableNames::geovals_average_air_temperature, + ufo::VariableNames::geovals_average_air_temperature_qcflags + });} + + private: + /// Run check on a profile in the original ObsSpace and + /// put the averaged data into the corresponding profile in the extended ObsSpace. + void runCheckOnProfiles(ProfileDataHolder &profileOriginal, + ProfileDataHolder &profileExtended); + }; +} // namespace ufo + +#endif // UFO_PROFILE_PROFILEAVERAGETEMPERATURE_H_ diff --git a/src/ufo/profile/ProfileAverageWindSpeed.cc b/src/ufo/profile/ProfileAverageWindSpeed.cc new file mode 100644 index 000000000..4b7ce3818 --- /dev/null +++ b/src/ufo/profile/ProfileAverageWindSpeed.cc @@ -0,0 +1,295 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include +#include + +#include "ufo/profile/ProfileAverageWindSpeed.h" +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/ProfileDataHolder.h" +#include "ufo/profile/ProfileVerticalAveraging.h" + +#include "ufo/utils/metoffice/MetOfficeObservationIDs.h" + +namespace ufo { + + static ProfileCheckMaker + makerProfileAverageWindSpeed_("AverageWindSpeed"); + + ProfileAverageWindSpeed::ProfileAverageWindSpeed + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) + {} + + void ProfileAverageWindSpeed::runCheck(ProfileDataHandler &profileDataHandler) + { + oops::Log::debug() << " Wind speed averaging" << std::endl; + + // Produce vector of profiles containing data for the wind speed averaging. + std::vector variableNamesInt = + {ufo::VariableNames::qcflags_eastward_wind, + ufo::VariableNames::qcflags_northward_wind, + ufo::VariableNames::counter_NumGapsU, + ufo::VariableNames::counter_NumGapsUWP, + ufo::VariableNames::ObsType, + ufo::VariableNames::extended_obs_space, + ufo::VariableNames::modellevels_average_eastward_wind_qcflags, + ufo::VariableNames::modellevels_average_northward_wind_qcflags}; + std::vector variableNamesFloat = + {ufo::VariableNames::obs_eastward_wind, + ufo::VariableNames::obs_northward_wind, + ufo::VariableNames::pgebd_eastward_wind, + ufo::VariableNames::LogP_derived, + ufo::VariableNames::bigPgaps_derived, + ufo::VariableNames::modellevels_logP_derived, + ufo::VariableNames::modellevels_average_eastward_wind_derived, + ufo::VariableNames::modellevels_average_northward_wind_derived}; + std::vector variableNamesGeoVaLs = + {ufo::VariableNames::geovals_surface_pressure}; + + if (options_.compareWithOPS.value()) { + variableNamesInt.insert + (variableNamesInt.end(), + {"OPS_" + + std::string(ufo::VariableNames::modellevels_average_eastward_wind_qcflags), + "OPS_" + + std::string(ufo::VariableNames::modellevels_average_northward_wind_qcflags)}); + variableNamesFloat.insert + (variableNamesFloat.end(), + {"OPS_" + + std::string(ufo::VariableNames::modellevels_average_eastward_wind_derived), + "OPS_" + + std::string(ufo::VariableNames::modellevels_average_northward_wind_derived)}); + variableNamesGeoVaLs.insert + (variableNamesGeoVaLs.end(), + {ufo::VariableNames::geovals_average_eastward_wind, + ufo::VariableNames::geovals_average_eastward_wind_qcflags, + ufo::VariableNames::geovals_average_northward_wind, + ufo::VariableNames::geovals_average_northward_wind_qcflags}); + } + + std::vector profiles = + profileDataHandler.produceProfileVector + (variableNamesInt, + variableNamesFloat, + {}, + variableNamesGeoVaLs, + {}); + + // Run wind speed averaging on each profile in the original ObsSpace, + // saving averaged output to the equivalent extended profile. + const size_t halfnprofs = profileDataHandler.getObsdb().nrecs() / 2; + for (size_t jprof = 0; jprof < halfnprofs; ++jprof) { + oops::Log::debug() << " Profile " << (jprof + 1) << " / " << halfnprofs << std::endl; + auto& profileOriginal = profiles[jprof]; + auto& profileExtended = profiles[jprof + halfnprofs]; + runCheckOnProfiles(profileOriginal, profileExtended); + } + + // Fill validation information if required. + if (options_.compareWithOPS.value()) { + oops::Log::debug() << " Filling validation data" << std::endl; + for (size_t jprof = 0; jprof < halfnprofs * 2; ++jprof) { + fillValidationData(profiles[jprof]); + } + } + + // Update data handler with profile information. + oops::Log::debug() << " Updating data handler" << std::endl; + profileDataHandler.updateAllProfiles(profiles); + } + + void ProfileAverageWindSpeed::runCheckOnProfiles(ProfileDataHolder &profileOriginal, + ProfileDataHolder &profileExtended) + { + // Check the two profiles are in the correct section of the ObsSpace. + profileOriginal.checkObsSpaceSection(ufo::ObsSpaceSection::Original); + profileExtended.checkObsSpaceSection(ufo::ObsSpaceSection::Extended); + + const size_t numProfileLevels = profileOriginal.getNumProfileLevels(); + // Do not perform averaging if there is just one reported level. + if (numProfileLevels <= 1) + return; + + const std::vector &uObs = + profileOriginal.get(ufo::VariableNames::obs_eastward_wind); + const std::vector &vObs = + profileOriginal.get(ufo::VariableNames::obs_northward_wind); + const std::vector &uPGEBd = + profileOriginal.get(ufo::VariableNames::pgebd_eastward_wind); + std::vector &uFlags = + profileOriginal.get(ufo::VariableNames::qcflags_eastward_wind); + std::vector &vFlags = + profileOriginal.get(ufo::VariableNames::qcflags_northward_wind); + std::vector &NumGapsU = + profileOriginal.get(ufo::VariableNames::counter_NumGapsU); + // Number of gaps for wind profilers. + std::vector &NumGapsUWP = + profileOriginal.get(ufo::VariableNames::counter_NumGapsUWP); + const std::vector &ObsType = + profileOriginal.get(ufo::VariableNames::ObsType); + + if (!oops::allVectorsSameNonZeroSize(uObs, vObs, + uPGEBd, + uFlags, vFlags, + ObsType)) { + oops::Log::warning() << "At least one vector is the wrong size. " + << "Wind speed averaging will not be performed." << std::endl; + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(uObs, vObs, + uPGEBd, + uFlags, vFlags, + ObsType) + << std::endl; + // todo(ctgh): Revisit this (and other routines in which a similar choice has been made) + // when the organisation of the input data becomes clearer. + return; + } + + // Obtain GeoVaLs surface pressure and eastward wind speed. + std::vector &geovals_surface_pressure = + profileOriginal.getGeoVaLVector(ufo::VariableNames::geovals_surface_pressure); + if (geovals_surface_pressure.empty()) + throw eckit::BadValue("Surface pressure GeoVaLs vector is empty.", Here()); + + // Obtain vectors that were produced in the AveragePressure routine. + const std::vector &LogPB = + profileExtended.get(ufo::VariableNames::modellevels_logP_derived); + const std::vector &RepLogP = + profileOriginal.get(ufo::VariableNames::LogP_derived); + const std::vector &BigGap = + profileOriginal.get(ufo::VariableNames::bigPgaps_derived); + + if (LogPB.empty() || + !oops::allVectorsSameNonZeroSize(RepLogP, BigGap)) { + std::stringstream errorMessage; + errorMessage << "At least one model-level vector is the wrong size. " + << "Ensure that the AveragePressure routine has been run before this one." + << std::endl; + errorMessage << "Vector sizes: " + << oops::listOfVectorSizes(LogPB, + RepLogP, BigGap) + << std::endl; + throw eckit::BadValue(errorMessage.str(), Here()); + } + + // Create concatenated vector of log(pressure) on both surface and upper-air levels + // for use in the wind speed averaging. + std::vector LogPWB = LogPB; + LogPWB.insert(LogPWB.begin(), std::log(geovals_surface_pressure[0])); + + // Flag reported value if the probability of gross error is too large. + // Values which have been flagged here, or previously, are not used in the averaging routines. + for (size_t jlev = 0; jlev < numProfileLevels; ++jlev) { + if (uPGEBd[jlev] > options_.AvgU_PGEskip.value()) { + uFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + vFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + } + } + + // Average observed wind speeds onto model levels. + int NumGaps = 0; // Number of large gaps in reported profile + std::vector uModObs; // u observations averaged onto model levels. + std::vector uFlagsModObs; // Flags associated with the u averaging procedure. + // Minimum fraction of a model layer that must have been covered (in the vertical coordinate) + // by observed values in order for averaging onto that layer to be performed. + const float SondeDZFraction = options_.AvgU_SondeDZFraction.value(); + calculateVerticalAverage(uFlags, + uObs, + RepLogP, + BigGap, + LogPWB, + SondeDZFraction, + ProfileAveraging::Method::Averaging, + uFlagsModObs, + uModObs, + NumGaps); + + // Increment wind speed gap counter if necessary. + if (NumGaps > 0) { + if (ObsType[0] == ufo::MetOfficeObsIDs::AtmosphericProfile::WindProf) + NumGapsUWP[0]++; + else + NumGapsU[0]++; + } + + std::vector vModObs; // v observations averaged onto model levels. + std::vector vFlagsModObs; // Flags associated with the v averaging procedure. + calculateVerticalAverage(vFlags, + vObs, + RepLogP, + BigGap, + LogPWB, + SondeDZFraction, + ProfileAveraging::Method::Averaging, + vFlagsModObs, + vModObs, + NumGaps); + + // Store the eastward wind speed averaged onto model levels. + profileExtended.set + (ufo::VariableNames::modellevels_average_eastward_wind_derived, std::move(uModObs)); + + // Store the QC flags associated with the eastward wind averaging. + profileExtended.set + (ufo::VariableNames::modellevels_average_eastward_wind_qcflags, std::move(uFlagsModObs)); + + // Store the northward wind speed averaged onto model levels. + profileExtended.set + (ufo::VariableNames::modellevels_average_northward_wind_derived, std::move(vModObs)); + + // Store the QC flags associated with the northward wind averaging. + profileExtended.set + (ufo::VariableNames::modellevels_average_northward_wind_qcflags, std::move(vFlagsModObs)); + } + + void ProfileAverageWindSpeed::fillValidationData(ProfileDataHolder &profile) + { + // Retrieve, then save, the OPS versions of + // eastward and northward wind averaged onto model levels, + // and the QC flags associated with the averaging process. + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_average_eastward_wind_derived), + std::move(profile.getGeoVaLVector + (ufo::VariableNames::geovals_average_eastward_wind))); + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_average_northward_wind_derived), + std::move(profile.getGeoVaLVector + (ufo::VariableNames::geovals_average_northward_wind))); + + // The QC flags are stored as floats but are converted to integers here. + // Due to the loss of precision, 5 must be added to the missing value. + const std::vector & average_eastward_wind_qcflags_float = + profile.getGeoVaLVector + (ufo::VariableNames::geovals_average_eastward_wind_qcflags); + std::vector average_eastward_wind_qcflags_int + (average_eastward_wind_qcflags_float.begin(), + average_eastward_wind_qcflags_float.end()); + std::replace(average_eastward_wind_qcflags_int.begin(), + average_eastward_wind_qcflags_int.end(), + -2147483648L, + -2147483643L); + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_average_eastward_wind_qcflags), + std::move(average_eastward_wind_qcflags_int)); + const std::vector & average_northward_wind_qcflags_float = + profile.getGeoVaLVector + (ufo::VariableNames::geovals_average_northward_wind_qcflags); + std::vector average_northward_wind_qcflags_int + (average_northward_wind_qcflags_float.begin(), + average_northward_wind_qcflags_float.end()); + std::replace(average_northward_wind_qcflags_int.begin(), + average_northward_wind_qcflags_int.end(), + -2147483648L, + -2147483643L); + profile.set + ("OPS_" + std::string(ufo::VariableNames::modellevels_average_northward_wind_qcflags), + std::move(average_northward_wind_qcflags_int)); + } +} // namespace ufo diff --git a/src/ufo/profile/ProfileAverageWindSpeed.h b/src/ufo/profile/ProfileAverageWindSpeed.h new file mode 100644 index 000000000..6ca1914bf --- /dev/null +++ b/src/ufo/profile/ProfileAverageWindSpeed.h @@ -0,0 +1,71 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILEAVERAGEWINDSPEED_H_ +#define UFO_PROFILE_PROFILEAVERAGEWINDSPEED_H_ + +#include +#include +#include + +#include "ufo/profile/ProfileCheckBase.h" +#include "ufo/profile/ProfileCheckValidator.h" +#include "ufo/profile/ProfileDataHandler.h" + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + class ProfileConsistencyCheckParameters; +} + +namespace ufo { + + /// \brief Profile QC: average wind speed observations onto model levels. + /// + /// Vectors produced by the AveragePressure routine must be present + /// otherwise the exception eckit::BadValue will be thrown. + /// + /// The eastward and northward wind components are averaged separately + /// over model layers defined by adjacent pressure levels, including the surface pressure. + class ProfileAverageWindSpeed : public ProfileCheckBase { + public: + explicit ProfileAverageWindSpeed(const ProfileConsistencyCheckParameters &options); + + /// Average wind speed observations onto model levels and store the results. + /// \throws eckit::BadValue if vectors produced by the AveragePressure routine + /// are not present. + void runCheck(ProfileDataHandler &profileDataHandler) override; + + /// Fill variables in validator (for comparison with OPS output). + void fillValidationData(ProfileDataHolder &profileDataHolder); + + /// Run this check on the entire sample? + bool runOnEntireSample() override {return true;} + + /// List of names of required GeoVaLs. + oops::Variables getGeoVaLNames() override { + return oops::Variables({ufo::VariableNames::geovals_surface_pressure});} + + /// List of names of GeoVaLs used in check validation. + oops::Variables getValidationGeoVaLNames() override { + return oops::Variables({ufo::VariableNames::geovals_average_eastward_wind, + ufo::VariableNames::geovals_average_eastward_wind_qcflags, + ufo::VariableNames::geovals_average_northward_wind, + ufo::VariableNames::geovals_average_northward_wind_qcflags + });} + + private: + /// Run check on a profile in the original ObsSpace and + /// put the averaged data into the corresponding profile in the extended ObsSpace. + void runCheckOnProfiles(ProfileDataHolder &profileOriginal, + ProfileDataHolder &profileExtended); + }; +} // namespace ufo + +#endif // UFO_PROFILE_PROFILEAVERAGEWINDSPEED_H_ diff --git a/src/ufo/profile/ProfileCheckBackgroundGeopotentialHeight.cc b/src/ufo/profile/ProfileCheckBackgroundGeopotentialHeight.cc index 6af3f5ef9..adc7f2942 100644 --- a/src/ufo/profile/ProfileCheckBackgroundGeopotentialHeight.cc +++ b/src/ufo/profile/ProfileCheckBackgroundGeopotentialHeight.cc @@ -13,43 +13,40 @@ namespace ufo { makerProfileCheckBackgroundGeopotentialHeight_("BackgroundGeopotentialHeight"); ProfileCheckBackgroundGeopotentialHeight::ProfileCheckBackgroundGeopotentialHeight - (const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator) + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) {} - void ProfileCheckBackgroundGeopotentialHeight::runCheck() + void ProfileCheckBackgroundGeopotentialHeight::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Background check for geopotential height" << std::endl; - const size_t numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const size_t numProfileLevels = profileDataHandler.getNumProfileLevels(); const bool ModelLevels = options_.modellevels.value(); const std::vector &Zstation = - profileDataHandler_.get(ufo::VariableNames::Zstation); + profileDataHandler.get(ufo::VariableNames::Zstation); const std::vector &pressures = - profileDataHandler_.get(ufo::VariableNames::obs_air_pressure); + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); const std::vector &zObs = - profileDataHandler_.get(ufo::VariableNames::obs_geopotential_height); + profileDataHandler.get(ufo::VariableNames::obs_geopotential_height); const std::vector &zObsErr = - profileDataHandler_.get(ufo::VariableNames::obserr_geopotential_height); + profileDataHandler.get(ufo::VariableNames::obserr_geopotential_height); const std::vector &zBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_geopotential_height); + profileDataHandler.get(ufo::VariableNames::hofx_geopotential_height); std::vector &zBkgErr = - profileDataHandler_.get(ufo::VariableNames::bkgerr_geopotential_height); + profileDataHandler.getObsDiag(ufo::VariableNames::bkgerr_geopotential_height); std::vector &zPGE = - profileDataHandler_.get(ufo::VariableNames::pge_geopotential_height); + profileDataHandler.get(ufo::VariableNames::pge_geopotential_height); std::vector &zPGEBd = - profileDataHandler_.get(ufo::VariableNames::pgebd_geopotential_height); + profileDataHandler.get(ufo::VariableNames::pgebd_geopotential_height); std::vector &zFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_geopotential_height); + profileDataHandler.get(ufo::VariableNames::qcflags_geopotential_height); const std::vector &tFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_air_temperature); + profileDataHandler.get(ufo::VariableNames::qcflags_air_temperature); const std::vector &zObsCorrection = - profileDataHandler_.get(ufo::VariableNames::obscorrection_geopotential_height); + profileDataHandler.get(ufo::VariableNames::obscorrection_geopotential_height); const std::vector &timeFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_time); + profileDataHandler.get(ufo::VariableNames::qcflags_time); if (!oops::allVectorsSameNonZeroSize(Zstation, pressures, zObs, zObsErr, zBkg, @@ -70,14 +67,14 @@ namespace ufo { correctVector(zObs, zObsCorrection, zObsFinal); // Probability density of 'bad' observations. - std::vector PdBad(numLevelsToCheck, 0); + std::vector PdBad(numProfileLevels, 0); // The z background error may not have been set before this point. if (zBkgErr.empty()) - zBkgErr.assign(numLevelsToCheck, missingValueFloat); + zBkgErr.assign(numProfileLevels, missingValueFloat); // Background error estimates are taken from ECMWF Research Manual 1 // (ECMWF Data Assimilation Scientific Documentation, 3/92, 3rd edition), table 2.1. // They are then multiplied by 1.2. - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if (zObsFinal[jlev] != missingValueFloat) { // Permanently reject any levels at/below surface if (tFlags[jlev] & ufo::MetOfficeQCFlags::Profile::SurfaceLevelFlag || @@ -100,7 +97,7 @@ namespace ufo { } // Modify observation PGE if certain flags have been set. - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if (zFlags[jlev] & ufo::MetOfficeQCFlags::Profile::InterpolationFlag) zPGE[jlev] = 0.5 + 0.5 * zPGE[jlev]; if (zFlags[jlev] & ufo::MetOfficeQCFlags::Profile::HydrostaticFlag) diff --git a/src/ufo/profile/ProfileCheckBackgroundGeopotentialHeight.h b/src/ufo/profile/ProfileCheckBackgroundGeopotentialHeight.h index 8798230d1..571c8e6d2 100644 --- a/src/ufo/profile/ProfileCheckBackgroundGeopotentialHeight.h +++ b/src/ufo/profile/ProfileCheckBackgroundGeopotentialHeight.h @@ -13,9 +13,7 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" #include "ufo/utils/ProbabilityOfGrossError.h" @@ -37,16 +35,16 @@ namespace ufo { /// (except those with PGE > 0.999) will be used in vertical averaging. class ProfileCheckBackgroundGeopotentialHeight : public ProfileCheckBase { public: - ProfileCheckBackgroundGeopotentialHeight(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckBackgroundGeopotentialHeight + (const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; + void runCheck(ProfileDataHandler &profileDataHandler) override; - /// Fill variables in validator - void fillValidator() override {} + /// List of names of required obs diagnostics. + oops::Variables getObsDiagNames() override { + return oops::Variables({ufo::VariableNames::bkgerr_geopotential_height}); + } }; } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckBackgroundRelativeHumidity.cc b/src/ufo/profile/ProfileCheckBackgroundRelativeHumidity.cc index 62a0b41fa..d1f3d0ef6 100644 --- a/src/ufo/profile/ProfileCheckBackgroundRelativeHumidity.cc +++ b/src/ufo/profile/ProfileCheckBackgroundRelativeHumidity.cc @@ -13,35 +13,32 @@ namespace ufo { makerProfileCheckBackgroundRelativeHumidity_("BackgroundRelativeHumidity"); ProfileCheckBackgroundRelativeHumidity::ProfileCheckBackgroundRelativeHumidity - (const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator) + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) {} - void ProfileCheckBackgroundRelativeHumidity::runCheck() + void ProfileCheckBackgroundRelativeHumidity::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Background check for relative humidity" << std::endl; - const size_t numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const size_t numProfileLevels = profileDataHandler.getNumProfileLevels(); const bool ModelLevels = options_.modellevels.value(); const std::vector &rhObs = - profileDataHandler_.get(ufo::VariableNames::obs_relative_humidity); + profileDataHandler.get(ufo::VariableNames::obs_relative_humidity); const std::vector &rhObsErr = - profileDataHandler_.get(ufo::VariableNames::obserr_relative_humidity); + profileDataHandler.get(ufo::VariableNames::obserr_relative_humidity); const std::vector &rhBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_relative_humidity); + profileDataHandler.get(ufo::VariableNames::hofx_relative_humidity); const std::vector &rhBkgErr = - profileDataHandler_.get(ufo::VariableNames::bkgerr_relative_humidity); + profileDataHandler.getObsDiag(ufo::VariableNames::bkgerr_relative_humidity); std::vector &rhPGE = - profileDataHandler_.get(ufo::VariableNames::pge_relative_humidity); + profileDataHandler.get(ufo::VariableNames::pge_relative_humidity); std::vector &rhPGEBd = - profileDataHandler_.get(ufo::VariableNames::pgebd_relative_humidity); + profileDataHandler.get(ufo::VariableNames::pgebd_relative_humidity); std::vector &rhFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_relative_humidity); + profileDataHandler.get(ufo::VariableNames::qcflags_relative_humidity); const std::vector &timeFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_time); + profileDataHandler.get(ufo::VariableNames::qcflags_time); if (!oops::allVectorsSameNonZeroSize(rhObs, rhObsErr, rhBkg, rhBkgErr, rhPGE, rhFlags, timeFlags)) { @@ -55,15 +52,15 @@ namespace ufo { } // Probability density of 'bad' observations. - std::vector PdBad(numLevelsToCheck, options_.BkCheck_PdBad_rh.value()); + std::vector PdBad(numProfileLevels, options_.BkCheck_PdBad_rh.value()); // Local version of relative humidity background error. - std::vector BackgrErrRH(numLevelsToCheck, 0.0); + std::vector BackgrErrRH(numProfileLevels, 0.0); // Local version of relative humidity observation error. - std::vector ObErrRH(numLevelsToCheck, 0.0); + std::vector ObErrRH(numProfileLevels, 0.0); // Relax QC to take account of long-tailed error distributions. const float sqrt2 = std::sqrt(2.0); - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { BackgrErrRH[jlev] = missingValueFloat; ObErrRH[jlev] = missingValueFloat; if (rhBkgErr[jlev] != missingValueFloat) diff --git a/src/ufo/profile/ProfileCheckBackgroundRelativeHumidity.h b/src/ufo/profile/ProfileCheckBackgroundRelativeHumidity.h index 469822599..549d37566 100644 --- a/src/ufo/profile/ProfileCheckBackgroundRelativeHumidity.h +++ b/src/ufo/profile/ProfileCheckBackgroundRelativeHumidity.h @@ -13,9 +13,7 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" #include "ufo/utils/ProbabilityOfGrossError.h" @@ -37,16 +35,16 @@ namespace ufo { /// (except those with PGE > 0.999) will be used in vertical averaging. class ProfileCheckBackgroundRelativeHumidity : public ProfileCheckBase { public: - ProfileCheckBackgroundRelativeHumidity(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckBackgroundRelativeHumidity + (const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; + void runCheck(ProfileDataHandler &profileDataHandler) override; - /// Fill variables in validator - void fillValidator() override {} + /// List of names of required obs diagnostics. + oops::Variables getObsDiagNames() override { + return oops::Variables({ufo::VariableNames::bkgerr_relative_humidity}); + } }; } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckBackgroundTemperature.cc b/src/ufo/profile/ProfileCheckBackgroundTemperature.cc index 43918adb4..a37c8622d 100644 --- a/src/ufo/profile/ProfileCheckBackgroundTemperature.cc +++ b/src/ufo/profile/ProfileCheckBackgroundTemperature.cc @@ -13,41 +13,38 @@ namespace ufo { makerProfileCheckBackgroundTemperature_("BackgroundTemperature"); ProfileCheckBackgroundTemperature::ProfileCheckBackgroundTemperature - (const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator) + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) {} - void ProfileCheckBackgroundTemperature::runCheck() + void ProfileCheckBackgroundTemperature::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Background check for temperature" << std::endl; - const size_t numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const size_t numProfileLevels = profileDataHandler.getNumProfileLevels(); const bool ModelLevels = options_.modellevels.value(); const std::vector &Latitude = - profileDataHandler_.get(ufo::VariableNames::Latitude); + profileDataHandler.get(ufo::VariableNames::Latitude); const std::vector &pressures = - profileDataHandler_.get(ufo::VariableNames::obs_air_pressure); + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); const std::vector &tObs = - profileDataHandler_.get(ufo::VariableNames::obs_air_temperature); + profileDataHandler.get(ufo::VariableNames::obs_air_temperature); const std::vector &tObsErr = - profileDataHandler_.get(ufo::VariableNames::obserr_air_temperature); + profileDataHandler.get(ufo::VariableNames::obserr_air_temperature); const std::vector &tBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_air_temperature); + profileDataHandler.get(ufo::VariableNames::hofx_air_temperature); const std::vector &tBkgErr = - profileDataHandler_.get(ufo::VariableNames::bkgerr_air_temperature); + profileDataHandler.getObsDiag(ufo::VariableNames::bkgerr_air_temperature); std::vector &tPGE = - profileDataHandler_.get(ufo::VariableNames::pge_air_temperature); + profileDataHandler.get(ufo::VariableNames::pge_air_temperature); std::vector &tPGEBd = - profileDataHandler_.get(ufo::VariableNames::pgebd_air_temperature); + profileDataHandler.get(ufo::VariableNames::pgebd_air_temperature); std::vector &tFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_air_temperature); + profileDataHandler.get(ufo::VariableNames::qcflags_air_temperature); const std::vector &timeFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_time); + profileDataHandler.get(ufo::VariableNames::qcflags_time); const std::vector &tObsCorrection = - profileDataHandler_.get(ufo::VariableNames::obscorrection_air_temperature); + profileDataHandler.get(ufo::VariableNames::obscorrection_air_temperature); if (!oops::allVectorsSameNonZeroSize(Latitude, pressures, tObs, tObsErr, tBkg, tBkgErr, @@ -66,10 +63,10 @@ namespace ufo { correctVector(tObs, tObsCorrection, tObsFinal); // Probability density of 'bad' observations. - std::vector PdBad(numLevelsToCheck, options_.BkCheck_PdBad_t.value()); + std::vector PdBad(numProfileLevels, options_.BkCheck_PdBad_t.value()); // Local version of temperature background error. - std::vector BackgrErrT(numLevelsToCheck, 0); - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + std::vector BackgrErrT(numProfileLevels, 0); + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { BackgrErrT[jlev] = tBkgErr[jlev]; } // Extra representivity error for data on reported levels. @@ -78,7 +75,7 @@ namespace ufo { std::fabs(Latitude[0]) < options_.BkCheck_Psplit_latitude_tropics ? options_.BkCheck_Psplit_tropics.value() : options_.BkCheck_Psplit_extratropics.value(); - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if (pressures[jlev] <= Psplit) { BackgrErrT[jlev] = tBkgErr[jlev] == missingValueFloat ? missingValueFloat : @@ -92,7 +89,7 @@ namespace ufo { } // Modify observation PGE if certain flags have been set. - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if (tFlags[jlev] & ufo::MetOfficeQCFlags::Profile::SuperadiabatFlag) tPGE[jlev] = 0.5 + 0.5 * tPGE[jlev]; if (tFlags[jlev] & ufo::MetOfficeQCFlags::Profile::InterpolationFlag) diff --git a/src/ufo/profile/ProfileCheckBackgroundTemperature.h b/src/ufo/profile/ProfileCheckBackgroundTemperature.h index 115ac80f0..ebb656781 100644 --- a/src/ufo/profile/ProfileCheckBackgroundTemperature.h +++ b/src/ufo/profile/ProfileCheckBackgroundTemperature.h @@ -13,9 +13,7 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" #include "ufo/utils/ProbabilityOfGrossError.h" @@ -37,16 +35,15 @@ namespace ufo { /// (except those with PGE > 0.999) will be used in vertical averaging. class ProfileCheckBackgroundTemperature : public ProfileCheckBase { public: - ProfileCheckBackgroundTemperature(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckBackgroundTemperature(const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; + void runCheck(ProfileDataHandler &profileDataHandler) override; - /// Fill variables in validator - void fillValidator() override {} + /// List of names of required obs diagnostics. + oops::Variables getObsDiagNames() override { + return oops::Variables({ufo::VariableNames::bkgerr_air_temperature}); + } }; } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckBackgroundWindSpeed.cc b/src/ufo/profile/ProfileCheckBackgroundWindSpeed.cc index 31625b095..eeffe04ba 100644 --- a/src/ufo/profile/ProfileCheckBackgroundWindSpeed.cc +++ b/src/ufo/profile/ProfileCheckBackgroundWindSpeed.cc @@ -13,49 +13,46 @@ namespace ufo { makerProfileCheckBackgroundWindSpeed_("BackgroundWindSpeed"); ProfileCheckBackgroundWindSpeed::ProfileCheckBackgroundWindSpeed - (const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator) + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) {} - void ProfileCheckBackgroundWindSpeed::runCheck() + void ProfileCheckBackgroundWindSpeed::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Background check for wind velocity" << std::endl; - const size_t numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const size_t numProfileLevels = profileDataHandler.getNumProfileLevels(); const bool ModelLevels = options_.modellevels.value(); const std::vector &uObs = - profileDataHandler_.get(ufo::VariableNames::obs_eastward_wind); + profileDataHandler.get(ufo::VariableNames::obs_eastward_wind); const std::vector &uObsErr = - profileDataHandler_.get(ufo::VariableNames::obserr_eastward_wind); + profileDataHandler.get(ufo::VariableNames::obserr_eastward_wind); const std::vector &uBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_eastward_wind); + profileDataHandler.get(ufo::VariableNames::hofx_eastward_wind); const std::vector &uBkgErr = - profileDataHandler_.get(ufo::VariableNames::bkgerr_eastward_wind); + profileDataHandler.getObsDiag(ufo::VariableNames::bkgerr_eastward_wind); std::vector &uPGE = - profileDataHandler_.get(ufo::VariableNames::pge_eastward_wind); + profileDataHandler.get(ufo::VariableNames::pge_eastward_wind); std::vector &uPGEBd = - profileDataHandler_.get(ufo::VariableNames::pgebd_eastward_wind); + profileDataHandler.get(ufo::VariableNames::pgebd_eastward_wind); std::vector &uFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_eastward_wind); + profileDataHandler.get(ufo::VariableNames::qcflags_eastward_wind); const std::vector &vObs = - profileDataHandler_.get(ufo::VariableNames::obs_northward_wind); + profileDataHandler.get(ufo::VariableNames::obs_northward_wind); const std::vector &vObsErr = - profileDataHandler_.get(ufo::VariableNames::obserr_northward_wind); + profileDataHandler.get(ufo::VariableNames::obserr_northward_wind); const std::vector &vBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_northward_wind); + profileDataHandler.get(ufo::VariableNames::hofx_northward_wind); const std::vector &vBkgErr = - profileDataHandler_.get(ufo::VariableNames::bkgerr_northward_wind); + profileDataHandler.getObsDiag(ufo::VariableNames::bkgerr_northward_wind); std::vector &vPGE = - profileDataHandler_.get(ufo::VariableNames::pge_northward_wind); + profileDataHandler.get(ufo::VariableNames::pge_northward_wind); std::vector &vPGEBd = - profileDataHandler_.get(ufo::VariableNames::pgebd_northward_wind); + profileDataHandler.get(ufo::VariableNames::pgebd_northward_wind); std::vector &vFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_northward_wind); + profileDataHandler.get(ufo::VariableNames::qcflags_northward_wind); const std::vector &timeFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_time); + profileDataHandler.get(ufo::VariableNames::qcflags_time); if (!oops::allVectorsSameNonZeroSize(uObs, uObsErr, uBkg, uBkgErr, uPGE, uFlags, @@ -73,10 +70,10 @@ namespace ufo { } // Probability density of 'bad' observations. - std::vector PdBad(numLevelsToCheck, options_.BkCheck_PdBad_uv.value()); + std::vector PdBad(numProfileLevels, options_.BkCheck_PdBad_uv.value()); // Modify observation PGE if certain flags have been set. - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if (uFlags[jlev] & ufo::MetOfficeQCFlags::Profile::InterpolationFlag) uPGE[jlev] = 0.5 + 0.5 * uPGE[jlev]; if (timeFlags[jlev]) diff --git a/src/ufo/profile/ProfileCheckBackgroundWindSpeed.h b/src/ufo/profile/ProfileCheckBackgroundWindSpeed.h index 37cffc312..1aa105a26 100644 --- a/src/ufo/profile/ProfileCheckBackgroundWindSpeed.h +++ b/src/ufo/profile/ProfileCheckBackgroundWindSpeed.h @@ -13,9 +13,7 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" #include "ufo/utils/ProbabilityOfGrossError.h" @@ -37,16 +35,16 @@ namespace ufo { /// (except those with PGE > 0.999) will be used in vertical averaging. class ProfileCheckBackgroundWindSpeed : public ProfileCheckBase { public: - ProfileCheckBackgroundWindSpeed(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckBackgroundWindSpeed(const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; + void runCheck(ProfileDataHandler &profileDataHandler) override; - /// Fill variables in validator - void fillValidator() override {} + /// List of names of required obs diagnostics. + oops::Variables getObsDiagNames() override { + return oops::Variables({ufo::VariableNames::bkgerr_eastward_wind, + ufo::VariableNames::bkgerr_northward_wind}); + } }; } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckBase.cc b/src/ufo/profile/ProfileCheckBase.cc index 4e24bab9b..1a5a6e69b 100644 --- a/src/ufo/profile/ProfileCheckBase.cc +++ b/src/ufo/profile/ProfileCheckBase.cc @@ -13,14 +13,8 @@ #include "oops/util/Logger.h" namespace ufo { - ProfileCheckBase::ProfileCheckBase(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : options_(options), - profileIndices_(profileIndices), - profileDataHandler_(profileDataHandler), - profileCheckValidator_(profileCheckValidator) + ProfileCheckBase::ProfileCheckBase(const ProfileConsistencyCheckParameters &options) + : options_(options) {} ProfileCheckFactory::ProfileCheckFactory(const std::string & name) @@ -32,10 +26,7 @@ namespace ufo { std::unique_ptr ProfileCheckFactory::create(const std::string& name, - const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) + const ProfileConsistencyCheckParameters &options) { oops::Log::trace() << "ProfileCheckBase::create starting" << std::endl; typename std::map::iterator jloc = @@ -46,10 +37,7 @@ namespace ufo { throw eckit::BadParameter(name + " does not exist in ufo::ProfileCheckFactory. " "Possible values:" + makerNameList, Here()); } - std::unique_ptr ptr = jloc->second->make(options, - profileIndices, - profileDataHandler, - profileCheckValidator); + std::unique_ptr ptr = jloc->second->make(options); oops::Log::trace() << "ProfileCheckBase::create done" << std::endl; return ptr; } diff --git a/src/ufo/profile/ProfileCheckBase.h b/src/ufo/profile/ProfileCheckBase.h index af2d77458..ddc26b113 100644 --- a/src/ufo/profile/ProfileCheckBase.h +++ b/src/ufo/profile/ProfileCheckBase.h @@ -25,35 +25,48 @@ #include "oops/util/missingValues.h" #include "oops/util/PropertiesOfNVectors.h" -#include "ufo/utils/metoffice/MetOfficeQCFlags.h" +#include "ufo/filters/ProfileConsistencyCheckParameters.h" -namespace ufo { - class ProfileConsistencyCheckParameters; - class ProfileCheckValidator; - class ProfileDataHandler; - class ProfileIndices; -} +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/VariableNames.h" + +#include "ufo/utils/metoffice/MetOfficeQCFlags.h" namespace ufo { /// \brief Profile QC checker base class class ProfileCheckBase { public: - ProfileCheckBase(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckBase(const ProfileConsistencyCheckParameters &options); + virtual ~ProfileCheckBase() {} /// Run check - virtual void runCheck() = 0; + virtual void runCheck(ProfileDataHandler &profileDataHandler) = 0; - /// Fill variables in validator - virtual void fillValidator() = 0; + /// Fill variables in validator using a ProfileDataHandler. + /// This function will only be called for subclasses of ProfileCheckBase + /// whose runOnEntireSample() implementation returns false. + /// If runOnEntireSample() returns true, the subclass needs + /// to handle the storage of validation data on its own. + /// For an example see the ProfileAveragePressure class. + virtual void fillValidationData(ProfileDataHandler &profileDataHandler) {} /// Get result of check (default fail) virtual bool getResult() {return false;} + /// Run this check on the entire sample? + virtual bool runOnEntireSample() {return false;} + + /// List of names of required GeoVaLs. + virtual oops::Variables getGeoVaLNames() {return oops::Variables();} + + /// List of names of GeoVaLs used in check validation. + virtual oops::Variables getValidationGeoVaLNames() {return oops::Variables();} + + /// List of names of required obs diagnostics. + virtual oops::Variables getObsDiagNames() {return oops::Variables();} + protected: // functions /// Apply correction to vector of values template @@ -66,18 +79,35 @@ namespace ufo { std::transform(v1.begin(), v1.end(), v2.begin(), vout.begin(), std::plus()); } + /// Set a QC flag on one profile level. + /// This is the base case for one vector. + template + void SetQCFlag(const int& flag, + const size_t& jlev, + std::vector &vec) + { + if (vec.size() > jlev) vec[jlev] |= flag; + } + + /// Set a QC flag on one profile level. + /// This is the recursive case that accepts an arbitrary number of vectors + /// using a variadic template. + template + void SetQCFlag(const int& flag, + const size_t& jlev, + std::vector &vec1, + Args&... vecs) + { + if (vec1.size() > jlev) vec1[jlev] |= flag; + SetQCFlag(flag, jlev, vecs...); + } + protected: // variables /// Configurable parameters const ProfileConsistencyCheckParameters &options_; - /// Indices of profile's observations in the entire sample - const ProfileIndices &profileIndices_; - - /// Profile data handler - ProfileDataHandler &profileDataHandler_; - - /// Profile check validator - ProfileCheckValidator &profileCheckValidator_; + /// Missing value (int) + const int missingValueInt = util::missingValue(1); /// Missing value (float) const float missingValueFloat = util::missingValue(1.0f); @@ -88,18 +118,12 @@ namespace ufo { { public: static std::unique_ptr create(const std::string&, - const ProfileConsistencyCheckParameters&, - const ProfileIndices&, - ProfileDataHandler&, - ProfileCheckValidator&); + const ProfileConsistencyCheckParameters&); virtual ~ProfileCheckFactory() = default; protected: explicit ProfileCheckFactory(const std::string &); private: - virtual std::unique_ptr make(const ProfileConsistencyCheckParameters&, - const ProfileIndices&, - ProfileDataHandler&, - ProfileCheckValidator&) = 0; + virtual std::unique_ptr make(const ProfileConsistencyCheckParameters&) = 0; static std::map & getMakers() { @@ -112,15 +136,9 @@ namespace ufo { class ProfileCheckMaker : public ProfileCheckFactory { virtual std::unique_ptr - make(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) + make(const ProfileConsistencyCheckParameters &options) { - return std::unique_ptr(new T(options, - profileIndices, - profileDataHandler, - profileCheckValidator)); + return std::unique_ptr(new T(options)); } public: explicit ProfileCheckMaker(const std::string & name) diff --git a/src/ufo/profile/ProfileCheckBasic.cc b/src/ufo/profile/ProfileCheckBasic.cc index 860d45365..5a04a587f 100644 --- a/src/ufo/profile/ProfileCheckBasic.cc +++ b/src/ufo/profile/ProfileCheckBasic.cc @@ -12,14 +12,11 @@ namespace ufo { static ProfileCheckMaker makerProfileCheckBasic_("Basic"); - ProfileCheckBasic::ProfileCheckBasic(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator) + ProfileCheckBasic::ProfileCheckBasic(const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) {} - void ProfileCheckBasic::runCheck() + void ProfileCheckBasic::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Basic checks" << std::endl; @@ -33,16 +30,16 @@ namespace ufo { return; } - const int numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const int numProfileLevels = profileDataHandler.getNumProfileLevels(); const std::vector &pressures = - profileDataHandler_.get(ufo::VariableNames::obs_air_pressure); + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); // All QC flags are retrieved for the basic checks. // (Some might be empty; that is checked before they are used.) - std::vector &tFlags = profileDataHandler_.get + std::vector &tFlags = profileDataHandler.get (ufo::VariableNames::qcflags_air_temperature); - std::vector &zFlags = profileDataHandler_.get + std::vector &zFlags = profileDataHandler.get (ufo::VariableNames::qcflags_geopotential_height); - std::vector &uFlags = profileDataHandler_.get + std::vector &uFlags = profileDataHandler.get (ufo::VariableNames::qcflags_eastward_wind); // Warn and exit if pressures vector is empty @@ -53,11 +50,11 @@ namespace ufo { } // Is the number of levels to check OK? - bool numLevelsToCheckOK = (numLevelsToCheck > 0); + bool numProfileLevelsOK = (numProfileLevels > 0); // Are any levels in the wrong order? bool pressOrderOK = true; - for (int jlev = 0; jlev < numLevelsToCheck - 1; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels - 1; ++jlev) { pressOrderOK = pressOrderOK && (pressures[jlev] >= pressures[jlev + 1]); if (!pressOrderOK) break; } @@ -72,18 +69,18 @@ namespace ufo { pressures.back() > options_.BChecks_minValidP.value() : false); - oops::Log::debug() << " -> numLevelsToCheckOK: " << numLevelsToCheckOK << std::endl; + oops::Log::debug() << " -> numProfileLevelsOK: " << numProfileLevelsOK << std::endl; oops::Log::debug() << " -> pressOrderOK: " << pressOrderOK << std::endl; oops::Log::debug() << " -> maxPressOK: " << maxPressOK << std::endl; oops::Log::debug() << " -> minPressOK: " << minPressOK << std::endl; - result_ = numLevelsToCheckOK && pressOrderOK && maxPressOK && minPressOK; + result_ = numProfileLevelsOK && pressOrderOK && maxPressOK && minPressOK; oops::Log::debug() << " -> basicResult: " << result_ << std::endl; // If the basic checks are failed, set reject flags // This is not done in the OPS sonde consistency checks, but is done in Ops_SondeAverage.inc if (options_.flagBasicChecksFail.value() && !result_) { - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if (!tFlags.empty()) tFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; if (!zFlags.empty()) zFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; if (!uFlags.empty()) uFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; diff --git a/src/ufo/profile/ProfileCheckBasic.h b/src/ufo/profile/ProfileCheckBasic.h index 31c481f78..d105b900c 100644 --- a/src/ufo/profile/ProfileCheckBasic.h +++ b/src/ufo/profile/ProfileCheckBasic.h @@ -11,29 +11,17 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" - -namespace ufo { - class ProfileConsistencyCheckParameters; -} namespace ufo { /// \brief Profile QC: basic checks on pressure class ProfileCheckBasic : public ProfileCheckBase { public: - ProfileCheckBasic(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckBasic(const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; - - /// Fill variables in validator - void fillValidator() override {} + void runCheck(ProfileDataHandler &profileDataHandler) override; /// Return result of basic checks bool getResult() override {return result_;} diff --git a/src/ufo/profile/ProfileCheckHistory.cc b/src/ufo/profile/ProfileCheckHistory.cc new file mode 100644 index 000000000..b306b1a07 --- /dev/null +++ b/src/ufo/profile/ProfileCheckHistory.cc @@ -0,0 +1,42 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/profile/ProfileCheckHistory.h" + +namespace ufo { + + static ProfileCheckMaker + makerProfileCheckHistory_("History"); + + ProfileCheckHistory::ProfileCheckHistory + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) + {} + + void ProfileCheckHistory::runCheck(ProfileDataHandler &profileDataHandler) + { + oops::Log::debug() << " History check" << std::endl; + + const size_t nprofs = profileDataHandler.getObsdb().nrecs(); + + // Vector of profiles containing data for the history check. + std::vector profiles; + for (size_t jprof = 0; jprof < nprofs; ++jprof) { + profileDataHandler.initialiseNextProfile(); + ProfileDataHistory profile(profileDataHandler); + profile.fill(); + profiles.emplace_back(profile); + } + + // todo: this is how to access data within individual profiles + /* + for (auto &profile : profiles) { + const std::vector &U = profile.get(ufo::VariableNames::obs_eastward_wind); + } + */ + } +} // namespace ufo diff --git a/src/ufo/profile/ProfileCheckHistory.h b/src/ufo/profile/ProfileCheckHistory.h new file mode 100644 index 000000000..01f05449b --- /dev/null +++ b/src/ufo/profile/ProfileCheckHistory.h @@ -0,0 +1,44 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILECHECKHISTORY_H_ +#define UFO_PROFILE_PROFILECHECKHISTORY_H_ + +#include +#include +#include + +#include "ufo/profile/ProfileCheckBase.h" +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/ProfileDataHistory.h" + +#include "ufo/utils/metoffice/MetOfficeObservationIDs.h" + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + class ProfileConsistencyCheckParameters; +} + +namespace ufo { + + /// \brief Profile QC: history check. + class ProfileCheckHistory : public ProfileCheckBase { + public: + explicit ProfileCheckHistory(const ProfileConsistencyCheckParameters &options); + + /// Run check + void runCheck(ProfileDataHandler &profileDataHandler) override; + + /// Run this check on the entire sample? + bool runOnEntireSample() override {return true;} + }; +} // namespace ufo + +#endif // UFO_PROFILE_PROFILECHECKHISTORY_H_ diff --git a/src/ufo/profile/ProfileCheckHydrostatic.cc b/src/ufo/profile/ProfileCheckHydrostatic.cc index 24e9b9660..f3e2aea6b 100644 --- a/src/ufo/profile/ProfileCheckHydrostatic.cc +++ b/src/ufo/profile/ProfileCheckHydrostatic.cc @@ -12,50 +12,47 @@ namespace ufo { static ProfileCheckMaker makerProfileCheckHydrostatic_("Hydrostatic"); - ProfileCheckHydrostatic::ProfileCheckHydrostatic(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator), + ProfileCheckHydrostatic::ProfileCheckHydrostatic(const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options), ProfileStandardLevels(options) {} - void ProfileCheckHydrostatic::runCheck() + void ProfileCheckHydrostatic::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Hydrostatic check" << std::endl; - const int numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const int numProfileLevels = profileDataHandler.getNumProfileLevels(); const std::vector &pressures = - profileDataHandler_.get(ufo::VariableNames::obs_air_pressure); + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); const std::vector &tObs = - profileDataHandler_.get(ufo::VariableNames::obs_air_temperature); + profileDataHandler.get(ufo::VariableNames::obs_air_temperature); const std::vector &tBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_air_temperature); + profileDataHandler.get(ufo::VariableNames::hofx_air_temperature); const std::vector &zObs = - profileDataHandler_.get(ufo::VariableNames::obs_geopotential_height); + profileDataHandler.get(ufo::VariableNames::obs_geopotential_height); const std::vector &zBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_geopotential_height); + profileDataHandler.get(ufo::VariableNames::hofx_geopotential_height); std::vector &tFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_air_temperature); + profileDataHandler.get(ufo::VariableNames::qcflags_air_temperature); std::vector &zFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_geopotential_height); + profileDataHandler.get(ufo::VariableNames::qcflags_geopotential_height); std::vector &NumAnyErrors = - profileDataHandler_.get(ufo::VariableNames::counter_NumAnyErrors); + profileDataHandler.get(ufo::VariableNames::counter_NumAnyErrors); std::vector &Num925Miss = - profileDataHandler_.get(ufo::VariableNames::counter_Num925Miss); + profileDataHandler.get(ufo::VariableNames::counter_Num925Miss); std::vector &Num100Miss = - profileDataHandler_.get(ufo::VariableNames::counter_Num100Miss); + profileDataHandler.get(ufo::VariableNames::counter_Num100Miss); std::vector &NumStdMiss = - profileDataHandler_.get(ufo::VariableNames::counter_NumStdMiss); + profileDataHandler.get(ufo::VariableNames::counter_NumStdMiss); std::vector &NumHydErrObs = - profileDataHandler_.get(ufo::VariableNames::counter_NumHydErrObs); + profileDataHandler.get(ufo::VariableNames::counter_NumHydErrObs); std::vector &NumIntHydErrors = - profileDataHandler_.get(ufo::VariableNames::counter_NumIntHydErrors); + profileDataHandler.get(ufo::VariableNames::counter_NumIntHydErrors); const std::vector &tObsCorrection = - profileDataHandler_.get(ufo::VariableNames::obscorrection_air_temperature); + profileDataHandler.get(ufo::VariableNames::obscorrection_air_temperature); std::vector &zObsCorrection = - profileDataHandler_.get(ufo::VariableNames::obscorrection_geopotential_height); + profileDataHandler.get(ufo::VariableNames::obscorrection_geopotential_height); if (!oops::allVectorsSameNonZeroSize(pressures, tObs, tBkg, zObs, zBkg, tFlags, zFlags, tObsCorrection, zObsCorrection)) { @@ -71,15 +68,15 @@ namespace ufo { std::vector tObsFinal; correctVector(tObs, tObsCorrection, tObsFinal); - calcStdLevels(numLevelsToCheck, pressures, tObsFinal, tFlags); + calcStdLevels(numProfileLevels, pressures, tObsFinal, tFlags); findHCheckStdLevs(); HydDesc_ = options_.HydDesc.value(); - DC_.assign(numLevelsToCheck, missingValueFloat); - ETol_.assign(numLevelsToCheck, missingValueFloat); - D_.assign(numLevelsToCheck, missingValueFloat); - E_.assign(numLevelsToCheck + 1, missingValueFloat); - HydError_.assign(numLevelsToCheck, 0); + DC_.assign(numProfileLevels, missingValueFloat); + ETol_.assign(numProfileLevels, missingValueFloat); + D_.assign(numProfileLevels, missingValueFloat); + E_.assign(numProfileLevels + 1, missingValueFloat); + HydError_.assign(numProfileLevels, 0); int NumErrors = 0; // Find large thickness residuals @@ -358,13 +355,13 @@ namespace ufo { } } - void ProfileCheckHydrostatic::fillValidator() + void ProfileCheckHydrostatic::fillValidationData(ProfileDataHandler &profileDataHandler) { - profileDataHandler_.set(ufo::VariableNames::DC, std::move(DC_)); - profileDataHandler_.set(ufo::VariableNames::ETol, std::move(ETol_)); - profileDataHandler_.set(ufo::VariableNames::D, std::move(D_)); - profileDataHandler_.set(ufo::VariableNames::E, std::move(E_)); - profileDataHandler_.set(ufo::VariableNames::HydError, std::move(HydError_)); + profileDataHandler.set(ufo::VariableNames::DC, std::move(DC_)); + profileDataHandler.set(ufo::VariableNames::ETol, std::move(ETol_)); + profileDataHandler.set(ufo::VariableNames::D, std::move(D_)); + profileDataHandler.set(ufo::VariableNames::E, std::move(E_)); + profileDataHandler.set(ufo::VariableNames::HydError, std::move(HydError_)); } } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckHydrostatic.h b/src/ufo/profile/ProfileCheckHydrostatic.h index 6f151c87f..c7fd6add4 100644 --- a/src/ufo/profile/ProfileCheckHydrostatic.h +++ b/src/ufo/profile/ProfileCheckHydrostatic.h @@ -14,9 +14,7 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" #include "ufo/profile/ProfileStandardLevels.h" namespace ufo { @@ -29,16 +27,13 @@ namespace ufo { class ProfileCheckHydrostatic : public ProfileCheckBase, private ProfileStandardLevels { public: - ProfileCheckHydrostatic(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckHydrostatic(const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; + void runCheck(ProfileDataHandler &profileDataHandler) override; /// Fill variables in validator - void fillValidator() override; + void fillValidationData(ProfileDataHandler &profileDataHandler) override; private: /// Hydrostatic error descriptions diff --git a/src/ufo/profile/ProfileCheckInterpolation.cc b/src/ufo/profile/ProfileCheckInterpolation.cc index 3aaab4935..c783e262f 100644 --- a/src/ufo/profile/ProfileCheckInterpolation.cc +++ b/src/ufo/profile/ProfileCheckInterpolation.cc @@ -14,36 +14,33 @@ namespace ufo { makerProfileCheckInterpolation_("Interpolation"); ProfileCheckInterpolation::ProfileCheckInterpolation - (const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator), + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options), ProfileStandardLevels(options) {} - void ProfileCheckInterpolation::runCheck() + void ProfileCheckInterpolation::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Interpolation check" << std::endl; - const int numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const int numProfileLevels = profileDataHandler.getNumProfileLevels(); const std::vector &pressures = - profileDataHandler_.get(ufo::VariableNames::obs_air_pressure); + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); const std::vector &tObs = - profileDataHandler_.get(ufo::VariableNames::obs_air_temperature); + profileDataHandler.get(ufo::VariableNames::obs_air_temperature); const std::vector &tBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_air_temperature); + profileDataHandler.get(ufo::VariableNames::hofx_air_temperature); std::vector &tFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_air_temperature); + profileDataHandler.get(ufo::VariableNames::qcflags_air_temperature); std::vector &NumAnyErrors = - profileDataHandler_.get(ufo::VariableNames::counter_NumAnyErrors); + profileDataHandler.get(ufo::VariableNames::counter_NumAnyErrors); std::vector &NumInterpErrors = - profileDataHandler_.get(ufo::VariableNames::counter_NumInterpErrors); + profileDataHandler.get(ufo::VariableNames::counter_NumInterpErrors); std::vector &NumInterpErrObs = - profileDataHandler_.get(ufo::VariableNames::counter_NumInterpErrObs); + profileDataHandler.get(ufo::VariableNames::counter_NumInterpErrObs); const std::vector &tObsCorrection = - profileDataHandler_.get(ufo::VariableNames::obscorrection_air_temperature); + profileDataHandler.get(ufo::VariableNames::obscorrection_air_temperature); if (!oops::allVectorsSameNonZeroSize(pressures, tObs, tBkg, tFlags, tObsCorrection)) { @@ -59,10 +56,10 @@ namespace ufo { std::vector tObsFinal; correctVector(tObs, tObsCorrection, tObsFinal); - calcStdLevels(numLevelsToCheck, pressures, tObsFinal, tFlags); + calcStdLevels(numProfileLevels, pressures, tObsFinal, tFlags); - LevErrors_.assign(numLevelsToCheck, -1); - tInterp_.assign(numLevelsToCheck, missingValueFloat); + LevErrors_.assign(numProfileLevels, -1); + tInterp_.assign(numProfileLevels, missingValueFloat); int NumErrors = 0; @@ -140,19 +137,19 @@ namespace ufo { if (NumErrors > 0) NumInterpErrObs[0]++; } - void ProfileCheckInterpolation::fillValidator() + void ProfileCheckInterpolation::fillValidationData(ProfileDataHandler &profileDataHandler) { - profileDataHandler_.set(ufo::VariableNames::StdLev, std::move(StdLev_)); - profileDataHandler_.set(ufo::VariableNames::SigAbove, std::move(SigAbove_)); - profileDataHandler_.set(ufo::VariableNames::SigBelow, std::move(SigBelow_)); - profileDataHandler_.set(ufo::VariableNames::IndStd, std::move(IndStd_)); - profileDataHandler_.set(ufo::VariableNames::LevErrors, std::move(LevErrors_)); - profileDataHandler_.set(ufo::VariableNames::tInterp, std::move(tInterp_)); - profileDataHandler_.set(ufo::VariableNames::LogP, std::move(LogP_)); - std::vector NumStd(profileIndices_.getNumLevelsToCheck(), std::move(NumStd_)); - std::vector NumSig(profileIndices_.getNumLevelsToCheck(), std::move(NumSig_)); - profileDataHandler_.set(ufo::VariableNames::NumStd, std::move(NumStd)); - profileDataHandler_.set(ufo::VariableNames::NumSig, std::move(NumSig)); + profileDataHandler.set(ufo::VariableNames::StdLev, std::move(StdLev_)); + profileDataHandler.set(ufo::VariableNames::SigAbove, std::move(SigAbove_)); + profileDataHandler.set(ufo::VariableNames::SigBelow, std::move(SigBelow_)); + profileDataHandler.set(ufo::VariableNames::IndStd, std::move(IndStd_)); + profileDataHandler.set(ufo::VariableNames::LevErrors, std::move(LevErrors_)); + profileDataHandler.set(ufo::VariableNames::tInterp, std::move(tInterp_)); + profileDataHandler.set(ufo::VariableNames::LogP, std::move(LogP_)); + std::vector NumStd(profileDataHandler.getNumProfileLevels(), std::move(NumStd_)); + std::vector NumSig(profileDataHandler.getNumProfileLevels(), std::move(NumSig_)); + profileDataHandler.set(ufo::VariableNames::NumStd, std::move(NumStd)); + profileDataHandler.set(ufo::VariableNames::NumSig, std::move(NumSig)); } } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckInterpolation.h b/src/ufo/profile/ProfileCheckInterpolation.h index e1e820a08..dc676b5e1 100644 --- a/src/ufo/profile/ProfileCheckInterpolation.h +++ b/src/ufo/profile/ProfileCheckInterpolation.h @@ -13,9 +13,7 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" #include "ufo/profile/ProfileStandardLevels.h" namespace ufo { @@ -28,15 +26,13 @@ namespace ufo { class ProfileCheckInterpolation : public ProfileCheckBase, private ProfileStandardLevels { public: - ProfileCheckInterpolation(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckInterpolation(const ProfileConsistencyCheckParameters &options); + /// Run check - void runCheck() override; + void runCheck(ProfileDataHandler &profileDataHandler) override; /// Fill variables in validator - void fillValidator() override; + void fillValidationData(ProfileDataHandler &profileDataHandler) override; private: /// Number of failed checks by level diff --git a/src/ufo/profile/ProfileCheckPermanentReject.cc b/src/ufo/profile/ProfileCheckPermanentReject.cc index 04cd14de0..165beb7da 100644 --- a/src/ufo/profile/ProfileCheckPermanentReject.cc +++ b/src/ufo/profile/ProfileCheckPermanentReject.cc @@ -13,31 +13,28 @@ namespace ufo { makerProfileCheckPermanentReject_("PermanentReject"); ProfileCheckPermanentReject::ProfileCheckPermanentReject - (const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator) + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) {} - void ProfileCheckPermanentReject::runCheck() + void ProfileCheckPermanentReject::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Permanent rejection check" << std::endl; - const size_t numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const size_t numProfileLevels = profileDataHandler.getNumProfileLevels(); const bool ModelLevels = options_.modellevels.value(); std::vector &tFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_air_temperature); + profileDataHandler.get(ufo::VariableNames::qcflags_air_temperature); std::vector &rhFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_relative_humidity); + profileDataHandler.get(ufo::VariableNames::qcflags_relative_humidity); std::vector &uFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_eastward_wind); + profileDataHandler.get(ufo::VariableNames::qcflags_eastward_wind); std::vector &vFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_northward_wind); + profileDataHandler.get(ufo::VariableNames::qcflags_northward_wind); std::vector &zFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_geopotential_height); + profileDataHandler.get(ufo::VariableNames::qcflags_geopotential_height); std::vector &ReportFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_observation_report); + profileDataHandler.get(ufo::VariableNames::qcflags_observation_report); if (ReportFlags.empty()) { oops::Log::debug() << "ReportFlags vector is empty. " @@ -46,24 +43,24 @@ namespace ufo { } // Set PermRejectFlag on individual elements if whole report has PermReject. - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if (ReportFlags[jlev] & ufo::MetOfficeQCFlags::WholeObReport::PermRejectReport) { - for (auto flags : {&tFlags, &rhFlags, &uFlags, &vFlags, &zFlags}) - if (!flags->empty()) (*flags)[jlev] |= ufo::MetOfficeQCFlags::Elem::PermRejectFlag; + SetQCFlag(ufo::MetOfficeQCFlags::Elem::PermRejectFlag, jlev, + tFlags, rhFlags, uFlags, vFlags, zFlags); } } // Set FinalRejectFlag on individual elements if a variety of criteria // are met on model-level data. if (ModelLevels) { - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if ((ReportFlags[jlev] & ufo::MetOfficeQCFlags::WholeObReport::PermRejectReport) || (ReportFlags[jlev] & ufo::MetOfficeQCFlags::WholeObReport::TrackRejectReport) || (ReportFlags[jlev] & ufo::MetOfficeQCFlags::WholeObReport::SurplusReport) || (ReportFlags[jlev] & ufo::MetOfficeQCFlags::WholeObReport::OutOfAreaReport)) { ReportFlags[jlev] |= ufo::MetOfficeQCFlags::WholeObReport::FinalRejectReport; - for (auto flags : {&tFlags, &rhFlags, &uFlags, &vFlags, &zFlags}) - if (!flags->empty()) (*flags)[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + SetQCFlag(ufo::MetOfficeQCFlags::Elem::FinalRejectFlag, jlev, + tFlags, rhFlags, uFlags, vFlags, zFlags); } } } diff --git a/src/ufo/profile/ProfileCheckPermanentReject.h b/src/ufo/profile/ProfileCheckPermanentReject.h index b462e15a5..61eea4e8d 100644 --- a/src/ufo/profile/ProfileCheckPermanentReject.h +++ b/src/ufo/profile/ProfileCheckPermanentReject.h @@ -13,9 +13,7 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" namespace ioda { class ObsSpace; @@ -30,16 +28,10 @@ namespace ufo { /// \brief Profile QC: reject observations which are flagged to be permanently rejected. class ProfileCheckPermanentReject : public ProfileCheckBase { public: - ProfileCheckPermanentReject(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckPermanentReject(const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; - - /// Fill variables in validator - void fillValidator() override {} + void runCheck(ProfileDataHandler &profileDataHandler) override; }; } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckRH.cc b/src/ufo/profile/ProfileCheckRH.cc index 57b1a0e84..22e938a0d 100644 --- a/src/ufo/profile/ProfileCheckRH.cc +++ b/src/ufo/profile/ProfileCheckRH.cc @@ -13,47 +13,44 @@ namespace ufo { static ProfileCheckMaker makerProfileCheckRH_("RH"); ProfileCheckRH::ProfileCheckRH - (const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator) + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) {} - void ProfileCheckRH::runCheck() + void ProfileCheckRH::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Relative humidity check" << std::endl; - const int numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const int numProfileLevels = profileDataHandler.getNumProfileLevels(); const std::vector &pressures = - profileDataHandler_.get(ufo::VariableNames::obs_air_pressure); + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); const std::vector &tObs = - profileDataHandler_.get(ufo::VariableNames::obs_air_temperature); + profileDataHandler.get(ufo::VariableNames::obs_air_temperature); const std::vector &tBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_air_temperature); + profileDataHandler.get(ufo::VariableNames::hofx_air_temperature); const std::vector &RHObs = - profileDataHandler_.get(ufo::VariableNames::obs_relative_humidity); + profileDataHandler.get(ufo::VariableNames::obs_relative_humidity); const std::vector &RHBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_relative_humidity); + profileDataHandler.get(ufo::VariableNames::hofx_relative_humidity); const std::vector &tdObs = - profileDataHandler_.get(ufo::VariableNames::obs_dew_point_temperature); + profileDataHandler.get(ufo::VariableNames::obs_dew_point_temperature); const std::vector &tFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_air_temperature); + profileDataHandler.get(ufo::VariableNames::qcflags_air_temperature); std::vector &RHFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_relative_humidity); + profileDataHandler.get(ufo::VariableNames::qcflags_relative_humidity); const std::vector &tObsCorrection = - profileDataHandler_.get(ufo::VariableNames::obscorrection_air_temperature); + profileDataHandler.get(ufo::VariableNames::obscorrection_air_temperature); std::vector &TotCProfs = - profileDataHandler_.get(ufo::VariableNames::counter_TotCProfs); + profileDataHandler.get(ufo::VariableNames::counter_TotCProfs); std::vector &TotHProfs = - profileDataHandler_.get(ufo::VariableNames::counter_TotHProfs); + profileDataHandler.get(ufo::VariableNames::counter_TotHProfs); std::vector &TotCFlags = - profileDataHandler_.get(ufo::VariableNames::counter_TotCFlags); + profileDataHandler.get(ufo::VariableNames::counter_TotCFlags); std::vector &TotHFlags = - profileDataHandler_.get(ufo::VariableNames::counter_TotHFlags); + profileDataHandler.get(ufo::VariableNames::counter_TotHFlags); std::vector &TotLFlags = - profileDataHandler_.get(ufo::VariableNames::counter_TotLFlags); + profileDataHandler.get(ufo::VariableNames::counter_TotLFlags); if (!oops::allVectorsSameNonZeroSize(pressures, tObs, tBkg, RHObs, RHBkg, tdObs, tFlags, RHFlags, tObsCorrection)) { @@ -75,17 +72,17 @@ namespace ufo { float PTrop = 0.0; // Tropopause pressure level int NLowP = 0; // Number of RH reports above 100 hPa float RHDLowP = 0.0; // Mean RH O-B above 100 hPa - Press_.assign(numLevelsToCheck, 0.0); - Temp_.assign(numLevelsToCheck, 0.0); - rh_.assign(numLevelsToCheck, 0.0); - td_.assign(numLevelsToCheck, 0.0); - tbk_.assign(numLevelsToCheck, 0.0); - rhbk_.assign(numLevelsToCheck, 0.0); - FlagH_.assign(numLevelsToCheck, 0); - Indx_.assign(numLevelsToCheck, -1); + Press_.assign(numProfileLevels, 0.0); + Temp_.assign(numProfileLevels, 0.0); + rh_.assign(numProfileLevels, 0.0); + td_.assign(numProfileLevels, 0.0); + tbk_.assign(numProfileLevels, 0.0); + rhbk_.assign(numProfileLevels, 0.0); + FlagH_.assign(numProfileLevels, 0); + Indx_.assign(numProfileLevels, -1); float Tmin = options_.RHCheck_TminInit.value(); - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if (tFlags[jlev] & ufo::MetOfficeQCFlags::Profile::TropopauseFlag) { PTrop = pressures[jlev] * 0.01; } @@ -190,15 +187,15 @@ namespace ufo { } } - void ProfileCheckRH::fillValidator() + void ProfileCheckRH::fillValidationData(ProfileDataHandler &profileDataHandler) { - profileDataHandler_.set(ufo::VariableNames::Press, std::move(Press_)); - profileDataHandler_.set(ufo::VariableNames::Temp, std::move(Temp_)); - profileDataHandler_.set(ufo::VariableNames::rh, std::move(rh_)); - profileDataHandler_.set(ufo::VariableNames::td, std::move(td_)); - profileDataHandler_.set(ufo::VariableNames::tbk, std::move(tbk_)); - profileDataHandler_.set(ufo::VariableNames::rhbk, std::move(rhbk_)); - profileDataHandler_.set(ufo::VariableNames::FlagH, std::move(FlagH_)); - profileDataHandler_.set(ufo::VariableNames::Indx, std::move(Indx_)); + profileDataHandler.set(ufo::VariableNames::Press, std::move(Press_)); + profileDataHandler.set(ufo::VariableNames::Temp, std::move(Temp_)); + profileDataHandler.set(ufo::VariableNames::rh, std::move(rh_)); + profileDataHandler.set(ufo::VariableNames::td, std::move(td_)); + profileDataHandler.set(ufo::VariableNames::tbk, std::move(tbk_)); + profileDataHandler.set(ufo::VariableNames::rhbk, std::move(rhbk_)); + profileDataHandler.set(ufo::VariableNames::FlagH, std::move(FlagH_)); + profileDataHandler.set(ufo::VariableNames::Indx, std::move(Indx_)); } } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckRH.h b/src/ufo/profile/ProfileCheckRH.h index 6677be6e0..dd0fe44ab 100644 --- a/src/ufo/profile/ProfileCheckRH.h +++ b/src/ufo/profile/ProfileCheckRH.h @@ -13,9 +13,7 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" namespace ufo { class ProfileConsistencyCheckParameters; @@ -29,16 +27,13 @@ namespace ufo { class ProfileCheckRH : public ProfileCheckBase { public: - ProfileCheckRH(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckRH(const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; + void runCheck(ProfileDataHandler &profileDataHandler) override; /// Fill variables in validator - void fillValidator() override; + void fillValidationData(ProfileDataHandler &profileDataHandler) override; private: /// Observed pressure for selected levels (hPa) diff --git a/src/ufo/profile/ProfileCheckSamePDiffT.cc b/src/ufo/profile/ProfileCheckSamePDiffT.cc index 4dd77a76b..06592caaf 100644 --- a/src/ufo/profile/ProfileCheckSamePDiffT.cc +++ b/src/ufo/profile/ProfileCheckSamePDiffT.cc @@ -12,35 +12,32 @@ namespace ufo { static ProfileCheckMaker makerProfileCheckSamePDiffT_("SamePDiffT"); - ProfileCheckSamePDiffT::ProfileCheckSamePDiffT(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator) + ProfileCheckSamePDiffT::ProfileCheckSamePDiffT(const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) {} - void ProfileCheckSamePDiffT::runCheck() + void ProfileCheckSamePDiffT::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Test for same pressure and different temperature" << std::endl; int jlevprev = -1; int NumErrors = 0; - const int numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const int numProfileLevels = profileDataHandler.getNumProfileLevels(); const std::vector &pressures = - profileDataHandler_.get(ufo::VariableNames::obs_air_pressure); + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); const std::vector &tObs = - profileDataHandler_.get(ufo::VariableNames::obs_air_temperature); + profileDataHandler.get(ufo::VariableNames::obs_air_temperature); const std::vector &tBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_air_temperature); + profileDataHandler.get(ufo::VariableNames::hofx_air_temperature); std::vector &tFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_air_temperature); + profileDataHandler.get(ufo::VariableNames::qcflags_air_temperature); std::vector &NumAnyErrors = - profileDataHandler_.get(ufo::VariableNames::counter_NumAnyErrors); + profileDataHandler.get(ufo::VariableNames::counter_NumAnyErrors); std::vector &NumSamePErrObs = - profileDataHandler_.get(ufo::VariableNames::counter_NumSamePErrObs); + profileDataHandler.get(ufo::VariableNames::counter_NumSamePErrObs); const std::vector &tObsCorrection = - profileDataHandler_.get(ufo::VariableNames::obscorrection_air_temperature); + profileDataHandler.get(ufo::VariableNames::obscorrection_air_temperature); if (!oops::allVectorsSameNonZeroSize(pressures, tObs, tBkg, tFlags, tObsCorrection)) { oops::Log::warning() << "At least one vector is the wrong size. " @@ -54,7 +51,7 @@ namespace ufo { std::vector tObsFinal; correctVector(tObs, tObsCorrection, tObsFinal); - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if (tObs[jlev] == missingValueFloat) continue; if (jlevprev == -1) { diff --git a/src/ufo/profile/ProfileCheckSamePDiffT.h b/src/ufo/profile/ProfileCheckSamePDiffT.h index c926642bb..7f7555477 100644 --- a/src/ufo/profile/ProfileCheckSamePDiffT.h +++ b/src/ufo/profile/ProfileCheckSamePDiffT.h @@ -11,9 +11,7 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" namespace ufo { class ProfileConsistencyCheckParameters; @@ -24,16 +22,10 @@ namespace ufo { /// \brief Profile QC: same P/different T check class ProfileCheckSamePDiffT : public ProfileCheckBase { public: - ProfileCheckSamePDiffT(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckSamePDiffT(const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; - - /// Fill variables in validator - void fillValidator() override {} + void runCheck(ProfileDataHandler &profileDataHandler) override; }; } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckSign.cc b/src/ufo/profile/ProfileCheckSign.cc index 4f4eb377a..617501939 100644 --- a/src/ufo/profile/ProfileCheckSign.cc +++ b/src/ufo/profile/ProfileCheckSign.cc @@ -12,51 +12,55 @@ namespace ufo { static ProfileCheckMaker makerProfileCheckSign_("Sign"); - ProfileCheckSign::ProfileCheckSign(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator) + ProfileCheckSign::ProfileCheckSign(const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) {} - void ProfileCheckSign::runCheck() + void ProfileCheckSign::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Sign check/correction" << std::endl; - const int numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const int numProfileLevels = profileDataHandler.getNumProfileLevels(); const std::vector &pressures = - profileDataHandler_.get(ufo::VariableNames::obs_air_pressure); + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); const std::vector &tObs = - profileDataHandler_.get(ufo::VariableNames::obs_air_temperature); + profileDataHandler.get(ufo::VariableNames::obs_air_temperature); const std::vector &tBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_air_temperature); - const std::vector &PstarBackgr = - profileDataHandler_.get(ufo::VariableNames::PstarBackgr); + profileDataHandler.get(ufo::VariableNames::hofx_air_temperature); std::vector &tFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_air_temperature); + profileDataHandler.get(ufo::VariableNames::qcflags_air_temperature); std::vector &NumAnyErrors = - profileDataHandler_.get(ufo::VariableNames::counter_NumAnyErrors); + profileDataHandler.get(ufo::VariableNames::counter_NumAnyErrors); std::vector &NumSignChange = - profileDataHandler_.get(ufo::VariableNames::counter_NumSignChange); + profileDataHandler.get(ufo::VariableNames::counter_NumSignChange); std::vector &tObsCorrection = - profileDataHandler_.get(ufo::VariableNames::obscorrection_air_temperature); + profileDataHandler.get(ufo::VariableNames::obscorrection_air_temperature); - if (!oops::allVectorsSameNonZeroSize(pressures, tObs, tBkg, PstarBackgr, + if (!oops::allVectorsSameNonZeroSize(pressures, tObs, tBkg, tFlags, tObsCorrection)) { oops::Log::warning() << "At least one vector is the wrong size. " << "Check will not be performed." << std::endl; oops::Log::warning() << "Vector sizes: " - << oops::listOfVectorSizes(pressures, tObs, tBkg, PstarBackgr, + << oops::listOfVectorSizes(pressures, tObs, tBkg, tFlags, tObsCorrection) << std::endl; return; } - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + // Obtain air pressure GeoVals. + const std::vector &pressureGeoVaLs = + profileDataHandler.getGeoVaLVector(ufo::VariableNames::geovals_pressure); + if (pressureGeoVaLs.empty()) + throw eckit::BadValue("Air pressure GeoVaLs vector is empty.", Here()); + + // Pstar is the pressure at the bottom of the upper-air column (following the OPS code). + const float Pstar = pressureGeoVaLs[0]; + + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { // Ignore this level if it has been flagged as rejected. if (tFlags[jlev] & ufo::MetOfficeQCFlags::Elem::FinalRejectFlag) continue; - if (pressures[jlev] <= PstarBackgr[jlev] - options_.SCheck_PstarThresh.value() && + if (pressures[jlev] <= Pstar - options_.SCheck_PstarThresh.value() && tObs[jlev] != missingValueFloat && std::abs(tObs[jlev] - tBkg[jlev]) >= options_.SCheck_tObstBkgThresh.value()) { // Change sign of tObs in C and compare to tBkg (also in C) diff --git a/src/ufo/profile/ProfileCheckSign.h b/src/ufo/profile/ProfileCheckSign.h index e9240e6b2..c0f1df76f 100644 --- a/src/ufo/profile/ProfileCheckSign.h +++ b/src/ufo/profile/ProfileCheckSign.h @@ -11,9 +11,7 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" namespace ufo { class ProfileConsistencyCheckParameters; @@ -24,16 +22,14 @@ namespace ufo { /// \brief Profile QC: sign check class ProfileCheckSign : public ProfileCheckBase { public: - ProfileCheckSign(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckSign(const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; + void runCheck(ProfileDataHandler &profileDataHandler) override; - /// Fill variables in validator - void fillValidator() override {} + /// List of names of required GeoVaLs. + oops::Variables getGeoVaLNames() override { + return oops::Variables({ufo::VariableNames::geovals_pressure});} }; } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckTime.cc b/src/ufo/profile/ProfileCheckTime.cc index f6bb33f8e..1b8dce7ee 100644 --- a/src/ufo/profile/ProfileCheckTime.cc +++ b/src/ufo/profile/ProfileCheckTime.cc @@ -5,6 +5,9 @@ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ +#include +#include + #include "ufo/profile/ProfileCheckTime.h" namespace ufo { @@ -13,37 +16,32 @@ namespace ufo { makerProfileCheckTime_("Time"); ProfileCheckTime::ProfileCheckTime - (const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator) + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) {} - void ProfileCheckTime::runCheck() + void ProfileCheckTime::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Time check" << std::endl; - const size_t numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const size_t numProfileLevels = profileDataHandler.getNumProfileLevels(); const bool ModelLevels = options_.modellevels.value(); const std::vector &ObsType = - profileDataHandler_.get(ufo::VariableNames::ObsType); + profileDataHandler.get(ufo::VariableNames::ObsType); const std::vector &level_time = - profileDataHandler_.get(ufo::VariableNames::obs_level_time); + profileDataHandler.get(ufo::VariableNames::obs_level_time); const std::vector &pressures = - profileDataHandler_.get(ufo::VariableNames::obs_air_pressure); + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); std::vector &uFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_eastward_wind); + profileDataHandler.get(ufo::VariableNames::qcflags_eastward_wind); std::vector &vFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_northward_wind); - std::vector &timeFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_time); + profileDataHandler.get(ufo::VariableNames::qcflags_northward_wind); - if (!oops::allVectorsSameNonZeroSize(ObsType, pressures)) { + if (!oops::allVectorsSameNonZeroSize(ObsType, pressures, uFlags, vFlags)) { oops::Log::warning() << "At least one vector is the wrong size. " << "Time checks will not be performed." << std::endl; oops::Log::warning() << "Vector sizes: " - << oops::listOfVectorSizes(ObsType, pressures) + << oops::listOfVectorSizes(ObsType, pressures, uFlags, vFlags) << std::endl; return; } @@ -52,11 +50,14 @@ namespace ufo { // The variable level_time is equal to the number of seconds relative to // the middle of the time window. level_time is compared to half of the // assimilation window length. - const float halfWindowLength = 0.5 * (profileDataHandler_.getObsdb().windowEnd() - - profileDataHandler_.getObsdb().windowStart()).toSeconds(); - timeFlags.assign(numLevelsToCheck, false); + const float halfWindowLength = 0.5 * (profileDataHandler.getObsdb().windowEnd() - + profileDataHandler.getObsdb().windowStart()).toSeconds(); + // Time QC flags. A value of true indicates an observation + // lies outside the time window. + // These flags are used in subsequent background checks. + std::vector timeFlags(numProfileLevels, false); if (!level_time.empty() && !ModelLevels) { - for (size_t jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (size_t jlev = 0; jlev < numProfileLevels; ++jlev) { const float leveltime = level_time[jlev]; if (leveltime == missingValueFloat) continue; timeFlags[jlev] = (leveltime < (-halfWindowLength - 0.5) || @@ -73,7 +74,7 @@ namespace ufo { (ObsType[0] != ufo::MetOfficeObsIDs::AtmosphericProfile::WindProf)) { PSurf = pressures[0]; for (size_t jlev = 0; - jlev < std::min(static_cast(numLevelsToCheck), 10); + jlev < std::min(static_cast(numProfileLevels), 10); ++jlev) { if (uFlags[jlev] & ufo::MetOfficeQCFlags::Profile::SurfaceLevelFlag) { PSurf = pressures[jlev]; @@ -86,7 +87,7 @@ namespace ufo { if (PSurf > 0.0) { int NWindRej = 0; // Number of wind levels rejected const float PLimit = PSurf - SondeLaunchWindRej * 100.0; // Convert from hPa to Pa - for (size_t jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (size_t jlev = 0; jlev < numProfileLevels; ++jlev) { if (pressures[jlev] > 0.0 && pressures[jlev] < PLimit) break; if (!uFlags.empty()) uFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::PermRejectFlag; if (!vFlags.empty()) vFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::PermRejectFlag; @@ -96,5 +97,8 @@ namespace ufo { << "Psurf = " << PSurf * 0.01 << " hPa, " << "NWindRej = " << NWindRej << std::endl; } + + // Store the time flags for use in later checks. + profileDataHandler.set(ufo::VariableNames::qcflags_time, std::move(timeFlags)); } } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckTime.h b/src/ufo/profile/ProfileCheckTime.h index a5b26b32f..ea00a2756 100644 --- a/src/ufo/profile/ProfileCheckTime.h +++ b/src/ufo/profile/ProfileCheckTime.h @@ -15,7 +15,6 @@ #include "ufo/profile/ProfileCheckBase.h" #include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" #include "ufo/utils/metoffice/MetOfficeObservationIDs.h" @@ -33,16 +32,10 @@ namespace ufo { /// Also, if requested, reject data taken a short period after the sonde launch. class ProfileCheckTime : public ProfileCheckBase { public: - ProfileCheckTime(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckTime(const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; - - /// Fill variables in validator - void fillValidator() override {} + void runCheck(ProfileDataHandler &profileDataHandler) override; }; } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckUInterp.cc b/src/ufo/profile/ProfileCheckUInterp.cc index 962359aa4..49f212735 100644 --- a/src/ufo/profile/ProfileCheckUInterp.cc +++ b/src/ufo/profile/ProfileCheckUInterp.cc @@ -13,31 +13,28 @@ namespace ufo { static ProfileCheckMaker makerProfileCheckUInterp_("UInterp"); ProfileCheckUInterp::ProfileCheckUInterp - (const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator), + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options), ProfileStandardLevels(options) {} - void ProfileCheckUInterp::runCheck() + void ProfileCheckUInterp::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " U interpolation check" << std::endl; - const int numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const int numProfileLevels = profileDataHandler.getNumProfileLevels(); const std::vector &pressures = - profileDataHandler_.get(ufo::VariableNames::obs_air_pressure); + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); const std::vector &uObs = - profileDataHandler_.get(ufo::VariableNames::obs_eastward_wind); + profileDataHandler.get(ufo::VariableNames::obs_eastward_wind); const std::vector &vObs = - profileDataHandler_.get(ufo::VariableNames::obs_northward_wind); + profileDataHandler.get(ufo::VariableNames::obs_northward_wind); std::vector &uFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_eastward_wind); + profileDataHandler.get(ufo::VariableNames::qcflags_eastward_wind); std::vector &NumSamePErrObs = - profileDataHandler_.get(ufo::VariableNames::counter_NumSamePErrObs); + profileDataHandler.get(ufo::VariableNames::counter_NumSamePErrObs); std::vector &NumInterpErrObs = - profileDataHandler_.get(ufo::VariableNames::counter_NumInterpErrObs); + profileDataHandler.get(ufo::VariableNames::counter_NumInterpErrObs); if (!oops::allVectorsSameNonZeroSize(pressures, uObs, vObs, uFlags)) { oops::Log::warning() << "At least one vector is the wrong size. " @@ -48,17 +45,17 @@ namespace ufo { return; } - calcStdLevelsUV(numLevelsToCheck, pressures, uObs, vObs, uFlags); + calcStdLevelsUV(numProfileLevels, pressures, uObs, vObs, uFlags); - LevErrors_.assign(numLevelsToCheck, -1); - uInterp_.assign(numLevelsToCheck, 0.0); - vInterp_.assign(numLevelsToCheck, 0.0); + LevErrors_.assign(numProfileLevels, -1); + uInterp_.assign(numProfileLevels, 0.0); + vInterp_.assign(numProfileLevels, 0.0); int NumErrors = 0; // Check levels with identical pressures int jlevprev = -1; - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if (uObs[jlev] != missingValueFloat && vObs[jlev] != missingValueFloat) { if (jlevprev != -1) { if (pressures[jlev] == pressures[jlevprev] && @@ -69,7 +66,8 @@ namespace ufo { NumErrors++; uFlags[jlevprev] |= ufo::MetOfficeQCFlags::Profile::InterpolationFlag; uFlags[jlev] |= ufo::MetOfficeQCFlags::Profile::InterpolationFlag; - oops::Log::debug() << " -> Wind speed interpolation check: identical P for " + oops::Log::debug() << " -> Wind speed interpolation check: identical P " + << "and significantly different wind speed magnitude for " << "levels " << jlevprev << " and " << jlev << std::endl; oops::Log::debug() << " -> Level " << jlevprev << ": " << "P = " << pressures[jlevprev] * 0.01 << "hPa, uObs = " @@ -151,18 +149,18 @@ namespace ufo { if (NumErrors > 0) NumInterpErrObs[0]++; } - void ProfileCheckUInterp::fillValidator() + void ProfileCheckUInterp::fillValidationData(ProfileDataHandler &profileDataHandler) { - profileDataHandler_.set(ufo::VariableNames::StdLev, std::move(StdLev_)); - profileDataHandler_.set(ufo::VariableNames::SigAbove, std::move(SigAbove_)); - profileDataHandler_.set(ufo::VariableNames::SigBelow, std::move(SigBelow_)); - profileDataHandler_.set(ufo::VariableNames::LevErrors, std::move(LevErrors_)); - profileDataHandler_.set(ufo::VariableNames::uInterp, std::move(uInterp_)); - profileDataHandler_.set(ufo::VariableNames::vInterp, std::move(vInterp_)); - profileDataHandler_.set(ufo::VariableNames::LogP, std::move(LogP_)); - std::vector NumStd(profileIndices_.getNumLevelsToCheck(), std::move(NumStd_)); - std::vector NumSig(profileIndices_.getNumLevelsToCheck(), std::move(NumSig_)); - profileDataHandler_.set(ufo::VariableNames::NumStd, std::move(NumStd)); - profileDataHandler_.set(ufo::VariableNames::NumSig, std::move(NumSig)); + profileDataHandler.set(ufo::VariableNames::StdLev, std::move(StdLev_)); + profileDataHandler.set(ufo::VariableNames::SigAbove, std::move(SigAbove_)); + profileDataHandler.set(ufo::VariableNames::SigBelow, std::move(SigBelow_)); + profileDataHandler.set(ufo::VariableNames::LevErrors, std::move(LevErrors_)); + profileDataHandler.set(ufo::VariableNames::uInterp, std::move(uInterp_)); + profileDataHandler.set(ufo::VariableNames::vInterp, std::move(vInterp_)); + profileDataHandler.set(ufo::VariableNames::LogP, std::move(LogP_)); + std::vector NumStd(profileDataHandler.getNumProfileLevels(), std::move(NumStd_)); + std::vector NumSig(profileDataHandler.getNumProfileLevels(), std::move(NumSig_)); + profileDataHandler.set(ufo::VariableNames::NumStd, std::move(NumStd)); + profileDataHandler.set(ufo::VariableNames::NumSig, std::move(NumSig)); } } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckUInterp.h b/src/ufo/profile/ProfileCheckUInterp.h index 2711de1af..a919b8100 100644 --- a/src/ufo/profile/ProfileCheckUInterp.h +++ b/src/ufo/profile/ProfileCheckUInterp.h @@ -13,9 +13,7 @@ #include #include "ufo/profile/ProfileCheckBase.h" -#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" #include "ufo/profile/ProfileStandardLevels.h" namespace ufo { @@ -28,16 +26,13 @@ namespace ufo { class ProfileCheckUInterp : public ProfileCheckBase, private ProfileStandardLevels { public: - ProfileCheckUInterp(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckUInterp(const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; + void runCheck(ProfileDataHandler &profileDataHandler) override; /// Fill variables in validator - void fillValidator() override; + void fillValidationData(ProfileDataHandler &profileDataHandler) override; private: /// Number of failed checks by level diff --git a/src/ufo/profile/ProfileCheckUInterpAlternative.cc b/src/ufo/profile/ProfileCheckUInterpAlternative.cc new file mode 100644 index 000000000..682e9b3f3 --- /dev/null +++ b/src/ufo/profile/ProfileCheckUInterpAlternative.cc @@ -0,0 +1,219 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/profile/ProfileCheckUInterpAlternative.h" +#include "ufo/profile/VariableNames.h" + +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/ProfileDataHolder.h" + +namespace ufo { + + static ProfileCheckMaker + makerProfileCheckUInterpAlternative_("UInterpAlternative"); + + ProfileCheckUInterpAlternative::ProfileCheckUInterpAlternative + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options), + ProfileStandardLevels(options) + {} + + void ProfileCheckUInterpAlternative::runCheck(ProfileDataHandler &profileDataHandler) + { + oops::Log::debug() << " Alternative U interpolation check" << std::endl; + + // Produce vector of profiles containing data for the alternative U interpolation check. + std::vector variableNamesInt = + {ufo::VariableNames::qcflags_eastward_wind, + ufo::VariableNames::counter_NumSamePErrObs, + ufo::VariableNames::counter_NumInterpErrObs}; + std::vector variableNamesFloat = + {ufo::VariableNames::obs_air_pressure, + ufo::VariableNames::obs_eastward_wind, + ufo::VariableNames::obs_northward_wind}; + if (options_.compareWithOPS.value()) { + variableNamesInt.insert(variableNamesInt.end(), + {ufo::VariableNames::StdLev, + ufo::VariableNames::SigAbove, + ufo::VariableNames::SigBelow, + ufo::VariableNames::LevErrors, + ufo::VariableNames::NumStd, + ufo::VariableNames::NumSig}); + variableNamesFloat.insert(variableNamesFloat.end(), + {ufo::VariableNames::uInterp, + ufo::VariableNames::vInterp, + ufo::VariableNames::LogP}); + } + + std::vector profiles = + profileDataHandler.produceProfileVector + (variableNamesInt, + variableNamesFloat, + {}, + {}, + {}); + + // Run alternative U interpolation check on each profile. + const size_t nprofs = profileDataHandler.getObsdb().nrecs(); + for (size_t jprof = 0; jprof < nprofs; ++jprof) { + oops::Log::debug() << "Profile " << (jprof + 1) << " / " << nprofs << std::endl; + auto& profile = profiles[jprof]; + runCheckOnProfile(profile); + // Fill validation information if required. + if (options_.compareWithOPS.value()) + fillValidationData(profile); + } + + // Update data handler with profile information. + oops::Log::debug() << " Updating data handler" << std::endl; + profileDataHandler.updateAllProfiles(profiles); + } + + void ProfileCheckUInterpAlternative::runCheckOnProfile(ProfileDataHolder &profile) + { + const int numProfileLevels = profile.getNumProfileLevels(); + const std::vector &pressures = + profile.get(ufo::VariableNames::obs_air_pressure); + const std::vector &uObs = + profile.get(ufo::VariableNames::obs_eastward_wind); + const std::vector &vObs = + profile.get(ufo::VariableNames::obs_northward_wind); + std::vector &uFlags = + profile.get(ufo::VariableNames::qcflags_eastward_wind); + std::vector &NumSamePErrObs = + profile.get(ufo::VariableNames::counter_NumSamePErrObs); + std::vector &NumInterpErrObs = + profile.get(ufo::VariableNames::counter_NumInterpErrObs); + + if (!oops::allVectorsSameNonZeroSize(pressures, uObs, vObs, uFlags)) { + oops::Log::warning() << "At least one vector is the wrong size. " + << "Check will not be performed." << std::endl; + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(pressures, uObs, vObs, uFlags) + << std::endl; + return; + } + + calcStdLevelsUV(numProfileLevels, pressures, uObs, vObs, uFlags); + + LevErrors_.assign(numProfileLevels, -1); + uInterp_.assign(numProfileLevels, 0.0); + vInterp_.assign(numProfileLevels, 0.0); + + int NumErrors = 0; + + // Check levels with identical pressures + int jlevprev = -1; + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { + if (uObs[jlev] != missingValueFloat && vObs[jlev] != missingValueFloat) { + if (jlevprev != -1) { + if (pressures[jlev] == pressures[jlevprev] && + (uObs[jlev] != uObs[jlevprev] || vObs[jlev] != vObs[jlevprev])) { + float VectDiffSq = std::pow(uObs[jlev] - uObs[jlevprev], 2) + + std::pow(vObs[jlev] - vObs[jlevprev], 2); + if (VectDiffSq > options_.UICheck_TInterpIdenticalPTolSq.value()) { + NumErrors++; + uFlags[jlevprev] |= ufo::MetOfficeQCFlags::Profile::InterpolationFlag; + uFlags[jlev] |= ufo::MetOfficeQCFlags::Profile::InterpolationFlag; + oops::Log::debug() << " -> Wind speed interpolation check: identical P " + << "and significantly different wind speed magnitude for " + << "levels " << jlevprev << " and " << jlev << std::endl; + oops::Log::debug() << " -> Level " << jlevprev << ": " + << "P = " << pressures[jlevprev] * 0.01 << "hPa, uObs = " + << uObs[jlevprev] << "ms^-1, vObs = " + << vObs[jlevprev] << "ms^-1" << std::endl; + oops::Log::debug() << " -> Level " << jlev << ": " + << "P = " << pressures[jlev] * 0.01 << "hPa, uObs = " + << uObs[jlev] << "ms^-1, vObs = " + << vObs[jlev] << "ms^-1" << std::endl; + oops::Log::debug() << " -> VectDiffSq = " << VectDiffSq << "m^2s^-2" << std::endl; + } + } + } + jlevprev = jlev; + } + } + + if (NumErrors > 0) NumSamePErrObs[0]++; + + if (NumSig_ < std::max(3, NumStd_ / 2)) return; // Too few sig levels for reliable check + + // Interpolation check + NumErrors = 0; + for (int jlevStd = 0; jlevStd < NumStd_; ++jlevStd) { + if (SigBelow_[jlevStd] == -1 || SigAbove_[jlevStd] == -1) continue; + int jlev = StdLev_[jlevStd]; // Standard level + int SigB = SigBelow_[jlevStd]; + int SigA = SigAbove_[jlevStd]; + float PStd = pressures[jlev]; + // BigGap - see 6.3.2.2.2 of the Guide on the Global Data-Processing System + float BigGap = options_.UICheck_BigGapLowP.value(); + const std::vector BigGaps = options_.UICheck_BigGaps.value(); + const std::vector BigGapsPThresh = options_.UICheck_BigGapsPThresh.value(); + for (size_t bgidx = 0; bgidx < BigGapsPThresh.size(); ++bgidx) { + if (PStd > BigGapsPThresh[bgidx]) { + BigGap = BigGaps[bgidx]; + break; + } + } + + if (pressures[SigB] - PStd > BigGap || + PStd - pressures[SigA] > BigGap) { + uInterp_[jlev] = missingValueFloat; + vInterp_[jlev] = missingValueFloat; + continue; + } + + if (LogP_[SigB] == LogP_[SigA]) continue; + + float Ratio = (LogP_[jlev] - LogP_[SigB]) / (LogP_[SigA] - LogP_[SigB]); // eqn 3.3a + uInterp_[jlev] = uObs[SigB] + (uObs[SigA] - uObs[SigB]) * Ratio; // eqn 3.3b + vInterp_[jlev] = vObs[SigB] + (vObs[SigA] - vObs[SigB]) * Ratio; // eqn 3.3b + + // Vector wind difference > UInterpTol m/s? + float VectDiffSq = std::pow(uObs[jlev] - uInterp_[jlev], 2) + + std::pow(vObs[jlev] - vInterp_[jlev], 2); + if (VectDiffSq > options_.UICheck_TInterpTolSq.value()) { + NumErrors++; + LevErrors_[jlev]++; + LevErrors_[SigB]++; + LevErrors_[SigA]++; + // Simplest form of flagging + uFlags[jlev] |= ufo::MetOfficeQCFlags::Profile::InterpolationFlag; + uFlags[SigB] |= ufo::MetOfficeQCFlags::Profile::InterpolationFlag; + uFlags[SigA] |= ufo::MetOfficeQCFlags::Profile::InterpolationFlag; + + oops::Log::debug() << " -> Failed wind speed interpolation check for levels " << jlev + << " (central), " << SigB << " (lower) and " + << SigA << " (upper)" << std::endl; + oops::Log::debug() << " -> Level " << jlev << ": " + << "P = " << pressures[jlev] * 0.01 << "hPa, uObs = " + << uObs[jlev] << "ms^-1, vObs = " + << vObs[jlev] << "ms^-1, uInterp = " << uInterp_[jlev] + << "ms^-1, vInterp = " << vInterp_[jlev] << "ms^-1" << std::endl; + oops::Log::debug() << " -> VectDiffSq = " << VectDiffSq << "m^2s^-2" << std::endl; + } + } + + if (NumErrors > 0) NumInterpErrObs[0]++; + } + + void ProfileCheckUInterpAlternative::fillValidationData(ProfileDataHolder &profile) + { + profile.set(ufo::VariableNames::StdLev, std::move(StdLev_)); + profile.set(ufo::VariableNames::SigAbove, std::move(SigAbove_)); + profile.set(ufo::VariableNames::SigBelow, std::move(SigBelow_)); + profile.set(ufo::VariableNames::LevErrors, std::move(LevErrors_)); + profile.set(ufo::VariableNames::uInterp, std::move(uInterp_)); + profile.set(ufo::VariableNames::vInterp, std::move(vInterp_)); + profile.set(ufo::VariableNames::LogP, std::move(LogP_)); + std::vector NumStd(profile.getNumProfileLevels(), std::move(NumStd_)); + std::vector NumSig(profile.getNumProfileLevels(), std::move(NumSig_)); + profile.set(ufo::VariableNames::NumStd, std::move(NumStd)); + profile.set(ufo::VariableNames::NumSig, std::move(NumSig)); + } +} // namespace ufo diff --git a/src/ufo/profile/ProfileCheckUInterpAlternative.h b/src/ufo/profile/ProfileCheckUInterpAlternative.h new file mode 100644 index 000000000..e1389c7f5 --- /dev/null +++ b/src/ufo/profile/ProfileCheckUInterpAlternative.h @@ -0,0 +1,58 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILECHECKUINTERPALTERNATIVE_H_ +#define UFO_PROFILE_PROFILECHECKUINTERPALTERNATIVE_H_ + +#include +#include +#include +#include + +#include "ufo/profile/ProfileCheckBase.h" +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/ProfileStandardLevels.h" + +namespace ufo { + class ProfileConsistencyCheckParameters; +} + +namespace ufo { + + /// \brief Profile QC: alternative wind speed interpolation check. + /// This check is passed all of the profiles in the sample and runs on each in sequence. + /// This check is otherwise identical to the ProfileCheckUInterp. + class ProfileCheckUInterpAlternative : public ProfileCheckBase, + private ProfileStandardLevels { + public: + explicit ProfileCheckUInterpAlternative(const ProfileConsistencyCheckParameters &options); + + /// Run check on all profiles. + void runCheck(ProfileDataHandler &profileDataHandler) override; + + /// Run check on an individual profile. + void runCheckOnProfile(ProfileDataHolder &profile); + + /// Fill variables in validator + void fillValidationData(ProfileDataHolder &profileDataHolder); + + /// Run this check on the entire sample? + bool runOnEntireSample() override {return true;} + + private: + /// Number of failed checks by level + std::vector LevErrors_; + + /// Interpolated value of u + std::vector uInterp_; + + /// Interpolated value of v + std::vector vInterp_; + }; +} // namespace ufo + +#endif // UFO_PROFILE_PROFILECHECKUINTERPALTERNATIVE_H_ diff --git a/src/ufo/profile/ProfileCheckUnstableLayer.cc b/src/ufo/profile/ProfileCheckUnstableLayer.cc index 8cb5b8fe6..a8e768cd3 100644 --- a/src/ufo/profile/ProfileCheckUnstableLayer.cc +++ b/src/ufo/profile/ProfileCheckUnstableLayer.cc @@ -14,33 +14,30 @@ namespace ufo { makerProfileCheckUnstableLayer_("UnstableLayer"); ProfileCheckUnstableLayer::ProfileCheckUnstableLayer - (const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) - : ProfileCheckBase(options, profileIndices, profileDataHandler, profileCheckValidator) + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) {} - void ProfileCheckUnstableLayer::runCheck() + void ProfileCheckUnstableLayer::runCheck(ProfileDataHandler &profileDataHandler) { oops::Log::debug() << " Unstable layer/superadiabat check" << std::endl; - const int numLevelsToCheck = profileIndices_.getNumLevelsToCheck(); + const int numProfileLevels = profileDataHandler.getNumProfileLevels(); const std::vector &pressures = - profileDataHandler_.get(ufo::VariableNames::obs_air_pressure); + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); const std::vector &tObs = - profileDataHandler_.get(ufo::VariableNames::obs_air_temperature); + profileDataHandler.get(ufo::VariableNames::obs_air_temperature); const std::vector &tBkg = - profileDataHandler_.get(ufo::VariableNames::hofx_air_temperature); + profileDataHandler.get(ufo::VariableNames::hofx_air_temperature); std::vector &tFlags = - profileDataHandler_.get(ufo::VariableNames::qcflags_air_temperature); + profileDataHandler.get(ufo::VariableNames::qcflags_air_temperature); std::vector &NumAnyErrors = - profileDataHandler_.get(ufo::VariableNames::counter_NumAnyErrors); + profileDataHandler.get(ufo::VariableNames::counter_NumAnyErrors); std::vector &NumSuperadiabat = - profileDataHandler_.get(ufo::VariableNames::counter_NumSuperadiabat); + profileDataHandler.get(ufo::VariableNames::counter_NumSuperadiabat); const std::vector &tObsCorrection = - profileDataHandler_.get(ufo::VariableNames::obscorrection_air_temperature); + profileDataHandler.get(ufo::VariableNames::obscorrection_air_temperature); if (!oops::allVectorsSameNonZeroSize(pressures, tObs, tBkg, tFlags, tObsCorrection)) { oops::Log::warning() << "At least one vector is the wrong size. " @@ -57,7 +54,7 @@ namespace ufo { PBottom_ = 0.0; int jlevprev = 0; - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { // Ignore this level if it has been flagged as rejected. if (tFlags[jlev] & ufo::MetOfficeQCFlags::Elem::FinalRejectFlag) continue; if (tObsFinal[jlev] != missingValueFloat && @@ -96,9 +93,9 @@ namespace ufo { } } - void ProfileCheckUnstableLayer::fillValidator() + void ProfileCheckUnstableLayer::fillValidationData(ProfileDataHandler &profileDataHandler) { - std::vector PBottom(profileIndices_.getNumLevelsToCheck(), PBottom_); - profileDataHandler_.set(ufo::VariableNames::PBottom, std::move(PBottom)); + std::vector PBottom(profileDataHandler.getNumProfileLevels(), PBottom_); + profileDataHandler.set(ufo::VariableNames::PBottom, std::move(PBottom)); } } // namespace ufo diff --git a/src/ufo/profile/ProfileCheckUnstableLayer.h b/src/ufo/profile/ProfileCheckUnstableLayer.h index 84e03f553..2f40a1451 100644 --- a/src/ufo/profile/ProfileCheckUnstableLayer.h +++ b/src/ufo/profile/ProfileCheckUnstableLayer.h @@ -14,7 +14,6 @@ #include "ufo/profile/ProfileCheckBase.h" #include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" -#include "ufo/profile/ProfileIndices.h" namespace ufo { class ProfileConsistencyCheckParameters; @@ -25,16 +24,13 @@ namespace ufo { /// \brief Profile QC: unstable layer check class ProfileCheckUnstableLayer : public ProfileCheckBase { public: - ProfileCheckUnstableLayer(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileCheckUnstableLayer(const ProfileConsistencyCheckParameters &options); /// Run check - void runCheck() override; + void runCheck(ProfileDataHandler &profileDataHandler) override; /// Fill variables in validator - void fillValidator() override; + void fillValidationData(ProfileDataHandler &profileDataHandler) override; private: /// PBottom diff --git a/src/ufo/profile/ProfileCheckValidator.cc b/src/ufo/profile/ProfileCheckValidator.cc index 659d060e2..54bc9ab9a 100644 --- a/src/ufo/profile/ProfileCheckValidator.cc +++ b/src/ufo/profile/ProfileCheckValidator.cc @@ -20,10 +20,8 @@ #include "ufo/utils/StringUtils.h" namespace ufo { - ProfileCheckValidator::ProfileCheckValidator(const ProfileConsistencyCheckParameters &options, - ProfileDataHandler &profileDataHandler) - : options_(options), - profileDataHandler_(profileDataHandler) + ProfileCheckValidator::ProfileCheckValidator(const ProfileConsistencyCheckParameters &options) + : options_(options) { // Set offsets due to C++ and Fortran array index starting values comparison_offsets_[ufo::VariableNames::StdLev] = 1; @@ -86,7 +84,7 @@ namespace ufo { valuesToCompare_float_.insert({ ufo::VariableNames::DC, ufo::VariableNames::ETol}); - } else if (check == "UInterp") { + } else if (check == "UInterp" || check == "UInterpAlternative") { valuesToCompare_int_.insert({ ufo::VariableNames::counter_NumSamePErrObs, ufo::VariableNames::counter_NumInterpErrObs, @@ -145,8 +143,42 @@ namespace ufo { ufo::VariableNames::pgebd_air_temperature, ufo::VariableNames::pgebd_relative_humidity, ufo::VariableNames::pgebd_eastward_wind, - ufo::VariableNames::pgebd_northward_wind, - ufo::VariableNames::pgebd_geopotential_height}); + ufo::VariableNames::pgebd_northward_wind}); + } else if (check == "Pressure") { + valuesToCompare_int_.insert({ + ufo::VariableNames::qcflags_observation_report}); + valuesToCompare_float_.insert({ + ufo::VariableNames::obs_air_pressure}); + } else if (check == "AveragePressure") { + valuesToCompare_float_.insert({ + ufo::VariableNames::LogP_derived, + ufo::VariableNames::bigPgaps_derived, + ufo::VariableNames::modellevels_logP_derived, + ufo::VariableNames::modellevels_ExnerP_derived, + ufo::VariableNames::modellevels_logP_rho_derived, + ufo::VariableNames::modellevels_ExnerP_rho_derived}); + } else if (check == "AverageTemperature") { + valuesToCompare_int_.insert({ + ufo::VariableNames::modellevels_average_air_temperature_qcflags, + ufo::VariableNames::counter_NumGapsT}); + valuesToCompare_float_.insert({ + ufo::VariableNames::modellevels_air_temperature_derived, + ufo::VariableNames::modellevels_average_air_temperature_derived}); + } else if (check == "AverageWindSpeed") { + valuesToCompare_int_.insert({ + ufo::VariableNames::modellevels_average_eastward_wind_qcflags, + ufo::VariableNames::modellevels_average_northward_wind_qcflags, + ufo::VariableNames::counter_NumGapsU, + ufo::VariableNames::counter_NumGapsUWP}); + valuesToCompare_float_.insert({ + ufo::VariableNames::modellevels_average_eastward_wind_derived, + ufo::VariableNames::modellevels_average_northward_wind_derived}); + } else if (check == "AverageRelativeHumidity") { + valuesToCompare_int_.insert({ + ufo::VariableNames::modellevels_average_relative_humidity_qcflags, + ufo::VariableNames::counter_NumGapsRH}); + valuesToCompare_float_.insert({ + ufo::VariableNames::modellevels_average_relative_humidity_derived}); } } } @@ -197,7 +229,8 @@ namespace ufo { } } - void ProfileCheckValidator::validate() + void ProfileCheckValidator::validate(ProfileDataHandler &profileDataHandler, + size_t commSize) { oops::Log::debug() << " Comparing values against OPS equivalents..." << std::endl; @@ -222,9 +255,9 @@ namespace ufo { // Obtain values for comparison const std::vector &values_thiscode = - profileDataHandler_.get(valueToCompare_int); + profileDataHandler.get(valueToCompare_int); const std::vector &values_OPS = - profileDataHandler_.get(varname_OPS); + profileDataHandler.get(varname_OPS); // Account for potential offset between values in this code and OPS int offset = 0; @@ -242,9 +275,19 @@ namespace ufo { // Only the first element of each counter is compared; // in all other cases tne entire vectors are compared. if (groupname == "Counters") { - if (!oops::anyVectorEmpty(values_OPS, values_thiscode)) - compareOutput(valueToCompare_int, values_OPS[0], values_thiscode[0], - offset, tol, nMismatches_); + // The counter comparison is only performed if there is one processor. + // With some refactoring it would be possible to use eckit::allGatherv + // to sum the results over multiple processors but, at present, + // if two processors have a different number of profiles then the allGatherv + // routine on the processor with more profiles will hang indefinitely. + if (commSize == 1) { + if (!oops::anyVectorEmpty(values_OPS, values_thiscode)) + compareOutput(valueToCompare_int, values_OPS[0], values_thiscode[0], + offset, tol, nMismatches_); + } else { + oops::Log::debug() << "The counter comparison was not performed " + << "because multiple processors are in use." << std::endl; + } } else { compareOutput(valueToCompare_int, values_OPS, values_thiscode, offset, tol, nMismatches_); @@ -258,9 +301,9 @@ namespace ufo { // Compare float values obtained in this code and OPS for (const auto& valueToCompare_float : valuesToCompare_float_) { const std::vector &values_thiscode = - profileDataHandler_.get(valueToCompare_float); + profileDataHandler.get(valueToCompare_float); const std::vector &values_OPS = - profileDataHandler_.get("OPS_" + valueToCompare_float); + profileDataHandler.get("OPS_" + valueToCompare_float); compareOutput(valueToCompare_float, values_OPS, values_thiscode, 0, tol, nMismatches_); } diff --git a/src/ufo/profile/ProfileCheckValidator.h b/src/ufo/profile/ProfileCheckValidator.h index b166c072d..99caaf26a 100644 --- a/src/ufo/profile/ProfileCheckValidator.h +++ b/src/ufo/profile/ProfileCheckValidator.h @@ -26,11 +26,11 @@ namespace ufo { /// the equivalent values produced in the OPS code. class ProfileCheckValidator { public: - ProfileCheckValidator(const ProfileConsistencyCheckParameters &options, - ProfileDataHandler &profileDataHandler); + explicit ProfileCheckValidator(const ProfileConsistencyCheckParameters &options); /// Validate check results against OPS values. - void validate(); + void validate(ProfileDataHandler &profileDataHandler, + size_t commSize); /// Get number of mismatches between values produced in this code and the OPS equivalents. int getMismatches() const {return nMismatches_;} @@ -63,9 +63,6 @@ namespace ufo { /// Configurable parameters. const ProfileConsistencyCheckParameters &options_; - /// Profile data handler. - ProfileDataHandler &profileDataHandler_; - /// Counters that are accumulated across profiles. std::map cumulativeCounters_; diff --git a/src/ufo/profile/ProfileChecker.cc b/src/ufo/profile/ProfileChecker.cc index 18c013a20..8fee0072b 100644 --- a/src/ufo/profile/ProfileChecker.cc +++ b/src/ufo/profile/ProfileChecker.cc @@ -6,6 +6,8 @@ */ #include +#include +#include #include "eckit/exception/Exceptions.h" @@ -25,14 +27,8 @@ #include "ufo/utils/metoffice/MetOfficeQCFlags.h" namespace ufo { - ProfileChecker::ProfileChecker(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator) + ProfileChecker::ProfileChecker(const ProfileConsistencyCheckParameters &options) : options_(options), - profileIndices_(profileIndices), - profileDataHandler_(profileDataHandler), - profileCheckValidator_(profileCheckValidator), checks_(options.Checks.value()) { // Ensure basic checks are always performed first @@ -44,29 +40,73 @@ namespace ufo { // If "Basic" is present but not at the start, move it there std::rotate(checks_.begin(), it_checks, it_checks + 1); } + + // Produce check subgroups. + // A subgroup is the longest sequence of consecutive checks which have + // the same mode of operation. + bool isFirst = true; // First check considered; used to initialise checkMode. + bool checkMode = true; // Mode of operation of the current check. + std::vector checkNames; // Filled anew for each subgroup. + // Also fill a set of any required GeoVaL names. + for (const auto& check : checks_) { + // Instantiate each check and check its mode of operation. + std::unique_ptr profileCheck = + ProfileCheckFactory::create(check, + options_); + if (profileCheck) { + if (isFirst) { + checkMode = profileCheck->runOnEntireSample(); + isFirst = false; + } + if (profileCheck->runOnEntireSample() == checkMode) { + checkNames.push_back(check); + } else { + checkSubgroups_.push_back({checkMode, checkNames}); + checkNames.clear(); + checkNames.push_back(check); + // Invert checkMode whenever a check with a different mode is reached. + checkMode = !checkMode; + } + GeoVaLNames_ += profileCheck->getGeoVaLNames(); + validationGeoVaLNames_ += profileCheck->getValidationGeoVaLNames(); + obsDiagNames_ += profileCheck->getObsDiagNames(); + } else { + throw eckit::NotImplemented("Have not implemented a check for " + check, Here()); + } + } + // Fill checkSubgroups with the final list to be produced. + checkSubgroups_.push_back({checkMode, checkNames}); } - void ProfileChecker::runChecks() + void ProfileChecker::runChecks(ProfileDataHandler &profileDataHandler, + const CheckSubgroup &subGroupChecks) { // Run all checks requested - for (const auto& check : checks_) { + for (const auto& check : subGroupChecks.checkNames) { std::unique_ptr profileCheck = ProfileCheckFactory::create(check, - options_, - profileIndices_, - profileDataHandler_, - profileCheckValidator_); + options_); if (profileCheck) { - profileCheck->runCheck(); - // Fill validation information if required - if (options_.compareWithOPS.value()) { - profileCheck->fillValidator(); - } - // Do not proceed if basic checks failed - if (!profileCheck->getResult() && check == "Basic") { - oops::Log::debug() << "Basic checks failed" << std::endl; - setBasicCheckResult(false); - break; + // Ensure correct type of check has been requested. + if (profileCheck->runOnEntireSample() == subGroupChecks.runOnEntireSample) { + // For checks on the entire sample, reset profile indices + // prior to looping through the profiles + if (profileCheck->runOnEntireSample()) + profileDataHandler.resetProfileIndices(); + // Run check + profileCheck->runCheck(profileDataHandler); + // Actions taken if a single profile was processed. + if (!profileCheck->runOnEntireSample()) { + // Fill validation information if required + if (options_.compareWithOPS.value()) + profileCheck->fillValidationData(profileDataHandler); + // Do not proceed if basic checks failed + if (!profileCheck->getResult() && check == "Basic") { + oops::Log::debug() << "Basic checks failed" << std::endl; + setBasicCheckResult(false); + break; + } + } } } else { throw eckit::NotImplemented("Have not implemented a check for " + check, Here()); diff --git a/src/ufo/profile/ProfileChecker.h b/src/ufo/profile/ProfileChecker.h index ac53bbe0c..6574f2415 100644 --- a/src/ufo/profile/ProfileChecker.h +++ b/src/ufo/profile/ProfileChecker.h @@ -11,30 +11,39 @@ #include #include #include +#include #include #include "ufo/filters/ProfileConsistencyCheckParameters.h" namespace ufo { - class ProfileCheckValidator; class ProfileDataHandler; - class ProfileIndices; } namespace ufo { + /// Information on each subgroup of checks. + struct CheckSubgroup { + /// \p runOnEntireSample specifies whether the checks in this subgroup run on all + /// profiles at once. + bool runOnEntireSample; + /// \p checkNames contains the names of the checks in this subgroup. + std::vector checkNames; + }; + /// \brief Profile QC checker /// /// Runs the various QC checks on individual profiles and modifies flags accordingly. class ProfileChecker { public: - ProfileChecker(const ProfileConsistencyCheckParameters &options, - const ProfileIndices &profileIndices, - ProfileDataHandler &profileDataHandler, - ProfileCheckValidator &profileCheckValidator); + explicit ProfileChecker(const ProfileConsistencyCheckParameters &options); + + /// Type for container of check subgroups. + typedef std::vector CheckSubgroupList; /// Run all checks requested - void runChecks(); + void runChecks(ProfileDataHandler &profileDataHandler, + const CheckSubgroup &subGroupChecks); /// Get basic check result bool getBasicCheckResult() {return basicCheckResult_;} @@ -42,24 +51,39 @@ namespace ufo { /// Set basic check result void setBasicCheckResult(bool result) {basicCheckResult_ = result;} - private: - /// Configurable parameters - const ProfileConsistencyCheckParameters &options_; + /// Get container of check subgroups + CheckSubgroupList getCheckSubgroups() {return checkSubgroups_;} + + /// Get vector of GeoVaL names for all checks. + oops::Variables getGeoVaLNames() const {return GeoVaLNames_;} - /// Indices of profile's observations in the entire sample - const ProfileIndices &profileIndices_; + /// Get vector of validation GeoVaL names for all checks. + oops::Variables getValidationGeoVaLNames() const {return validationGeoVaLNames_;} - /// Profile data - ProfileDataHandler &profileDataHandler_; + /// Get vector of obs diagnostic names for all checks. + oops::Variables getObsDiagNames() const {return obsDiagNames_;} - /// Profile check validator - ProfileCheckValidator &profileCheckValidator_; + private: + /// Configurable parameters + const ProfileConsistencyCheckParameters &options_; /// Checks to perform std::vector checks_; /// Basic check result bool basicCheckResult_ = true; + + /// Subgroups of checks with the same mode of operation. + CheckSubgroupList checkSubgroups_; + + /// Names of all required GeoVaLs. + oops::Variables GeoVaLNames_; + + /// Names of all validation GeoVaLs. + oops::Variables validationGeoVaLNames_; + + /// Names of all required obs diagnostics. + oops::Variables obsDiagNames_; }; } // namespace ufo diff --git a/src/ufo/profile/ProfileDataHandler.cc b/src/ufo/profile/ProfileDataHandler.cc index 03967f448..f599a6792 100644 --- a/src/ufo/profile/ProfileDataHandler.cc +++ b/src/ufo/profile/ProfileDataHandler.cc @@ -5,23 +5,62 @@ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ +#include "oops/util/missingValues.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsDiagnostics.h" + +#include "ufo/profile/ProfileCheckBase.h" #include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/ProfileDataHolder.h" #include "ufo/profile/VariableNames.h" namespace ufo { - ProfileDataHandler::ProfileDataHandler(ioda::ObsSpace &obsdb, + ProfileDataHandler::ProfileDataHandler(const ObsFilterData &data, const DataHandlerParameters &options, - EntireSampleDataHandler &entireSampleDataHandler, - const ProfileIndices &profileIndices) - : obsdb_(obsdb), + const std::vector &apply, + const Variables &filtervars, + std::vector> &flagged) + : obsdb_(data.obsspace()), + geovals_(data.getGeoVaLs()), + obsdiags_(data.getObsDiags()), options_(options), - entireSampleDataHandler_(entireSampleDataHandler), - profileIndices_(profileIndices) - {} + filtervars_(filtervars), + flagged_(flagged) + { + profileIndices_.reset(new ProfileIndices(obsdb_, options, apply)); + entireSampleDataHandler_.reset(new EntireSampleDataHandler(obsdb_, options)); + } - void ProfileDataHandler::reset() + void ProfileDataHandler::resetProfileInformation() { profileData_.clear(); + GeoVaLData_.clear(); + obsDiagData_.clear(); + } + + void ProfileDataHandler::initialiseNextProfile() + { + resetProfileInformation(); + profileIndices_->updateNextProfileIndices(); + } + + void ProfileDataHandler::updateProfileInformation() + { + // Set final report flags in this profile. + setFinalReportFlags(); + + // Modify 'flagged' vector for each filter variable based on check results. + setFlagged(); + + // If any variables in the current profile were modified by the checks, + // the equivalent variables in the entire sample are set to the modified values. + updateEntireSampleData(); + } + + void ProfileDataHandler::writeQuantitiesToObsdb() + { + entireSampleDataHandler_->writeQuantitiesToObsdb(); } void ProfileDataHandler::getProfileIndicesInEntireSample(const std::string& groupname) @@ -31,16 +70,17 @@ namespace ufo { // If the number of entries per profile was not specified, use the indices // that were obtained by sorting and grouping the record numbers. if (entriesPerProfile == 0) { - profileIndicesInEntireSample_ = profileIndices_.getProfileIndices(); + profileIndicesInEntireSample_ = profileIndices_->getProfileIndices(); } else { // Otherwise increment the indices sequentially, starting at the // relevant position. profileIndicesInEntireSample_.resize(entriesPerProfile); std::iota(profileIndicesInEntireSample_.begin(), profileIndicesInEntireSample_.end(), - profileIndices_.getProfileNumCurrent() * entriesPerProfile); + profileIndices_->getProfileNumCurrent() * entriesPerProfile); } } + void ProfileDataHandler::updateEntireSampleData() { for (const auto &it_profile : profileData_) { @@ -49,19 +89,29 @@ namespace ufo { std::string groupname; ufo::splitVarGroup(fullname, varname, groupname); - if (groupname == "QCFlags" || fullname == ufo::VariableNames::counter_NumAnyErrors) { - getProfileIndicesInEntireSample(groupname); + if (groupname == "QCFlags" || + groupname == "ModelLevelsFlags" || + groupname == "ModelLevelsQCFlags" || + groupname == "ModelRhoLevelsFlags" || + groupname == "Counters") { const std::vector & profileData = get(fullname); - std::vector & entireSampleData = entireSampleDataHandler_.get(fullname); + getProfileIndicesInEntireSample(groupname); + std::vector & entireSampleData = entireSampleDataHandler_->get(fullname); size_t idx = 0; for (const auto& profileIndex : profileIndicesInEntireSample_) { updateValueIfPresent(profileData, idx, entireSampleData, profileIndex); idx++; } - } else if (groupname == "Corrections") { - getProfileIndicesInEntireSample(groupname); + } else if (groupname == "Corrections" || + groupname == "DerivedValue" || + groupname == "GrossErrorProbability" || + groupname == "GrossErrorProbabilityBuddyCheck" || + groupname == "ModelLevelsDerivedValue" || + groupname == "ModelRhoLevelsDerivedValue" || + fullname == ufo::VariableNames::obs_air_pressure) { const std::vector & profileData = get(fullname); - std::vector & entireSampleData = entireSampleDataHandler_.get(fullname); + getProfileIndicesInEntireSample(groupname); + std::vector & entireSampleData = entireSampleDataHandler_->get(fullname); size_t idx = 0; for (const auto& profileIndex : profileIndicesInEntireSample_) { updateValueIfPresent(profileData, idx, entireSampleData, profileIndex); @@ -84,8 +134,7 @@ namespace ufo { } } - void ProfileDataHandler::setFlagged(const size_t nvars, - std::vector> &flagged) + void ProfileDataHandler::setFlagged() { oops::Log::debug() << "Flagging observations" << std::endl; @@ -98,29 +147,150 @@ namespace ufo { if (groupname == "QCFlags") { oops::Log::debug() << " " << fullname << std::endl; + // Obtain QC flags const std::vector &Flags = get(fullname); + if (Flags.empty()) continue; getProfileIndicesInEntireSample(groupname); - size_t idx = 0; + // Determine index of varname in the filter variables. + // If it is not present then the variable will not be flagged individually. + size_t idxvar = filtervars_.size(); + for (size_t idx = 0; idx < idxvar; ++idx) { + if (filtervars_[idx].variable() == varname) { + idxvar = idx; + break; + } + } + + // If varname is observation_report then all filter variables will be rejected. + bool isObservationReport = varname == "observation_report"; + + // Index of elements in this profile. + size_t idxprof = 0; + // Loop over indices of elements in entire profile sample. for (const auto& profileIndex : profileIndicesInEntireSample_) { - // Please note this concise code relies on both FlagsElem::FinalRejectFlag - // and FlagsWholeObReport::FinalRejectReport being equal to the same value - // (as is the case in OPS). - // If one or both values change, and clash with another flag in the enum, - // this will have to be rewritten. - if (!Flags.empty() && - (Flags[idx] & ufo::MetOfficeQCFlags::Elem::FinalRejectFlag || - Flags[idx] & ufo::MetOfficeQCFlags::WholeObReport::FinalRejectReport)) { - oops::Log::debug() << " " << profileIndex << std::endl; - // Flag all variables - for (size_t jv = 0; jv < nvars; ++jv) - flagged[jv][profileIndex] = true; + // Flag all filter variables if the whole observation has been rejected. + if (isObservationReport && + Flags[idxprof] & ufo::MetOfficeQCFlags::WholeObReport::FinalRejectReport) { + oops::Log::debug() << " Reject all variables, index " << profileIndex << std::endl; + for (size_t jvar = 0; jvar < filtervars_.size(); ++jvar) + flagged_[jvar][profileIndex] = true; + // Move to next element in the profile. + idxprof++; + continue; } - idx++; + // Flag variable if its specific value has been rejected. + if (idxvar < filtervars_.size() && + Flags[idxprof] & ufo::MetOfficeQCFlags::Elem::FinalRejectFlag) { + oops::Log::debug() << " Reject " << varname + << ", index " << profileIndex << std::endl; + flagged_[idxvar][profileIndex] = true; + } + idxprof++; } } } } -} // namespace ufo + std::vector & ProfileDataHandler::getGeoVaLVector(const std::string &variableName) + { + auto it_GeoVaLData = GeoVaLData_.find(variableName); + if (it_GeoVaLData != GeoVaLData_.end()) { + // If the GeoVaL vector is already present, return it. + return it_GeoVaLData->second; + } else { + std::vector vec_GeoVaL_column; + // Only fill the GeoVaL vector if the required GeoVaLs are present + // and there is at least one observation location. + if (geovals_ && + obsdb_.nlocs() > 0 && + geovals_->has(variableName)) { + // Location at which to retrieve the GeoVaL. + // This assumes each model column for each observation in a profile is identical + // so takes the first entry in each case. + // todo(ctgh): this is an approximation that should be revisited + // when considering horizontal drift. + const size_t jloc = profileIndices_->getProfileIndices()[0]; + // Vector storing GeoVaL data for current profile. + vec_GeoVaL_column.assign(geovals_->nlevs(variableName), 0.0); + // Get GeoVaLs at the specified location. + geovals_->getAtLocation(vec_GeoVaL_column, variableName, jloc); + } + // Add GeoVaL vector to map (even if it is empty). + GeoVaLData_.emplace(variableName, std::move(vec_GeoVaL_column)); + return GeoVaLData_[variableName]; + } + } + + std::vector & ProfileDataHandler::getObsDiag(const std::string &fullname) + { + auto it_obsDiagData = obsDiagData_.find(fullname); + if (it_obsDiagData != obsDiagData_.end()) { + // If the ObsDiag vector is already present, return it. + return it_obsDiagData->second; + } else { + std::string varname; + std::string groupname; + ufo::splitVarGroup(fullname, varname, groupname); + std::vector vec_ObsDiag; + // Attempt to retrieve variable vector from entire sample. + // If it is not present, the vector will remain empty. + std::vector &vec_all = entireSampleDataHandler_->get(fullname); + // If the vector is empty, attempt to fill it from the ObsDiags + // (if they are present and have the required variable). + if (vec_all.empty() && + obsdiags_ && + obsdb_.nlocs() > 0 && + obsdiags_->has(varname)) { + vec_all.assign(obsdb_.nlocs(), util::missingValue(1.0f)); + obsdiags_->get(vec_all, varname); + } + // If the ObsDiags vector for the entire sample is not empty, + // fill the values for this profile. + if (!vec_all.empty()) { + getProfileIndicesInEntireSample(groupname); + for (const auto& profileIndex : profileIndicesInEntireSample_) + vec_ObsDiag.emplace_back(vec_all[profileIndex]); + } + // Add ObsDiag vector to map (even if it is empty). + obsDiagData_.emplace(fullname, std::move(vec_ObsDiag)); + return obsDiagData_[fullname]; + } + } + + std::vector ProfileDataHandler::produceProfileVector + (const std::vector &variableNamesInt, + const std::vector &variableNamesFloat, + const std::vector &variableNamesString, + const std::vector &variableNamesGeoVaLs, + const std::vector &variableNamesObsDiags) + { + profileIndices_->reset(); + std::vector profiles; + oops::Log::debug() << "Filling vector of profiles" << std::endl; + for (size_t jprof = 0; jprof < obsdb_.nrecs(); ++jprof) { + initialiseNextProfile(); + ProfileDataHolder profile(*this); + profile.fill(variableNamesInt, + variableNamesFloat, + variableNamesString, + variableNamesGeoVaLs, + variableNamesObsDiags); + profiles.emplace_back(profile); + } + return profiles; + } + void ProfileDataHandler::updateAllProfiles(std::vector &profiles) + { + this->resetProfileIndices(); + for (size_t jprof = 0; jprof < obsdb_.nrecs(); ++jprof) { + this->initialiseNextProfile(); + auto& profile = profiles[jprof]; + // Move values from profile to this object. + profile.moveValuesToHandler(); + // Update information, including the 'flagged' vector, for this profile. + this->updateProfileInformation(); + } + } +} // namespace ufo diff --git a/src/ufo/profile/ProfileDataHandler.h b/src/ufo/profile/ProfileDataHandler.h index 311eebb3e..25386210a 100644 --- a/src/ufo/profile/ProfileDataHandler.h +++ b/src/ufo/profile/ProfileDataHandler.h @@ -8,6 +8,7 @@ #ifndef UFO_PROFILE_PROFILEDATAHANDLER_H_ #define UFO_PROFILE_PROFILEDATAHANDLER_H_ +#include #include #include #include @@ -21,6 +22,9 @@ #include "oops/util/CompareNVectors.h" #include "oops/util/missingValues.h" +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/Variables.h" + #include "ufo/profile/DataHandlerParameters.h" #include "ufo/profile/EntireSampleDataHandler.h" #include "ufo/profile/ProfileIndices.h" @@ -32,6 +36,12 @@ namespace ioda { class ObsSpace; } +namespace ufo { + class GeoVaLs; + class ObsDiagnostics; + class ProfileDataHolder; +} + namespace ufo { /// \brief Retrieve and store data for individual profiles. @@ -39,10 +49,11 @@ namespace ufo { /// then the relevant data corresponding to this profile are extracted. class ProfileDataHandler { public: - ProfileDataHandler(ioda::ObsSpace &obsdb, + ProfileDataHandler(const ObsFilterData &data, const DataHandlerParameters &options, - EntireSampleDataHandler &entireSampleDataHandler, - const ProfileIndices &profileIndices); + const std::vector &apply, + const Variables &filtervars, + std::vector> &flagged); /// Retrieve a vector containing the requested variable for the current profile. /// -# If the variable has previously been placed in a vector, return the vector. @@ -52,27 +63,26 @@ namespace ufo { template std::vector& get(const std::string &fullname) { - // Determine variable and group names, optional, and number of entries per profile. + // Determine variable and group names std::string varname; std::string groupname; ufo::splitVarGroup(fullname, varname, groupname); - const bool optional = options_.getOptional(groupname); - const size_t entriesPerProfile = options_.getEntriesPerProfile(groupname); - if (profileData_.find(fullname) != profileData_.end()) { + auto it_profileData = profileData_.find(fullname); + if (it_profileData != profileData_.end()) { // If the vector is already present, return it. // If the type T is incorrect then boost::get will return an exception; // provide additional information if that occurs. try { - return boost::get> (profileData_[fullname]); + return boost::get> (it_profileData->second); } catch (boost::bad_get) { - throw eckit::BadParameter("Template parameter passed to boost::get" - " probably has the wrong type", Here()); + throw eckit::BadParameter("Template parameter passed to boost::get for " + + fullname + " probably has the wrong type", Here()); } } else { std::vector vec_prof; // Vector storing data for current profile. // Retrieve variable vector from entire sample. - const std::vector &vec_all = entireSampleDataHandler_.get(fullname); + const std::vector &vec_all = entireSampleDataHandler_->get(fullname); // Only proceed if the vector is not empty. if (!vec_all.empty()) { getProfileIndicesInEntireSample(groupname); @@ -88,32 +98,83 @@ namespace ufo { /// Directly set a vector for the current profile. /// Typically used to store variables that are used locally in checks /// (e.g. intermediate values). + /// Also initialise a vector in the entire sample, allowing the data to + /// be stored between checks. template void set(const std::string &fullname, std::vector &&vec_in) { + // Determine variable and group names + std::string varname; + std::string groupname; + ufo::splitVarGroup(fullname, varname, groupname); // Check whether vector is already in map. - if (profileData_.find(fullname) != profileData_.end()) { + auto it_profileData = profileData_.find(fullname); + if (it_profileData != profileData_.end()) { // Replace vector in map. - profileData_[fullname] = vec_in; + it_profileData->second = std::move(vec_in); } else { // Add vector to map. - profileData_.emplace(fullname, vec_in); + profileData_.emplace(fullname, std::move(vec_in)); + } + entireSampleDataHandler_->initialiseVector(fullname); + // Transfer this profile's data into the entire sample. + getProfileIndicesInEntireSample(groupname); + std::vector & entireSampleData = entireSampleDataHandler_->get(fullname); + const std::vector & profileData = this->get(fullname); + size_t idx = 0; + for (const auto& profileIndex : profileIndicesInEntireSample_) { + updateValueIfPresent(profileData, idx, entireSampleData, profileIndex); + idx++; } } + /// Initialise the next profile prior to applying checks. + /// Clears \p profileData_ and determines the \p profileIndices_ for the next profile. + void initialiseNextProfile(); + + /// Update information for this profile. + /// This function calls three other functions which take the following actions: + /// 1. Set final report flags in this profile, + /// 2. Modify 'flagged' vector for each filter variable based on check results, + /// 3. If any variables in the current profile were modified by the checks, + /// the equivalent variables in the entire sample are set to the modified values. + void updateProfileInformation(); + + /// Write various quantities to the obsdb so they can be used in future QC checks. + /// Use the method in EntireSampleDataHandler to do this. + void writeQuantitiesToObsdb(); + + /// Return obsdb + ioda::ObsSpace &getObsdb() {return obsdb_;} + + /// Return number of levels to which QC checks should be applied. + int getNumProfileLevels() const {return profileIndices_->getNumProfileLevels();} + + /// Get GeoVaLs for a particular profile. + std::vector & getGeoVaLVector(const std::string &variableName); + + /// Get ObsDiags for a particular profile. + std::vector & getObsDiag(const std::string &variableName); + + /// Reset profile indices (required if it is desired to loop through + /// the entire sample again). + void resetProfileIndices() {profileIndices_->reset();} + + /// Produce a vector of all profiles, loading the requested variables into each one. + std::vector produceProfileVector + (const std::vector &variableNamesInt, + const std::vector &variableNamesFloat, + const std::vector &variableNamesString, + const std::vector &variableNamesGeoVaLs, + const std::vector &variableNamesObsDiags); + + /// Read values from a collection of profiles and update information related to each one. + void updateAllProfiles(std::vector &profiles); + + private: // functions /// Reset profile information (vectors and corresponding names). /// This should be called every time a new profile will be retrieved. - void reset(); - - /// Transfer values from one vector to another (as long as neither is empty). - template - void updateValueIfPresent(const std::vector &vecIn, const size_t &idxIn, - std::vector &vecOut, const size_t &idxOut) - { - // Ensure neither vector is empty. - if (oops::anyVectorEmpty(vecIn, vecOut)) return; - vecOut[idxOut] = vecIn[idxIn]; - } + void resetProfileInformation(); /// If any variables in the current profile were modified by the checks, /// the equivalent variables in the entire sample are set to the modified values. @@ -127,30 +188,55 @@ namespace ufo { /// Update the 'flagged' vector based on any flags that may have changed during the checks. /// The QC flag group is hardocded but this could be changed to /// a configurable value if required. - void setFlagged(const size_t nvars, std::vector> &flagged); + void setFlagged(); + + /// Transfer values from one vector to another (as long as neither is empty). + template + void updateValueIfPresent(const std::vector &vecIn, const size_t &idxIn, + std::vector &vecOut, const size_t &idxOut) + { + // Ensure neither vector is empty. + if (oops::anyVectorEmpty(vecIn, vecOut)) return; + vecOut[idxOut] = vecIn[idxIn]; + } /// Get indices in entire sample corresponding to current profile. void getProfileIndicesInEntireSample(const std::string& groupname); - /// Return obsdb - ioda::ObsSpace &getObsdb() {return obsdb_;} - - private: + private: // members /// Container of each variable in the current profile. std::unordered_map , std::vector , std::vector >> profileData_; + /// Container of GeoVaLs in the current profile. + std::unordered_map > GeoVaLData_; + + /// Container of ObsDiags in the current profile. + std::unordered_map > obsDiagData_; + /// Observation database. ioda::ObsSpace &obsdb_; + /// GeoVaLs loaded by the filter. + const GeoVaLs* const geovals_; + + /// ObsDiags loaded by the filter. + const ObsDiagnostics* const obsdiags_; + /// Configurable parameters. const DataHandlerParameters &options_; + /// Filter variables + const Variables &filtervars_; + + /// Flagged values + std::vector> &flagged_; + /// Class that handles the entire data sample. - EntireSampleDataHandler &entireSampleDataHandler_; + std::unique_ptr entireSampleDataHandler_; /// Class that handles profile indices. - const ProfileIndices &profileIndices_; + std::unique_ptr profileIndices_; /// Indices in the entire data sample that correspond to the current profile. std::vector profileIndicesInEntireSample_; diff --git a/src/ufo/profile/ProfileDataHolder.cc b/src/ufo/profile/ProfileDataHolder.cc new file mode 100644 index 000000000..847129a06 --- /dev/null +++ b/src/ufo/profile/ProfileDataHolder.cc @@ -0,0 +1,94 @@ +/* + * (C) Copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include + +#include "ufo/profile/ProfileDataHolder.h" + +namespace ufo { + ProfileDataHolder::ProfileDataHolder(ProfileDataHandler &profileDataHandler) + : profileDataHandler_(profileDataHandler) + { + numProfileLevels_ = profileDataHandler_.getNumProfileLevels(); + } + + void ProfileDataHolder::fill(const std::vector &variableNamesInt, + const std::vector &variableNamesFloat, + const std::vector &variableNamesString, + const std::vector &variableNamesGeoVaLs, + const std::vector &variableNamesObsDiags) + { + variableNamesInt_ = variableNamesInt; + variableNamesFloat_ = variableNamesFloat; + variableNamesString_ = variableNamesString; + variableNamesGeoVaLs_ = variableNamesGeoVaLs; + variableNamesObsDiags_ = variableNamesObsDiags; + + for (const auto& variable : variableNamesInt_) + profileData_.emplace(variable, profileDataHandler_.get(variable)); + for (const auto& variable : variableNamesFloat_) + profileData_.emplace(variable, profileDataHandler_.get(variable)); + for (const auto& variable : variableNamesString_) + profileData_.emplace(variable, profileDataHandler_.get(variable)); + for (const auto& variable : variableNamesGeoVaLs_) + profileGeoVaLs_.emplace(variable, profileDataHandler_.getGeoVaLVector(variable)); + for (const auto& variable : variableNamesObsDiags_) + profileObsDiags_.emplace(variable, profileDataHandler_.getObsDiag(variable)); + } + + std::vector & ProfileDataHolder::getGeoVaLVector(const std::string &fullname) + { + auto it_profileGeoVaLs = profileGeoVaLs_.find(fullname); + if (it_profileGeoVaLs != profileGeoVaLs_.end()) { + return it_profileGeoVaLs->second; + } else { + throw eckit::BadValue("GeoVaL " + fullname + " not present in profile. " + "Please add it to the relevant argument in the call " + "to produceProfileVector()", Here()); + } + } + + std::vector & ProfileDataHolder::getObsDiagVector(const std::string &fullname) + { + auto it_profileObsDiags = profileObsDiags_.find(fullname); + if (it_profileObsDiags != profileObsDiags_.end()) { + return it_profileObsDiags->second; + } else { + throw eckit::BadValue("ObsDiag " + fullname + " not present in profile. " + "Please add it to the relevant argument in the call " + "to produceProfileVector()", Here()); + } + } + + void ProfileDataHolder::moveValuesToHandler() + { + for (const auto& variable : variableNamesInt_) + profileDataHandler_.set(variable, std::move(this->get(variable))); + for (const auto& variable : variableNamesFloat_) + profileDataHandler_.set(variable, std::move(this->get(variable))); + for (const auto& variable : variableNamesString_) + profileDataHandler_.set(variable, std::move(this->get(variable))); + profileData_.clear(); + } + + void ProfileDataHolder::checkObsSpaceSection(ufo::ObsSpaceSection section) + { + // If extended_obs_space is not present this will throw an exception. + const auto &extended_obs_space = this->get(ufo::VariableNames::extended_obs_space); + if (section == ufo::ObsSpaceSection::Original && + std::find(extended_obs_space.begin(), + extended_obs_space.end(), 1) != extended_obs_space.end()) + throw eckit::BadValue("This profile is expected to be in the original ObsSpace " + "but has been labelled as being in the extended ObsSpace.", Here()); + if (section == ufo::ObsSpaceSection::Extended && + std::find(extended_obs_space.begin(), + extended_obs_space.end(), 0) != extended_obs_space.end()) + throw eckit::BadValue("This profile is expected to be in the extended ObsSpace " + "but has been labelled as being in the original ObsSpace.", Here()); + } +} // namespace ufo diff --git a/src/ufo/profile/ProfileDataHolder.h b/src/ufo/profile/ProfileDataHolder.h new file mode 100644 index 000000000..97ffc7a7c --- /dev/null +++ b/src/ufo/profile/ProfileDataHolder.h @@ -0,0 +1,131 @@ +/* + * (C) Copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILEDATAHOLDER_H_ +#define UFO_PROFILE_PROFILEDATAHOLDER_H_ + +#include +#include +#include +#include + +#include "boost/variant.hpp" + +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/VariableNames.h" + +namespace ufo { + + enum class ObsSpaceSection {Original, Extended}; + + /// \brief Profile data holder class. + /// + /// \details Stores data for a single profile. + /// Each profile is filled with a user-specified list of variables that are transferred + /// from an associated ProfileDataHandler. + /// A collection of ProfileDataHolders can be used in checks that act on the entire + /// sample of profiles at once (unlike checks that act on each profile individually, + /// in which case the ProfileDataHandler can be used). + /// This class can also be used to modify the values in the associated ProfileDataHandler, + /// which is necessary when updating values in the entire sample. + class ProfileDataHolder { + public: + explicit ProfileDataHolder(ProfileDataHandler &profileDataHandler); + + /// Fill profile with data. + void fill(const std::vector &variableNamesInt, + const std::vector &variableNamesFloat, + const std::vector &variableNamesString, + const std::vector &variableNamesGeoVaLs, + const std::vector &variableNamesObsDiags); + + /// Retrieve a vector if it is present. If not, throw an exception. + template + std::vector & get(const std::string &fullname) + { + auto it_profileData = profileData_.find(fullname); + if (it_profileData != profileData_.end()) { + // If the type T is incorrect then boost::get will return an exception; + // provide additional information if that occurs. + try { + return boost::get> (it_profileData->second); + } catch (boost::bad_get) { + throw eckit::BadParameter("Template parameter passed to boost::get for " + + fullname + " probably has the wrong type", Here()); + } + } else { + throw eckit::BadValue("Variable " + fullname + " not present in profile. " + "Please add it to the relevant argument in the call " + "to produceProfileVector()", Here()); + } + } + + /// Retrieve a GeoVaL vector if it is present. If not, throw an exception. + std::vector & getGeoVaLVector(const std::string& fullname); + + /// Retrieve an ObsDiag vector if it is present. If not, throw an exception. + std::vector & getObsDiagVector(const std::string& fullname); + + /// Set values in a vector. + template + void set(const std::string &fullname, std::vector &&vec_in) + { + // Check whether vector is already in map. + auto it_profileData = profileData_.find(fullname); + if (it_profileData != profileData_.end()) { + // Replace vector in map. + it_profileData->second = std::move(vec_in); + } else { + // Add vector to map. + profileData_.emplace(fullname, std::move(vec_in)); + } + } + + /// Get number of profile levels for this profile. + int getNumProfileLevels() const {return static_cast(numProfileLevels_);} + + /// Move all values to the associated ProfileDataHandler. + void moveValuesToHandler(); + + /// Check this profile is in the expected ObsSpace section (original or extended). + void checkObsSpaceSection(ufo::ObsSpaceSection section); + + private: + /// Number of profile levels + std::size_t numProfileLevels_; + + /// Container of each variable in the current profile. + std::unordered_map , std::vector , std::vector >> profileData_; + + /// Container of GeoVaLs in the current profile. + std::unordered_map > profileGeoVaLs_; + + /// Container of ObsDiags in the current profile. + std::unordered_map > profileObsDiags_; + + /// Profile data handler + ProfileDataHandler &profileDataHandler_; + + /// Names of int variables + std::vector variableNamesInt_; + + /// Names of float variables + std::vector variableNamesFloat_; + + /// Names of string variables + std::vector variableNamesString_; + + /// Names of GeoVaLs + std::vector variableNamesGeoVaLs_; + + /// Names of ObsDiags + std::vector variableNamesObsDiags_; + }; +} // namespace ufo + +#endif // UFO_PROFILE_PROFILEDATAHOLDER_H_ diff --git a/src/ufo/profile/ProfileIndices.cc b/src/ufo/profile/ProfileIndices.cc index 24517ead2..18e85f9fa 100644 --- a/src/ufo/profile/ProfileIndices.cc +++ b/src/ufo/profile/ProfileIndices.cc @@ -12,23 +12,38 @@ namespace ufo { ProfileIndices::ProfileIndices(ioda::ObsSpace &obsdb, - const ProfileConsistencyCheckParameters &options, + const DataHandlerParameters &options, const std::vector &apply) : obsdb_(obsdb), options_(options), apply_(apply), - profileNums_(obsdb.recnum()), - profileNumCurrent_(0), - profileNumToFind_(0), - profIndex_(0) + profileNums_(obsdb.recnum()) { + this->reset(); + + // Determine unique profile numbers. + uniqueProfileNums_.insert(profileNums_.begin(), profileNums_.end()); + // If not sorting observations, ensure number of profiles is consistent // with quantity reported by obsdb. // (If sorting is imposed, nothing is assumed about the ordering of the input data // so this validation should not be performed.) - if (obsdb_.obs_sort_var().empty() && options_.ValidateTotalNumProf.value()) { + if (!profileNums_.empty() && + obsdb_.obs_sort_var().empty() && + options_.ValidateTotalNumProf.value()) { validateTotalNumProf(); } + } + + void ProfileIndices::reset() + { + // Do not proceed if there are no profiles on the current processor. + if (profileNums_.empty()) + return; + + profileNumCurrent_ = profileNums_[0]; + profileNumToFind_ = profileNums_[0]; + profIndex_ = 0; // If sorting observations, point to beginning of record index iterator if (!obsdb_.obs_sort_var().empty() && @@ -37,12 +52,12 @@ namespace ufo { } } - void ProfileIndices::determineProfileIndices() + void ProfileIndices::updateNextProfileIndices() { profileIndices_.clear(); - // If there are no profiles in the sample, warn and exit - if (profileNums_.size() == 0) { + // Do not proceed if there are no profiles on the current processor. + if (profileNums_.empty()) { oops::Log::debug() << "No profiles found in the sample" << std::endl; return; } @@ -78,16 +93,16 @@ namespace ufo { } // Number of levels to which QC checks should be applied - numLevelsToCheck_ = static_cast (profileIndices_.size()); + numProfileLevels_ = static_cast (profileIndices_.size()); - if (numLevelsToCheck_ > 0) { + if (numProfileLevels_ > 0) { oops::Log::debug() << "First and last profile indices: " << profileIndices_.front() << ", " << profileIndices_.back() << std::endl; } // Replace with maxlev if defined (a legacy of the OPS code) if (options_.maxlev.value() != boost::none) { - numLevelsToCheck_ = std::min(options_.maxlev.value().get(), numLevelsToCheck_); + numProfileLevels_ = std::min(options_.maxlev.value().get(), numProfileLevels_); } // Update counters and iterators (if used) @@ -107,14 +122,20 @@ namespace ufo { } } + size_t ProfileIndices::getProfileNumCurrent() const + { + const auto &it = uniqueProfileNums_.find(profileNumCurrent_); + return std::distance(uniqueProfileNums_.begin(), it); + } + void ProfileIndices::validateTotalNumProf() { // If no sorting is performed on the observations it is possible that // two separate profiles could (inadvertently) have the same value of the group variable. // This would lead to an incorrect value of nrecs. // This routine ensures that nrecs is the same as the actual number of profiles in the sample. - size_t profNum = 0; - std::vector allProfileNums = {0}; + size_t profNum = profileNums_[0]; + std::vector allProfileNums = {profNum}; for (size_t j = 0; j < profileNums_.size(); ++j) { size_t profNum_j = profileNums_[j]; if (profNum_j != profNum) { diff --git a/src/ufo/profile/ProfileIndices.h b/src/ufo/profile/ProfileIndices.h index 9fc32564e..19388a693 100644 --- a/src/ufo/profile/ProfileIndices.h +++ b/src/ufo/profile/ProfileIndices.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,7 +19,7 @@ #include "ioda/ObsDataVector.h" #include "ioda/ObsSpace.h" -#include "ufo/filters/ProfileConsistencyCheckParameters.h" +#include "ufo/profile/DataHandlerParameters.h" namespace ioda { class ObsSpace; @@ -39,29 +40,26 @@ namespace ufo { class ProfileIndices { public: ProfileIndices(ioda::ObsSpace &obsdb, - const ProfileConsistencyCheckParameters &options, + const DataHandlerParameters &options, const std::vector &apply); - /// Determine indices in entire sample for this profile. - void determineProfileIndices(); + /// Determine indices in entire sample for the next profile. + void updateNextProfileIndices(); /// Return indices for the current profile. const std::vector &getProfileIndices() const {return profileIndices_;} /// Return number of levels to which QC checks should be applied. - int getNumLevelsToCheck() const {return numLevelsToCheck_;} + int getNumProfileLevels() const {return numProfileLevels_;} - /// Get number of current profile. - size_t getProfileNumCurrent() const {return profileNumCurrent_;} + /// Get number of current profile, accounting for distribution across processors. + size_t getProfileNumCurrent() const; - /// Profile index map. - typedef std::map> ProfIdxMap; - - /// Profile index map iterator. - typedef ProfIdxMap::const_iterator ProfIdxIter; + /// Reset profile indices to point to the beginning of the sample. + void reset(); private: // functions - // Ensure number of profiles is consistent with quantity reported by obsdb. + /// Ensure number of profiles is consistent with quantity reported by obsdb. void validateTotalNumProf(); private: // variables @@ -69,7 +67,7 @@ namespace ufo { ioda::ObsSpace &obsdb_; /// Configurable parameters. - const ProfileConsistencyCheckParameters &options_; + const DataHandlerParameters &options_; /// Observations to apply the filter to. const std::vector &apply_; @@ -77,6 +75,15 @@ namespace ufo { /// Profile numbers for the entire sample. const std::vector profileNums_; + /// Unique profile numbers for the entire sample. + std::set uniqueProfileNums_; + + /// Profile index map. + typedef std::map> ProfIdxMap; + + /// Profile index map iterator. + typedef ProfIdxMap::const_iterator ProfIdxIter; + /// Iterator over profile indices (used for sorting). ProfIdxMap profidx_; @@ -87,7 +94,7 @@ namespace ufo { std::vector profileIndices_; /// Number of profile levels to which QC checks should be applied. - int numLevelsToCheck_; + int numProfileLevels_; /// Current profile number in the sample. size_t profileNumCurrent_; diff --git a/src/ufo/profile/ProfilePressure.cc b/src/ufo/profile/ProfilePressure.cc new file mode 100644 index 000000000..b10a7a1a5 --- /dev/null +++ b/src/ufo/profile/ProfilePressure.cc @@ -0,0 +1,131 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/profile/ProfilePressure.h" + +namespace ufo { + + static ProfileCheckMaker + makerProfilePressure_("Pressure"); + + ProfilePressure::ProfilePressure + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) + {} + + void ProfilePressure::runCheck(ProfileDataHandler &profileDataHandler) + { + oops::Log::debug() << " Pressure calculations" << std::endl; + + const int numProfileLevels = profileDataHandler.getNumProfileLevels(); + + // Retrieve the observed geopotential height and associated metadata. + std::vector &zObs = + profileDataHandler.get(ufo::VariableNames::obs_geopotential_height); + const std::vector &ObsType = + profileDataHandler.get(ufo::VariableNames::ObsType); + std::vector &ReportFlags = + profileDataHandler.get(ufo::VariableNames::qcflags_observation_report); + + if (!oops::allVectorsSameNonZeroSize(zObs, ObsType, ReportFlags)) { + oops::Log::warning() << "At least one vector is the wrong size. " + << "Profile pressure routine will not run." << std::endl; + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(zObs, ObsType, ReportFlags) + << std::endl; + return; + } + + // Retrieve the model background fields. + const std::vector &orogGeoVaLs = + profileDataHandler.getGeoVaLVector(ufo::VariableNames::geovals_orog); + const std::vector &pressureGeoVaLs = + profileDataHandler.getGeoVaLVector(ufo::VariableNames::geovals_pressure); + + if (!oops::allVectorsSameNonZeroSize(orogGeoVaLs, pressureGeoVaLs)) { + oops::Log::warning() << "At least one GeoVaLs vector is the wrong size. " + << "Profile pressure routine will not run." << std::endl; + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(orogGeoVaLs, pressureGeoVaLs) + << std::endl; + return; + } + + // Retrive the vector of observed pressures. + std::vector &pressures = + profileDataHandler.get(ufo::VariableNames::obs_air_pressure); + // If pressures have not been recorded, initialise the vector with missing values. + if (pressures.empty()) + pressures.assign(numProfileLevels, missingValueFloat); + + // Determine whether the instrument that recorded this profile had a pressure sensor. + const bool ObsHasNoPressureSensor = + (ObsType[0] == MetOfficeObsIDs::AtmosphericProfile::PilotLand || + ObsType[0] == MetOfficeObsIDs::AtmosphericProfile::PilotShip || + ObsType[0] == MetOfficeObsIDs::AtmosphericProfile::PilotMobile || + ObsType[0] == MetOfficeObsIDs::AtmosphericProfile::WindProf || + ObsType[0] == MetOfficeObsIDs::AtmosphericProfile::Pilot); + + // Determine whether all levels have a valid pressure. + const int NumValidPLevels = std::count_if(pressures.begin(), pressures.end(), + [](float PLev){return PLev >= 0;}); + bool AllLevelsHaveValidP = NumValidPLevels == numProfileLevels; + // Allow a small amount of flexibility for BUFR sondes. + if (ObsType[0] == MetOfficeObsIDs::AtmosphericProfile::Sonde || + ObsType[0] == MetOfficeObsIDs::AtmosphericProfile::TSTSonde) { + AllLevelsHaveValidP = NumValidPLevels >= std::max(numProfileLevels - 2, + static_cast(round(numProfileLevels + * 0.98))); + } + + // Set a QC flag for wind profilers and for BUFR sondes for which not + // all levels have a valid pressure. + if (ObsType[0] == MetOfficeObsIDs::AtmosphericProfile::WindProf || + (!AllLevelsHaveValidP && (ObsType[0] == MetOfficeObsIDs::AtmosphericProfile::Sonde || + ObsType[0] == MetOfficeObsIDs::AtmosphericProfile::TSTSonde))) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { + ReportFlags[jlev] |= MetOfficeQCFlags::WholeObReport::NoPressureSensor; + } + } + + // Determine whether any values of geopotential height are missing. + const int NumValidZLevels = std::count_if(zObs.begin(), zObs.end(), + [](float ZLev){return ZLev >= 0.0;}); + const bool AllLevelsHaveValidZ = NumValidZLevels == numProfileLevels; + + // Override AllLevelsHaveValidP if the observation has been flagged as having no pressure sensor + // either in this routine or in an earlier one. + // In this case the pressures will be (re)calculated from the geopotential heights. + // This aims to improved the accuracy of the reported pressures if they are already present. + if (AllLevelsHaveValidP && + ReportFlags[0] & MetOfficeQCFlags::WholeObReport::NoPressureSensor && + AllLevelsHaveValidZ) { + AllLevelsHaveValidP = false; + } + + // Perform this for instruments without a pressure sensor + // that do not have a valid pressure on all levels. + // In all other cases assume pressure is already fine. + if (ObsHasNoPressureSensor && + !AllLevelsHaveValidP) { + // Compute model heights on rho and theta levels. + // todo(ctgh): This could be performed when retrieving the GeoVaLs. + // zThetaGeoVaLs are not used further in this routine but do appear elsewhere in the code. + std::vector zRhoGeoVaLs; + std::vector zThetaGeoVaLs; + ufo::CalculateModelHeight(options_.DHParameters.ModParameters, + orogGeoVaLs[0], + zRhoGeoVaLs, + zThetaGeoVaLs); + // Compute observation pressures based on vertical interpolation from model heights. + ufo::profileVerticalInterpolation(zRhoGeoVaLs, + pressureGeoVaLs, + zObs, + pressures); + } + } +} // namespace ufo diff --git a/src/ufo/profile/ProfilePressure.h b/src/ufo/profile/ProfilePressure.h new file mode 100644 index 000000000..13611c052 --- /dev/null +++ b/src/ufo/profile/ProfilePressure.h @@ -0,0 +1,55 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILEPRESSURE_H_ +#define UFO_PROFILE_PROFILEPRESSURE_H_ + +#include +#include +#include +#include + +#include "ufo/profile/ModelHeightCalculator.h" +#include "ufo/profile/ProfileCheckBase.h" +#include "ufo/profile/ProfileCheckValidator.h" +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/ProfileVerticalInterpolation.h" + +#include "ufo/utils/metoffice/MetOfficeObservationIDs.h" + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + class ProfileConsistencyCheckParameters; +} + +namespace ufo { + + /// \brief Profile QC: calculates values of pressure that + /// have not been reported due to the lack of a pressure sensor + /// (e.g. for PILOT sondes and wind profilers). + /// The missing pressures are calculated using the reported heights + /// and linearly-interpolated model background fields. + /// Profiles without a pressure sensor are flagged. + class ProfilePressure : public ProfileCheckBase { + public: + explicit ProfilePressure(const ProfileConsistencyCheckParameters &options); + + /// Run check + void runCheck(ProfileDataHandler &profileDataHandler) override; + + /// List of names of required GeoVaLs. + oops::Variables getGeoVaLNames() override { + return oops::Variables({ufo::VariableNames::geovals_orog, + ufo::VariableNames::geovals_pressure}); + } + }; +} // namespace ufo + +#endif // UFO_PROFILE_PROFILEPRESSURE_H_ diff --git a/src/ufo/profile/ProfileSondeFlags.cc b/src/ufo/profile/ProfileSondeFlags.cc new file mode 100644 index 000000000..f4a75741a --- /dev/null +++ b/src/ufo/profile/ProfileSondeFlags.cc @@ -0,0 +1,112 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/profile/ProfileSondeFlags.h" + +namespace ufo { + + static ProfileCheckMaker + makerProfileSondeFlags_("SondeFlags"); + + ProfileSondeFlags::ProfileSondeFlags + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) + {} + + void ProfileSondeFlags::runCheck(ProfileDataHandler &profileDataHandler) + { + oops::Log::debug() << " Set sonde QC Flags" << std::endl; + + const int numProfileLevels = profileDataHandler.getNumProfileLevels(); + + std::vector &tFlags = + profileDataHandler.get(ufo::VariableNames::qcflags_air_temperature); + std::vector &rhFlags = + profileDataHandler.get(ufo::VariableNames::qcflags_relative_humidity); + std::vector &uFlags = + profileDataHandler.get(ufo::VariableNames::qcflags_eastward_wind); + std::vector &vFlags = + profileDataHandler.get(ufo::VariableNames::qcflags_northward_wind); + const std::vector &ObsType = + profileDataHandler.get(ufo::VariableNames::ObsType); + const std::vector &LevelType = + profileDataHandler.get(ufo::VariableNames::LevelType); + + if (!oops::allVectorsSameNonZeroSize(tFlags, rhFlags, uFlags, vFlags, + ObsType, LevelType)) { + oops::Log::warning() << "At least one vector is the wrong size. " + << "Check will not be performed." << std::endl; + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(tFlags, rhFlags, uFlags, vFlags, + ObsType, LevelType) + << std::endl; + return; + } + + // Do not perform for wind profilers. + if (ObsType[0] == ufo::MetOfficeObsIDs::AtmosphericProfile::WindProf) + return; + + // Check whether BUFR data or not and set flags to check accordingly. + const bool isBUFR = (ObsType[0] == ufo::MetOfficeObsIDs::AtmosphericProfile::Sonde || + ObsType[0] == ufo::MetOfficeObsIDs::AtmosphericProfile::TSTSonde); + const int IBSigWind = isBUFR ? ufo::MetOfficeQCFlags::Sounding::BUFRSigWind : + ufo::MetOfficeQCFlags::Sounding::TEMPSigWind; + const int IBSigTemp = isBUFR ? ufo::MetOfficeQCFlags::Sounding::BUFRSigTemp : + ufo::MetOfficeQCFlags::Sounding::TEMPSigTemp; + const int IBMaxWind = isBUFR ? ufo::MetOfficeQCFlags::Sounding::BUFRMaxWind : + ufo::MetOfficeQCFlags::Sounding::TEMPMaxWind; + const int IBTropopause = isBUFR ? ufo::MetOfficeQCFlags::Sounding::BUFRTropopause : + ufo::MetOfficeQCFlags::Sounding::TEMPTropopause; + const int IBStandard = isBUFR ? ufo::MetOfficeQCFlags::Sounding::BUFRStandard : + ufo::MetOfficeQCFlags::Sounding::TEMPStandard; + const int IBStandardX = isBUFR ? ufo::MetOfficeQCFlags::Sounding::BUFRStandardX : + ufo::MetOfficeQCFlags::Sounding::TEMPStandardX; + const int IBSurface = isBUFR ? ufo::MetOfficeQCFlags::Sounding::BUFRSurface : + ufo::MetOfficeQCFlags::Sounding::TEMPSurface; + + // Set flags on each level. + for (size_t jlev = 0; jlev < numProfileLevels; ++jlev) { + if (LevelType[jlev] == missingValueInt) continue; + + // Surface level + if (LevelType[jlev] & IBSurface) { + SetQCFlag(ufo::MetOfficeQCFlags::Profile::SurfaceLevelFlag, jlev, + tFlags, rhFlags, uFlags, vFlags); + } + + // Standard level + if (LevelType[jlev] & IBStandard || LevelType[jlev] & IBStandardX) { + SetQCFlag(ufo::MetOfficeQCFlags::Profile::StandardLevelFlag, jlev, + tFlags, rhFlags, uFlags, vFlags); + } + + // Tropopause + if (LevelType[jlev] & IBTropopause) { + SetQCFlag(ufo::MetOfficeQCFlags::Profile::TropopauseFlag, jlev, + tFlags, rhFlags, uFlags, vFlags); + } + + // Maximum wind level + if (LevelType[jlev] & IBMaxWind) { + SetQCFlag(ufo::MetOfficeQCFlags::Profile::MaxWindLevelFlag, jlev, + uFlags, vFlags); + } + + // Significant temperature level + if (LevelType[jlev] & IBSigTemp) { + tFlags[jlev] |= ufo::MetOfficeQCFlags::SigTempLevelFlag; + } + + // Significant wind level + if (LevelType[jlev] & IBSigWind) { + SetQCFlag(ufo::MetOfficeQCFlags::Profile::SigWindLevelFlag, jlev, + uFlags, vFlags); + } + } + } +} // namespace ufo diff --git a/src/ufo/profile/ProfileSondeFlags.h b/src/ufo/profile/ProfileSondeFlags.h new file mode 100644 index 000000000..ac37e443b --- /dev/null +++ b/src/ufo/profile/ProfileSondeFlags.h @@ -0,0 +1,50 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILESONDEFLAGS_H_ +#define UFO_PROFILE_PROFILESONDEFLAGS_H_ + +#include +#include +#include + +#include "ufo/profile/ProfileCheckBase.h" +#include "ufo/profile/ProfileCheckValidator.h" +#include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/ProfileIndices.h" + +#include "ufo/utils/metoffice/MetOfficeObservationIDs.h" + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + class ProfileConsistencyCheckParameters; +} + +namespace ufo { + + /// \brief Sets level indicator flags for radiosondes. + /// The code specifying the level type is checked, and the appropriate flags set. + /// Note that the values given are mutually exclusive e.g. if a + /// significant level temperature is present, a wind will not be present + /// at the same level number. If both significant winds and temperatures are + /// reported, they will occur with different level numbers i.e. + /// Level No. Pressure Temperature Wind + /// 10 800.0 -5.0 missing + /// 11 800.0 missing 50.0 knots, 270 degs + class ProfileSondeFlags : public ProfileCheckBase { + public: + explicit ProfileSondeFlags(const ProfileConsistencyCheckParameters &options); + + /// Run check + void runCheck(ProfileDataHandler &profileDataHandler) override; + }; +} // namespace ufo + +#endif // UFO_PROFILE_PROFILESONDEFLAGS_H_ diff --git a/src/ufo/profile/ProfileStandardLevels.cc b/src/ufo/profile/ProfileStandardLevels.cc index fbeba4362..4893e45dd 100644 --- a/src/ufo/profile/ProfileStandardLevels.cc +++ b/src/ufo/profile/ProfileStandardLevels.cc @@ -15,7 +15,7 @@ namespace ufo { BigGaps_ = optionsSL_.BigGaps.value(); } - void ProfileStandardLevels::calcStdLevels(const int numLevelsToCheck, + void ProfileStandardLevels::calcStdLevels(const int numProfileLevels, const std::vector &pressures, const std::vector &tObs, const std::vector &tFlags) @@ -25,18 +25,18 @@ namespace ufo { // Reset calculated values NumSig_ = 0; NumStd_ = 0; - StdLev_.assign(numLevelsToCheck, -1); - SigBelow_.assign(numLevelsToCheck, -1); - SigAbove_.assign(numLevelsToCheck, -1); - LogP_.assign(numLevelsToCheck, 0.0); - IndStd_.assign(numLevelsToCheck, -1); + StdLev_.assign(numProfileLevels, -1); + SigBelow_.assign(numProfileLevels, -1); + SigAbove_.assign(numProfileLevels, -1); + LogP_.assign(numProfileLevels, 0.0); + IndStd_.assign(numProfileLevels, -1); /// Missing value (float) const float missingValueFloat = util::missingValue(1.0f); int SigPrev = -1; // Previous significant level int jlevStdA = 0; // Standard level below previous significant level - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { // Ignore this level if it has been flagged as rejected. if (tFlags[jlev] & ufo::MetOfficeQCFlags::Elem::FinalRejectFlag) continue; if (tObs[jlev] != missingValueFloat && @@ -94,7 +94,7 @@ namespace ufo { Ind100_ = std::distance(StandardLevels_.begin(), it100.base()) - 1; } - void ProfileStandardLevels::calcStdLevelsUV(const int numLevelsToCheck, + void ProfileStandardLevels::calcStdLevelsUV(const int numProfileLevels, const std::vector &pressures, const std::vector &uObs, const std::vector &vObs, @@ -105,10 +105,10 @@ namespace ufo { // Reset calculated values NumSig_ = 0; NumStd_ = 0; - StdLev_.assign(numLevelsToCheck, -1); - SigBelow_.assign(numLevelsToCheck, -1); - SigAbove_.assign(numLevelsToCheck, -1); - LogP_.assign(numLevelsToCheck, 0.0); + StdLev_.assign(numProfileLevels, -1); + SigBelow_.assign(numProfileLevels, -1); + SigAbove_.assign(numProfileLevels, -1); + LogP_.assign(numProfileLevels, 0.0); /// Missing value (float) const float missingValueFloat = util::missingValue(1.0f); @@ -116,7 +116,7 @@ namespace ufo { int SigPrev = -1; // Previous significant level int jlevStdA = 0; // Standard level below previous significant level - for (int jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { if (uObs[jlev] != missingValueFloat && vObs[jlev] != missingValueFloat) { LogP_[jlev] = std::log(pressures[jlev]); if (uFlags[jlev] & ufo::MetOfficeQCFlags::Profile::StandardLevelFlag) { // Standard level diff --git a/src/ufo/profile/ProfileStandardLevels.h b/src/ufo/profile/ProfileStandardLevels.h index 7d78e8532..706a1408b 100644 --- a/src/ufo/profile/ProfileStandardLevels.h +++ b/src/ufo/profile/ProfileStandardLevels.h @@ -35,7 +35,7 @@ namespace ufo { protected: // functions /// Calculate standard levels - void calcStdLevels(const int numLevelsToCheck, + void calcStdLevels(const int numProfileLevels, const std::vector &pressures, const std::vector &tObs, const std::vector &tFlags); @@ -44,7 +44,7 @@ namespace ufo { void findHCheckStdLevs(); /// Calculate standard levels for U and V data - void calcStdLevelsUV(const int numLevelsToCheck, + void calcStdLevelsUV(const int numProfileLevels, const std::vector &pressures, const std::vector &uObs, const std::vector &vObs, diff --git a/src/ufo/profile/ProfileVerticalAveraging.cc b/src/ufo/profile/ProfileVerticalAveraging.cc new file mode 100644 index 000000000..604679eef --- /dev/null +++ b/src/ufo/profile/ProfileVerticalAveraging.cc @@ -0,0 +1,222 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include + +#include "eckit/exception/Exceptions.h" + +#include "oops/util/CompareNVectors.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" +#include "oops/util/PropertiesOfNVectors.h" + +#include "ufo/profile/ProfileVerticalAveraging.h" + +#include "ufo/utils/metoffice/MetOfficeQCFlags.h" + +namespace ufo { + void calculateVerticalAverage(const std::vector &flagsIn, + const std::vector &valuesIn, + const std::vector &coordIn, + const std::vector &bigGap, + const std::vector &coordOut, + float DZFrac, + ProfileAveraging::Method method, + std::vector &flagsOut, + std::vector &valuesOut, + int &numGaps, + std::vector *coordMax, + std::vector *coordMin) + { + const float missingValueFloat = util::missingValue(missingValueFloat); + const size_t numRepLev = coordIn.size(); + const size_t numInterp = coordOut.size(); + const size_t numOut = method == ProfileAveraging::Method::Interpolation ? + numInterp : numInterp - 1; // Number of output levels. + + // Coordinates in ascending order? + const bool Ascending = coordOut[0] < coordOut[numInterp - 1]; + + // Make local copies of the vertical coordinates in order to allow them to be reversed. + std::vector ZIn = coordIn; + std::vector ZOut = coordOut; + + // Multiply coordinates by -1 if ascending. + if (Ascending) { + std::transform(ZIn.begin(), ZIn.end(), ZIn.begin(), + std::bind(std::multiplies(), std::placeholders::_1, -1)); + std::transform(ZOut.begin(), ZOut.end(), ZOut.begin(), + std::bind(std::multiplies(), std::placeholders::_1, -1)); + } + + // Initialise coordMin and coordMax, which record the minimum and maximum + // coordinates of the values used in the model layer average. + if (coordMin) coordMin->assign(numOut, missingValueFloat); + if (coordMax) coordMax->assign(numOut, missingValueFloat); + + // Note observation levels with data that are useful for averaging. + + // Interpolated/averaged values. + std::vector valuesInterp(numInterp, missingValueFloat); + // Indices of useful data. + std::vector idxUsefulLevels; + // True if there is a big gap relative to the previous level. + std::vector bigGapWithPreviousLevel(numRepLev + 1, false); + // Previous value of JLev. + size_t JLevP = 0; + for (size_t JLev = 0; JLev < numRepLev; ++JLev) { + // Ignore levels with missing data or pre-existing rejection flags. + if (valuesIn[JLev] == missingValueFloat || + flagsIn[JLev] & ufo::MetOfficeQCFlags::Elem::FinalRejectFlag) + continue; + // Ignore duplicate levels. + if (idxUsefulLevels.size() > 0 && ZIn[JLevP] == ZIn[JLev]) continue; + // Set bigGapWithPreviousLevel in two cases: + if (idxUsefulLevels.size() == 0) { + // Cannot interpolate before first level. + bigGapWithPreviousLevel[idxUsefulLevels.size()] = true; + } else if (ZIn[JLevP] - ZIn[JLev] > std::max(bigGap[JLevP], bigGap[JLev])) { + numGaps++; + // Big gap from previous useful level. + bigGapWithPreviousLevel[idxUsefulLevels.size()] = true; + } + // Record index of this level. + idxUsefulLevels.push_back(JLev); + JLevP = JLev; + } + + // Loop over model levels, interpolating from useful reported levels. + + std::vector flagsInterp(numInterp, 0); // Interpolation flags. + // Counter over reported levels which is incremented inside the loop over model levels. + size_t JLev = 0; + for (size_t MLev = 0; MLev < numInterp; ++MLev) { + JLevP = JLev; // Previous value of JLev. + const double ZMLev = ZOut[MLev]; // Coordinate value at current output level. + // Increment JLev until the associated coordinate is less than ZMLev + // or the number of useful levels is reached. + while (JLev != idxUsefulLevels.size() && ZIn[idxUsefulLevels[JLev]] >= ZMLev) + ++JLev; + + // JLev is the first reported level above model level MLev. + // JLev = 0 => ZMLev below bottom level. + // JLev = idxUsefulLevels.size() => ZMLev above top level. + if (JLev == 0 || JLev == idxUsefulLevels.size()) { + // Top or bottom - do not allow extrapolation. + } else if (!bigGapWithPreviousLevel[JLev]) { + // Interpolate to model layer boundaries. + const size_t J1 = idxUsefulLevels[JLev - 1]; + const size_t J2 = idxUsefulLevels[JLev]; + // Weight given to upper layer in interpolation. + const double Interp_factor = (ZMLev - ZIn[J1]) / (ZIn[J2] - ZIn[J1]); + // Interpolate reported level values onto model levels. + valuesInterp[MLev] = valuesIn[J1] + (valuesIn[J2] - valuesIn[J1]) * Interp_factor; + flagsInterp[MLev] = flagsIn[J1] | flagsIn[J2]; + } + + // Loop if interpolating or at lowest model level. + if (method == ProfileAveraging::Method::Interpolation || MLev == 0) + continue; + + // Average over model layers. + + if (JLevP == JLev) { + // Model layer contains no reported levels. + if (valuesInterp[MLev - 1] != missingValueFloat && + valuesInterp[MLev] != missingValueFloat) { + // Use mean of values at model layer bounds. + valuesInterp[MLev - 1] = (valuesInterp[MLev - 1] + valuesInterp[MLev]) * 0.5; + flagsInterp[MLev - 1] |= flagsInterp[MLev]; + if (coordMax) (*coordMax)[MLev - 1] = ZOut[MLev - 1]; + if (coordMin) (*coordMin)[MLev - 1] = ZOut[MLev]; + } + } else { + // Model layer contains at least one reported level. + // Compute mean of values over the layer, + // weighting by the difference between coordinates in each segment. + + // Sum of DZ (coordinate differences) within model layer. + // Used as the denominator in the weighted mean. + // This is only different from unity if one or both of the upper or lower model bounds + // are missing. + float DZSum = 0.0; + + // Contribution from lowest segment, i.e. gap between + // lower bound of model layer and lower reported level coordinate. + size_t J2 = idxUsefulLevels[JLevP]; + if (valuesInterp[MLev - 1] != missingValueFloat) { + const double DZ = ZOut[MLev - 1] - ZIn[J2]; + valuesInterp[MLev - 1] = (valuesInterp[MLev - 1] + valuesIn[J2]) * DZ; + DZSum += DZ; + if (coordMax) (*coordMax)[MLev - 1] = ZOut[MLev - 1]; + } else { + valuesInterp[MLev - 1] = 0.0; + if (coordMax) (*coordMax)[MLev - 1] = ZIn[J2]; + } + + // Contribution from highest segment, i.e. gap between + // highest reported level coordinate and upper bound of model layer. + size_t J1 = idxUsefulLevels[JLev - 1]; + if (valuesInterp[MLev] != missingValueFloat) { + const double DZ = ZIn[J1] - ZOut[MLev]; + valuesInterp[MLev - 1] += (valuesInterp[MLev] + valuesIn[J1]) * DZ; + DZSum += DZ; + flagsInterp[MLev - 1] |= flagsInterp[MLev]; + if (coordMin) (*coordMin)[MLev - 1] = ZOut[MLev]; + } else { + if (coordMin) (*coordMin)[MLev - 1] = ZIn[J1]; + } + + // Sum contributions from intermediate segments, i.e. gaps between + // adjacent reported level coordinates. + for (size_t J = JLevP + 1; J < JLev; ++J) { + J1 = idxUsefulLevels[J - 1]; + J2 = idxUsefulLevels[J]; + const double DZ = ZIn[J1] - ZIn[J2]; + valuesInterp[MLev - 1] += (valuesIn[J1] + valuesIn[J2]) * DZ; + DZSum += DZ; + flagsInterp[MLev - 1] |= flagsIn[J1]; + } + J1 = idxUsefulLevels[JLev - 1]; + flagsInterp[MLev - 1] |= flagsIn[J1]; + + // Calculate layer mean values. + + // Difference in coordinate across model layer. + const double DZMod = ZOut[MLev - 1] - ZOut[MLev]; + + // Only process layers that are full by more than a certain amount. + if (DZSum > DZFrac * DZMod) { + // Set partial layer flag if the layer is between DZFrac and 99.5% full. + if (DZSum <= 0.995 * DZMod) + flagsInterp[MLev - 1] |= ufo::MetOfficeQCFlags::Profile::PartialLayerFlag; + // Divide the weighted sum by sum of the weights + // (the factor of 0.5 ensures correct normalisation). + valuesInterp[MLev - 1] *= 0.5 / DZSum; + } else { + valuesInterp[MLev - 1] = missingValueFloat; + } + } + + // Negate and swap coordMax and coordMin for this model level if the coordinates are ascending + // and the values are not missing. + if (Ascending && coordMin && coordMax && + (*coordMax)[MLev - 1] != missingValueFloat && + (*coordMin)[MLev - 1] != missingValueFloat) { + (*coordMax)[MLev - 1] *= -1; + (*coordMin)[MLev - 1] *= -1; + std::swap((*coordMax)[MLev - 1], (*coordMin)[MLev - 1]); + } + } + + // Fill output arrays. + flagsOut = {flagsInterp.begin(), flagsInterp.begin() + numOut}; + valuesOut = {valuesInterp.begin(), valuesInterp.begin() + numOut}; + } +} // namespace ufo diff --git a/src/ufo/profile/ProfileVerticalAveraging.h b/src/ufo/profile/ProfileVerticalAveraging.h new file mode 100644 index 000000000..65dd9d172 --- /dev/null +++ b/src/ufo/profile/ProfileVerticalAveraging.h @@ -0,0 +1,76 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILEVERTICALAVERAGING_H_ +#define UFO_PROFILE_PROFILEVERTICALAVERAGING_H_ + +#include + +namespace ufo { + namespace ProfileAveraging { + enum class Method {Interpolation, Averaging}; + } + + /// \brief Profile vertical averaging onto model levels. + /// + /// This routine averages the observed values in a profile onto model levels. + /// The vertical positions of observations are referred to as reported levels. + /// + /// Firstly, all valid reported level values are interpolated onto the adjacent model level + /// boundaries. If there are no reported level values adjacent to a model level then the + /// model level value is set to missing. + /// + /// If the parameter \p method is set to 'Interpolation' then no further action is taken; + /// otherwise, averaging onto the midpoint of the model layer is performed as follows. + /// + /// If a model layer contains no reported levels then the mean of the values at the + /// upper and lower model layer bounds is taken as the average value. + /// If the model layer contains at least one reported level then the weighted mean of the + /// values at the model layer boundaries and the reported levels is calculated. + /// The distance between adjacent levels (including the model layer boundaries) + /// is used as the weight in each case. + /// If either of the model boundary values is missing then the layer is referred to as a + /// partial layer. In this case a minimum fraction of distance between the upper and lower + /// model levels must have been included in the weighted mean, otherwise the mean is + /// set to the missing value. + /// The minimum fraction is governed by the parameter \p DZFrac. + /// + /// The vector \p BigGap contains the thresholds for defining the distance between + /// reported level coordinates as a 'big gap'. + /// Interpolation to model levels is not performed for reported levels separted by a big gap. + /// + /// The vectors \p coordMin and \p coordMax, which are the coordinates of the highest and lowest + /// valid values used in the weighted mean calculation, can optionally be recorded. + /// These values are used in (e.g.) atmospheric profile temperature averaging. + /// + /// \param[in] flagsIn: flags on input data. + /// \param[in] valuesIn: reported-level values. + /// \param[in] coordIn: reported-level coordinates. + /// \param[in] bigGap: maximum gap to be filled in. + /// \param[in] coordOut: model layer boundaries. + /// \param[in] DZFrac: minimum fraction for partially filled layers to be used. + /// \param[in] method: interpolate to model layer boundaries or average over model layers? + /// \param[out] flagsOut: flags on output data. + /// \param[out] valuesOut: output data (averaged or interpolated). + /// \param[out] numGaps: number of large gaps in profile. + /// \param[out] coordMax: maximum coordinate of the values used in the model layer average. + /// \param[out] coordMin: minimum coordinate of the values used in the model layer average. + void calculateVerticalAverage(const std::vector &flagsIn, + const std::vector &valuesIn, + const std::vector &coordIn, + const std::vector &bigGap, + const std::vector &coordOut, + float DZFrac, + ProfileAveraging::Method method, + std::vector &flagsOut, + std::vector &valuesOut, + int &numGaps, + std::vector *coordMax = nullptr, + std::vector *coordMin = nullptr); +} // namespace ufo + +#endif // UFO_PROFILE_PROFILEVERTICALAVERAGING_H_ diff --git a/src/ufo/profile/ProfileVerticalInterpolation.cc b/src/ufo/profile/ProfileVerticalInterpolation.cc new file mode 100644 index 000000000..bd249d805 --- /dev/null +++ b/src/ufo/profile/ProfileVerticalInterpolation.cc @@ -0,0 +1,113 @@ +/* + * (C) Copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/profile/ProfileVerticalInterpolation.h" + +namespace ufo { + void profileVerticalInterpolation(const std::vector &coordIn, + const std::vector &valuesIn, + const std::vector &coordOut, + std::vector &valuesOut, + const ProfileInterpolation::InterpolationMethod interpMethod, + const ProfileInterpolation::CoordinateOrder coordOrder, + const ProfileInterpolation::OutOfBoundsTreatment outOfBounds) + { + const float missingValueFloat = util::missingValue(missingValueFloat); + // Number of input levels. + const int nLevsIn = static_cast (coordIn.size()); + // Coordinate multiplier (depends upon the coordinate ordering). + const float coordMultiplier = + coordOrder == ProfileInterpolation::CoordinateOrder::Descending ? -1.0 : 1.0; + + // Initial value of 'previous' output coordinate is +/- the maximum float. + float previousCoordOut = coordMultiplier * std::numeric_limits::max(); + // Initial value of 'previous' input level index is the maximum integer. + int previousLevIn = std::numeric_limits::max(); + + // Loop over each output level. + for (int jlevOut = 0; jlevOut < coordOut.size(); ++jlevOut) { + if (coordOut[jlevOut] == missingValueFloat) continue; + // Index of input level closest to output level (from below or above depending on + // whether the coordinates are in ascending or descending order). + int jlevIn = 0; + + // Position at which to start searching for the closest input level. + // By default this is set to the start of the vector. + int PosStart = 0; + // If the output coordinate for this level is larger than the previous value, + // start searching at the previous level index. + // (This is in the case of ascending coordinate order; the opposite criterion must be + // fulfilled for descending order.) + if (coordMultiplier * coordOut[jlevOut] > coordMultiplier * previousCoordOut) + PosStart = previousLevIn; + + // Perform the search for the closest input level. + for (int Pos = PosStart; Pos < nLevsIn; ++Pos) { + if (coordMultiplier * coordIn[Pos] > coordMultiplier * coordOut[jlevOut]) { + jlevIn = Pos; + break; + } + } + // Modify previous input level index and output coordinate. + previousLevIn = jlevIn; + previousCoordOut = coordOut[jlevOut]; + + // Perform the interpolation. + if (coordOut[jlevOut] == coordIn[jlevIn]) { + // Copy the value if the input and output coordinate are identical. + valuesOut[jlevOut] = valuesIn[jlevIn]; + } else if (outOfBounds != ProfileInterpolation::OutOfBoundsTreatment::Extrapolate && + coordMultiplier * coordOut[jlevOut] > coordMultiplier * coordIn[nLevsIn - 1]) { + // If the output coordinate is either: + // - larger than the largest input coordinate if the coordinates are in ascending order, or + // - smaller than the smallest input coordinate if the coordinates are in descending order + // and there is no extrapolation, set the output value to one of two possibilities: + if (outOfBounds == ProfileInterpolation::OutOfBoundsTreatment::SetMissing) + valuesOut[jlevOut] = missingValueFloat; + else if (outOfBounds == ProfileInterpolation::OutOfBoundsTreatment::SetToBound) + valuesOut[jlevOut] = valuesIn[nLevsIn - 1]; + } else if (outOfBounds != ProfileInterpolation::OutOfBoundsTreatment::Extrapolate && + coordMultiplier * coordOut[jlevOut] < coordMultiplier * coordIn[0]) { + // If the output coordinate is either: + // - smaller than the smallest input coordinate if the coordinates + // are in ascending order, or + // - larger than the largest input coordinate if the coordinates are in descending order + // and there is no extrapolation, set the output value to one of two possibilities: + if (outOfBounds == ProfileInterpolation::OutOfBoundsTreatment::SetMissing) + valuesOut[jlevOut] = missingValueFloat; + else if (outOfBounds == ProfileInterpolation::OutOfBoundsTreatment::SetToBound) + valuesOut[jlevOut] = valuesIn[0]; + } else { + // Interpolation (or extrapolation if the output coordinate is out-of-bounds) + // will be performed. + // Compute the interpolation factor. + float Interp_factor = 0.0; + if (interpMethod == ProfileInterpolation::InterpolationMethod::LogLinear) { + // Log-linear interpolation. + if (coordOut[jlevOut] > 0.0 && + coordIn[jlevIn] > 0.0 && + coordIn[jlevIn + 1] > 0.0) { + Interp_factor = std::log(coordOut[jlevOut] / coordIn[jlevIn]) / + std::log(coordIn[jlevIn + 1] / coordIn[jlevIn]); + } + } else { + // Linear interpolation. + if (coordIn[jlevIn + 1] - coordIn[jlevIn] != 0) { + Interp_factor = (coordOut[jlevOut] - coordIn[jlevIn]) / + (coordIn[jlevIn + 1] - coordIn[jlevIn]); + } + } + // Determine the interpolated value. + if (valuesIn[jlevIn] != missingValueFloat && + valuesIn[jlevIn + 1] != missingValueFloat) { + valuesOut[jlevOut] = valuesIn[jlevIn] + + (valuesIn[jlevIn + 1] - valuesIn[jlevIn]) * Interp_factor; + } + } + } + } +} // namespace ufo diff --git a/src/ufo/profile/ProfileVerticalInterpolation.h b/src/ufo/profile/ProfileVerticalInterpolation.h new file mode 100644 index 000000000..75047f793 --- /dev/null +++ b/src/ufo/profile/ProfileVerticalInterpolation.h @@ -0,0 +1,48 @@ +/* + * (C) Copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILEVERTICALINTERPOLATION_H_ +#define UFO_PROFILE_PROFILEVERTICALINTERPOLATION_H_ + +#include +#include +#include +#include +#include + +#include "oops/util/missingValues.h" + +#include "ufo/utils/Constants.h" + +namespace ufo { + namespace ProfileInterpolation { + enum class InterpolationMethod {Linear, LogLinear}; + enum class CoordinateOrder {Ascending, Descending}; + enum class OutOfBoundsTreatment {SetMissing, SetToBound, Extrapolate}; + } + + /// Performs vertical interpolation from model levels onto observation levels. + /// \param[in] coordIn: Coordinates of input values. + /// \param[in] valuesIn: Input values. + /// \param[in] coordOut: Coordinates of output values. + /// \param[out] valuesOut: Interpolated values. + /// \param[in] interpMethod: Interpolation method (linear or log-linear). + /// \param[in] coordOrder: Order of coordinates (ascending or desceding). + /// \param[in] outOfBounds: How to treat out-of-bounds data. + void profileVerticalInterpolation(const std::vector &coordIn, + const std::vector &valuesIn, + const std::vector &coordOut, + std::vector &valuesOut, + const ProfileInterpolation::InterpolationMethod interpMethod = + ProfileInterpolation::InterpolationMethod::Linear, + const ProfileInterpolation::CoordinateOrder coordOrder = + ProfileInterpolation::CoordinateOrder::Ascending, + const ProfileInterpolation::OutOfBoundsTreatment outOfBounds = + ProfileInterpolation::OutOfBoundsTreatment::SetToBound); +} // namespace ufo + +#endif // UFO_PROFILE_PROFILEVERTICALINTERPOLATION_H_ diff --git a/src/ufo/profile/ProfileWindProfilerFlags.cc b/src/ufo/profile/ProfileWindProfilerFlags.cc new file mode 100644 index 000000000..50a3887cc --- /dev/null +++ b/src/ufo/profile/ProfileWindProfilerFlags.cc @@ -0,0 +1,53 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/profile/ProfileWindProfilerFlags.h" +#include "ufo/profile/VariableNames.h" + +namespace ufo { + + static ProfileCheckMaker makerProfileWindProfilerFlags_("WinProFlags"); + + ProfileWindProfilerFlags::ProfileWindProfilerFlags + (const ProfileConsistencyCheckParameters &options) + : ProfileCheckBase(options) + {} + + void ProfileWindProfilerFlags::runCheck(ProfileDataHandler &profileDataHandler) + { + oops::Log::debug() << " Wind profiler flag check" << std::endl; + + const int numProfileLevels = profileDataHandler.getNumProfileLevels(); + + std::vector &uFlags = + profileDataHandler.get(ufo::VariableNames::qcflags_eastward_wind); + std::vector &vFlags = + profileDataHandler.get(ufo::VariableNames::qcflags_northward_wind); + const std::vector &WinProQCFlags = + profileDataHandler.get(ufo::VariableNames::qcflags_wind_profiler); + const std::vector &ObsType = + profileDataHandler.get(ufo::VariableNames::ObsType); + + if (!oops::allVectorsSameNonZeroSize(uFlags, vFlags, WinProQCFlags, ObsType)) { + oops::Log::warning() << "At least one vector is the wrong size. " + << "Check will not be performed." << std::endl; + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(uFlags, vFlags, WinProQCFlags, ObsType) + << std::endl; + return; + } + + if (ObsType[0] == ufo::MetOfficeObsIDs::AtmosphericProfile::WindProf) { + for (int jlev = 0; jlev < numProfileLevels; ++jlev) { + if (WinProQCFlags[jlev] > 0) { + uFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + vFlags[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + } + } + } + } +} // namespace ufo diff --git a/src/ufo/profile/ProfileWindProfilerFlags.h b/src/ufo/profile/ProfileWindProfilerFlags.h new file mode 100644 index 000000000..434082b81 --- /dev/null +++ b/src/ufo/profile/ProfileWindProfilerFlags.h @@ -0,0 +1,41 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_PROFILE_PROFILEWINDPROFILERFLAGS_H_ +#define UFO_PROFILE_PROFILEWINDPROFILERFLAGS_H_ + +#include +#include +#include + +#include "ufo/profile/ProfileCheckBase.h" +#include "ufo/profile/ProfileDataHandler.h" + +#include "ufo/utils/metoffice/MetOfficeObservationIDs.h" + +namespace ioda { + class ObsSpace; +} + +namespace ufo { + class ProfileConsistencyCheckParameters; +} + +namespace ufo { + + /// \brief Profile QC: wind profiler QC flags. + /// Rejects levels of wind-profiler observations for which reported QC flags indicate bad obs. + class ProfileWindProfilerFlags : public ProfileCheckBase { + public: + explicit ProfileWindProfilerFlags(const ProfileConsistencyCheckParameters &options); + + /// Run check + void runCheck(ProfileDataHandler &profileDataHandler) override; + }; +} // namespace ufo + +#endif // UFO_PROFILE_PROFILEWINDPROFILERFLAGS_H_ diff --git a/src/ufo/profile/VariableNames.cc b/src/ufo/profile/VariableNames.cc new file mode 100644 index 000000000..013d75ce6 --- /dev/null +++ b/src/ufo/profile/VariableNames.cc @@ -0,0 +1,195 @@ +/* + * (C) Copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/profile/VariableNames.h" + +// Observation values + +constexpr const char* const ufo::VariableNames::obs_air_pressure; +constexpr const char* const ufo::VariableNames::obs_air_temperature; +constexpr const char* const ufo::VariableNames::obs_relative_humidity; +constexpr const char* const ufo::VariableNames::obs_eastward_wind; +constexpr const char* const ufo::VariableNames::obs_northward_wind; +constexpr const char* const ufo::VariableNames::obs_geopotential_height; +constexpr const char* const ufo::VariableNames::obs_dew_point_temperature; + +// Observation errors + +constexpr const char* const ufo::VariableNames::obserr_air_temperature; +constexpr const char* const ufo::VariableNames::obserr_relative_humidity; +constexpr const char* const ufo::VariableNames::obserr_eastward_wind; +constexpr const char* const ufo::VariableNames::obserr_northward_wind; +constexpr const char* const ufo::VariableNames::obserr_geopotential_height; +constexpr const char* const ufo::VariableNames::obserr_dew_point_temperature; + +// HofX + +constexpr const char* const ufo::VariableNames::hofx_air_temperature; +constexpr const char* const ufo::VariableNames::hofx_geopotential_height; +constexpr const char* const ufo::VariableNames::hofx_relative_humidity; +constexpr const char* const ufo::VariableNames::hofx_eastward_wind; +constexpr const char* const ufo::VariableNames::hofx_northward_wind; +constexpr const char* const ufo::VariableNames::hofx_dew_point_temperature; + +// Background errors + +constexpr const char* const ufo::VariableNames::bkgerr_air_temperature; +constexpr const char* const ufo::VariableNames::bkgerr_relative_humidity; +constexpr const char* const ufo::VariableNames::bkgerr_eastward_wind; +constexpr const char* const ufo::VariableNames::bkgerr_northward_wind; +constexpr const char* const ufo::VariableNames::bkgerr_geopotential_height; +constexpr const char* const ufo::VariableNames::bkgerr_dew_point_temperature; + +// Probability of gross error + +constexpr const char* const ufo::VariableNames::pge_air_temperature; +constexpr const char* const ufo::VariableNames::pge_relative_humidity; +constexpr const char* const ufo::VariableNames::pge_eastward_wind; +constexpr const char* const ufo::VariableNames::pge_northward_wind; +constexpr const char* const ufo::VariableNames::pge_geopotential_height; + +// Probability of gross error used in buddy check + +constexpr const char* const ufo::VariableNames::pgebd_air_temperature; +constexpr const char* const ufo::VariableNames::pgebd_relative_humidity; +constexpr const char* const ufo::VariableNames::pgebd_eastward_wind; +constexpr const char* const ufo::VariableNames::pgebd_northward_wind; +constexpr const char* const ufo::VariableNames::pgebd_geopotential_height; + +// MetaData + +constexpr const char* const ufo::VariableNames::station_ID; +constexpr const char* const ufo::VariableNames::obs_level_time; +constexpr const char* const ufo::VariableNames::ObsType; +constexpr const char* const ufo::VariableNames::Latitude; +constexpr const char* const ufo::VariableNames::Longitude; +constexpr const char* const ufo::VariableNames::Time; +constexpr const char* const ufo::VariableNames::Zstation; +constexpr const char* const ufo::VariableNames::LevelType; +constexpr const char* const ufo::VariableNames::InstrType; +constexpr const char* const ufo::VariableNames::extended_obs_space; + +// QC flags + +constexpr const char* const ufo::VariableNames::qcflags_observation_report; +constexpr const char* const ufo::VariableNames::qcflags_air_temperature; +constexpr const char* const ufo::VariableNames::qcflags_relative_humidity; +constexpr const char* const ufo::VariableNames::qcflags_geopotential_height; +constexpr const char* const ufo::VariableNames::qcflags_eastward_wind; +constexpr const char* const ufo::VariableNames::qcflags_northward_wind; +constexpr const char* const ufo::VariableNames::qcflags_time; +constexpr const char* const ufo::VariableNames::qcflags_wind_profiler; + +// Counters + +constexpr const char* const ufo::VariableNames::counter_NumAnyErrors; +constexpr const char* const ufo::VariableNames::counter_NumSamePErrObs; +constexpr const char* const ufo::VariableNames::counter_NumSuperadiabat; +constexpr const char* const ufo::VariableNames::counter_Num925Miss; +constexpr const char* const ufo::VariableNames::counter_Num100Miss; +constexpr const char* const ufo::VariableNames::counter_NumStdMiss; +constexpr const char* const ufo::VariableNames::counter_NumHydErrObs; +constexpr const char* const ufo::VariableNames::counter_NumIntHydErrors; +constexpr const char* const ufo::VariableNames::counter_NumInterpErrors; +constexpr const char* const ufo::VariableNames::counter_NumInterpErrObs; +constexpr const char* const ufo::VariableNames::counter_NumSignChange; +constexpr const char* const ufo::VariableNames::counter_TotCProfs; +constexpr const char* const ufo::VariableNames::counter_TotHProfs; +constexpr const char* const ufo::VariableNames::counter_TotCFlags; +constexpr const char* const ufo::VariableNames::counter_TotHFlags; +constexpr const char* const ufo::VariableNames::counter_TotLFlags; +constexpr const char* const ufo::VariableNames::counter_NumGapsT; +constexpr const char* const ufo::VariableNames::counter_NumGapsU; +constexpr const char* const ufo::VariableNames::counter_NumGapsUWP; +constexpr const char* const ufo::VariableNames::counter_NumGapsRH; + +// Corrections + +constexpr const char* const ufo::VariableNames::obscorrection_air_temperature; +constexpr const char* const ufo::VariableNames::obscorrection_geopotential_height; + +// Intermediate values + +constexpr const char* const ufo::VariableNames::DC; +constexpr const char* const ufo::VariableNames::ETol; +constexpr const char* const ufo::VariableNames::D; +constexpr const char* const ufo::VariableNames::E; +constexpr const char* const ufo::VariableNames::HydError; +constexpr const char* const ufo::VariableNames::PBottom; +constexpr const char* const ufo::VariableNames::StdLev; +constexpr const char* const ufo::VariableNames::SigAbove; +constexpr const char* const ufo::VariableNames::SigBelow; +constexpr const char* const ufo::VariableNames::IndStd; +constexpr const char* const ufo::VariableNames::LevErrors; +constexpr const char* const ufo::VariableNames::tInterp; +constexpr const char* const ufo::VariableNames::uInterp; +constexpr const char* const ufo::VariableNames::vInterp; +constexpr const char* const ufo::VariableNames::LogP; +constexpr const char* const ufo::VariableNames::NumStd; +constexpr const char* const ufo::VariableNames::NumSig; +constexpr const char* const ufo::VariableNames::Press; +constexpr const char* const ufo::VariableNames::Temp; +constexpr const char* const ufo::VariableNames::rh; +constexpr const char* const ufo::VariableNames::td; +constexpr const char* const ufo::VariableNames::tbk; +constexpr const char* const ufo::VariableNames::rhbk; +constexpr const char* const ufo::VariableNames::FlagH; +constexpr const char* const ufo::VariableNames::Indx; + +// Derived values + +constexpr const char* const ufo::VariableNames::LogP_derived; +constexpr const char* const ufo::VariableNames::bigPgaps_derived; + +// GeoVaLs + +constexpr const char* const ufo::VariableNames::geovals_orog; +constexpr const char* const ufo::VariableNames::geovals_pressure; +constexpr const char* const ufo::VariableNames::geovals_pressure_rho; +constexpr const char* const ufo::VariableNames::geovals_height; +constexpr const char* const ufo::VariableNames::geovals_potential_temperature; +constexpr const char* const ufo::VariableNames::geovals_surface_pressure; +constexpr const char* const ufo::VariableNames::geovals_relative_humidity; + +// GeoVaLs used in validation + +constexpr const char* const ufo::VariableNames::geovals_logP; +constexpr const char* const ufo::VariableNames::geovals_ExnerP; +constexpr const char* const ufo::VariableNames::geovals_logP_rho; +constexpr const char* const ufo::VariableNames::geovals_ExnerP_rho; +constexpr const char* const ufo::VariableNames::geovals_air_temperature; +constexpr const char* const ufo::VariableNames::geovals_average_air_temperature; +constexpr const char* const ufo::VariableNames::geovals_average_eastward_wind; +constexpr const char* const ufo::VariableNames::geovals_average_northward_wind; +constexpr const char* const ufo::VariableNames::geovals_average_relative_humidity; +constexpr const char* const ufo::VariableNames::geovals_average_air_temperature_qcflags; +constexpr const char* const ufo::VariableNames::geovals_average_eastward_wind_qcflags; +constexpr const char* const ufo::VariableNames::geovals_average_northward_wind_qcflags; +constexpr const char* const ufo::VariableNames::geovals_average_relative_humidity_qcflags; + +// Derived values on model levels + +constexpr const char* const ufo::VariableNames::modellevels_logP_derived; +constexpr const char* const ufo::VariableNames::modellevels_ExnerP_derived; +constexpr const char* const ufo::VariableNames::modellevels_air_temperature_derived; + +// Averaged values on model levels + +constexpr const char* const ufo::VariableNames::modellevels_average_air_temperature_derived; +constexpr const char* const ufo::VariableNames::modellevels_average_eastward_wind_derived; +constexpr const char* const ufo::VariableNames::modellevels_average_northward_wind_derived; +constexpr const char* const ufo::VariableNames::modellevels_average_relative_humidity_derived; +constexpr const char* const ufo::VariableNames::modellevels_average_air_temperature_qcflags; +constexpr const char* const ufo::VariableNames::modellevels_average_eastward_wind_qcflags; +constexpr const char* const ufo::VariableNames::modellevels_average_northward_wind_qcflags; +constexpr const char* const ufo::VariableNames::modellevels_average_relative_humidity_qcflags; + +// Derived values on model rho levels + +constexpr const char* const ufo::VariableNames::modellevels_logP_rho_derived; +constexpr const char* const ufo::VariableNames::modellevels_logPWB_rho_derived; +constexpr const char* const ufo::VariableNames::modellevels_ExnerP_rho_derived; diff --git a/src/ufo/profile/VariableNames.h b/src/ufo/profile/VariableNames.h index 1debd6104..71de0f71b 100644 --- a/src/ufo/profile/VariableNames.h +++ b/src/ufo/profile/VariableNames.h @@ -13,7 +13,7 @@ namespace ufo struct VariableNames { - // Variable names: observation values + // Observation values static constexpr const char* const obs_air_pressure = "air_pressure@MetaData"; static constexpr const char* const obs_air_temperature = "air_temperature@ObsValue"; @@ -23,7 +23,7 @@ struct VariableNames static constexpr const char* const obs_geopotential_height = "geopotential_height@ObsValue"; static constexpr const char* const obs_dew_point_temperature = "dew_point_temperature@ObsValue"; - // Variable names: observation errors + // Observation errors static constexpr const char* const obserr_air_temperature = "air_temperature@ObsError"; static constexpr const char* const obserr_relative_humidity = "relative_humidity@ObsError"; @@ -33,7 +33,7 @@ struct VariableNames static constexpr const char* const obserr_dew_point_temperature = "dew_point_temperature@ObsError"; - // Variable names: HofX + // HofX static constexpr const char* const hofx_air_temperature = "air_temperature@HofX"; static constexpr const char* const hofx_geopotential_height = "geopotential_height@HofX"; @@ -42,17 +42,22 @@ struct VariableNames static constexpr const char* const hofx_northward_wind = "northward_wind@HofX"; static constexpr const char* const hofx_dew_point_temperature = "dew_point_temperature@HofX"; - // Variable names: background errors - - static constexpr const char* const bkgerr_air_temperature = "air_temperature@BkgError"; - static constexpr const char* const bkgerr_relative_humidity = "relative_humidity@BkgError"; - static constexpr const char* const bkgerr_eastward_wind = "eastward_wind@BkgError"; - static constexpr const char* const bkgerr_northward_wind = "northward_wind@BkgError"; - static constexpr const char* const bkgerr_geopotential_height = "geopotential_height@BkgError"; + // Background errors + + static constexpr const char* const bkgerr_air_temperature = + "air_temperature_background_error@ObsDiag"; + static constexpr const char* const bkgerr_relative_humidity = + "relative_humidity_background_error@ObsDiag"; + static constexpr const char* const bkgerr_eastward_wind = + "eastward_wind_background_error@ObsDiag"; + static constexpr const char* const bkgerr_northward_wind = + "northward_wind_background_error@ObsDiag"; + static constexpr const char* const bkgerr_geopotential_height = + "geopotential_height_background_error@ObsDiag"; static constexpr const char* const bkgerr_dew_point_temperature = - "dew_point_temperature@BkgError"; + "dew_point_temperature_background_error@ObsDiag"; - // Variable names: probability of gross error + // Probability of gross error static constexpr const char* const pge_air_temperature = "air_temperature@GrossErrorProbability"; static constexpr const char* const pge_relative_humidity = @@ -62,7 +67,7 @@ struct VariableNames static constexpr const char* const pge_geopotential_height = "geopotential_height@GrossErrorProbability"; - // Variable names: probability of gross error used in buddy check + // Probability of gross error used in buddy check static constexpr const char* const pgebd_air_temperature = "air_temperature@GrossErrorProbabilityBuddyCheck"; @@ -75,18 +80,20 @@ struct VariableNames static constexpr const char* const pgebd_geopotential_height = "geopotential_height@GrossErrorProbabilityBuddyCheck"; - // Variable names: MetaData + // MetaData static constexpr const char* const station_ID = "station_id@MetaData"; static constexpr const char* const obs_level_time = "level_time@MetaData"; - static constexpr const char* const PstarBackgr = "PstarBackgr@MetaData"; static constexpr const char* const ObsType = "ObsType@MetaData"; static constexpr const char* const Latitude = "latitude@MetaData"; static constexpr const char* const Longitude = "longitude@MetaData"; static constexpr const char* const Time = "time@MetaData"; static constexpr const char* const Zstation = "Zstation@MetaData"; + static constexpr const char* const LevelType = "LevelType@MetaData"; + static constexpr const char* const InstrType = "InstrType@MetaData"; + static constexpr const char* const extended_obs_space = "extended_obs_space@MetaData"; - // Variable names: QC flags + // QC flags static constexpr const char* const qcflags_observation_report = "observation_report@QCFlags"; static constexpr const char* const qcflags_air_temperature = "air_temperature@QCFlags"; @@ -95,8 +102,9 @@ struct VariableNames static constexpr const char* const qcflags_eastward_wind = "eastward_wind@QCFlags"; static constexpr const char* const qcflags_northward_wind = "northward_wind@QCFlags"; static constexpr const char* const qcflags_time = "time@QCFlags"; + static constexpr const char* const qcflags_wind_profiler = "wind_profiler@QCFlags"; - // Variable names: counters + // Counters static constexpr const char* const counter_NumAnyErrors = "NumAnyErrors@Counters"; static constexpr const char* const counter_NumSamePErrObs = "NumSamePErrObs@Counters"; @@ -114,14 +122,18 @@ struct VariableNames static constexpr const char* const counter_TotCFlags = "TotCFlags@Counters"; static constexpr const char* const counter_TotHFlags = "TotHFlags@Counters"; static constexpr const char* const counter_TotLFlags = "TotLFlags@Counters"; + static constexpr const char* const counter_NumGapsT = "NumGapsT@Counters"; + static constexpr const char* const counter_NumGapsU = "NumGapsU@Counters"; + static constexpr const char* const counter_NumGapsUWP = "NumGapsUWP@Counters"; + static constexpr const char* const counter_NumGapsRH = "NumGapsRH@Counters"; - // Variable names: corrections + // Corrections static constexpr const char* const obscorrection_air_temperature = "air_temperature@Corrections"; static constexpr const char* const obscorrection_geopotential_height = "geopotential_height@Corrections"; - // Variable names: intermediate values + // Intermediate values static constexpr const char* const DC = "DC@MetaData"; static constexpr const char* const ETol = "ETol@MetaData"; @@ -148,6 +160,77 @@ struct VariableNames static constexpr const char* const rhbk = "rhbk@MetaData"; static constexpr const char* const FlagH = "FlagH@MetaData"; static constexpr const char* const Indx = "Indx@MetaData"; + + // Derived values + + static constexpr const char* const LogP_derived = "logP@DerivedValue"; + static constexpr const char* const bigPgaps_derived = "bigPgaps@DerivedValue"; + + // GeoVaLs + + static constexpr const char* const geovals_orog = "orography"; + static constexpr const char* const geovals_pressure = "air_pressure"; + static constexpr const char* const geovals_pressure_rho = "air_pressure_rho"; + static constexpr const char* const geovals_height = "height"; + static constexpr const char* const geovals_potential_temperature = "air_potential_temperature"; + static constexpr const char* const geovals_surface_pressure = "surface_pressure"; + static constexpr const char* const geovals_relative_humidity = "relative_humidity"; + + // GeoVaLs used in validation + + static constexpr const char* const geovals_logP = "LogPB"; + static constexpr const char* const geovals_ExnerP = "ExnerPB"; + static constexpr const char* const geovals_logP_rho = "LogPA"; + static constexpr const char* const geovals_ExnerP_rho = "ExnerPA"; + static constexpr const char* const geovals_air_temperature = "air_temperature"; + static constexpr const char* const geovals_average_air_temperature = "average_air_temperature"; + static constexpr const char* const geovals_average_eastward_wind = "average_eastward_wind"; + static constexpr const char* const geovals_average_northward_wind = "average_northward_wind"; + static constexpr const char* const geovals_average_relative_humidity = + "average_relative_humidity"; + static constexpr const char* const geovals_average_air_temperature_qcflags = + "average_air_temperature_flags"; + static constexpr const char* const geovals_average_eastward_wind_qcflags = + "average_eastward_wind_flags"; + static constexpr const char* const geovals_average_northward_wind_qcflags = + "average_northward_wind_flags"; + static constexpr const char* const geovals_average_relative_humidity_qcflags = + "average_relative_humidity_flags"; + + // Derived values on model levels + + static constexpr const char* const modellevels_logP_derived = "LogPB@ModelLevelsDerivedValue"; + static constexpr const char* const modellevels_ExnerP_derived = "ExnerPB@ModelLevelsDerivedValue"; + static constexpr const char* const modellevels_air_temperature_derived = + "air_temperature@ModelLevelsDerivedValue"; + + // Averaged values on model levels + + static constexpr const char* const modellevels_average_air_temperature_derived = + "average_air_temperature@ModelLevelsDerivedValue"; + static constexpr const char* const modellevels_average_eastward_wind_derived = + "average_eastward_wind@ModelLevelsDerivedValue"; + static constexpr const char* const modellevels_average_northward_wind_derived = + "average_northward_wind@ModelLevelsDerivedValue"; + static constexpr const char* const modellevels_average_relative_humidity_derived = + "average_relative_humidity@ModelLevelsDerivedValue"; + static constexpr const char* const modellevels_average_air_temperature_qcflags = + "average_air_temperature@ModelLevelsQCFlags"; + static constexpr const char* const modellevels_average_eastward_wind_qcflags = + "average_eastward_wind@ModelLevelsQCFlags"; + static constexpr const char* const modellevels_average_northward_wind_qcflags = + "average_northward_wind@ModelLevelsQCFlags"; + static constexpr const char* const modellevels_average_relative_humidity_qcflags = + "average_relative_humidity@ModelLevelsQCFlags"; + + // Derived values on model rho levels + + static constexpr const char* const modellevels_logP_rho_derived = + "LogPA@ModelRhoLevelsDerivedValue"; + static constexpr const char* const modellevels_logPWB_rho_derived = + "LogPWB@ModelRhoLevelsDerivedValue"; + static constexpr const char* const modellevels_ExnerP_rho_derived = + "ExnerPA@ModelRhoLevelsDerivedValue"; }; } // namespace ufo diff --git a/src/ufo/radarradialvelocity/CMakeLists.txt b/src/ufo/radarradialvelocity/CMakeLists.txt index c791eef4f..03b1017ab 100644 --- a/src/ufo/radarradialvelocity/CMakeLists.txt +++ b/src/ufo/radarradialvelocity/CMakeLists.txt @@ -6,9 +6,14 @@ set ( radarradialvelocity_files ObsRadarRadialVelocity.h ObsRadarRadialVelocity.cc + ObsRadarRadialVelocityTLAD.h + ObsRadarRadialVelocityTLAD.cc ObsRadarRadialVelocity.interface.F90 ObsRadarRadialVelocity.interface.h + ObsRadarRadialVelocityTLAD.interface.F90 + ObsRadarRadialVelocityTLAD.interface.h ufo_radarradialvelocity_mod.F90 + ufo_radarradialvelocity_tlad_mod.F90 ) PREPEND( _p_radarradialvelocity_files "radarradialvelocity" ${radarradialvelocity_files} ) diff --git a/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.cc b/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.cc new file mode 100644 index 000000000..9d776ee26 --- /dev/null +++ b/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.cc @@ -0,0 +1,85 @@ +/* + * (C) Copyright 2017-2018 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.h" + +#include + +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsBias.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static LinearObsOperatorMaker + makerRadarRadialVelocityTL_("RadarRadialVelocity"); +// ----------------------------------------------------------------------------- + +ObsRadarRadialVelocityTLAD::ObsRadarRadialVelocityTLAD(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : LinearObsOperatorBase(odb), keyOperRadarRadialVelocity_(0), varin_() +{ + ufo_radarradialvelocity_tlad_setup_f90(keyOperRadarRadialVelocity_, + config, odb.obsvariables(), varin_); + + oops::Log::trace() << "ObsRadarRadialVelocityTLAD created" << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsRadarRadialVelocityTLAD::~ObsRadarRadialVelocityTLAD() { + ufo_radarradialvelocity_tlad_delete_f90(keyOperRadarRadialVelocity_); + oops::Log::trace() << "ObsRadarRadialVelocityTLAD destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsRadarRadialVelocityTLAD::setTrajectory(const GeoVaLs & geovals, + const ObsBias & bias, ObsDiagnostics &) { + oops::Log::trace() << "ObsRadarRadialVelocityTLAD::setTrajectory entering" << std::endl; + + ufo_radarradialvelocity_tlad_settraj_f90(keyOperRadarRadialVelocity_, geovals.toFortran(), + obsspace()); + + oops::Log::trace() << "ObsRadarRadialVelocityTLAD::setTrajectory exiting" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsRadarRadialVelocityTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) + const { + ufo_radarradialvelocity_simobs_tl_f90(keyOperRadarRadialVelocity_, geovals.toFortran(), + obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); + + oops::Log::trace() << "ObsRadarRadialVelocityTLAD::simulateObsTL exiting" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsRadarRadialVelocityTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) + const { + ufo_radarradialvelocity_simobs_ad_f90(keyOperRadarRadialVelocity_, geovals.toFortran(), + obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); + + oops::Log::trace() << "ObsRadarRadialVelocityTLAD::simulateObsAD exiting" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsRadarRadialVelocityTLAD::print(std::ostream & os) const { + os << "ObsRadarRadialVelocityTLAD::print not implemented" << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.h b/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.h new file mode 100644 index 000000000..e79df9891 --- /dev/null +++ b/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.h @@ -0,0 +1,65 @@ +/* + * (C) Copyright 2017-2018 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_RADARRADIALVELOCITY_OBSRADARRADIALVELOCITYTLAD_H_ +#define UFO_RADARRADIALVELOCITY_OBSRADARRADIALVELOCITYTLAD_H_ + + +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/LinearObsOperatorBase.h" +#include "ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.interface.h" + +// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsBias; + class ObsDiagnostics; + +// ----------------------------------------------------------------------------- +/// RadarRadialVelocity observation operator +class ObsRadarRadialVelocityTLAD : public LinearObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsRadarRadialVelocityTLAD";} + + ObsRadarRadialVelocityTLAD(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsRadarRadialVelocityTLAD(); + + // Obs Operators + void setTrajectory(const GeoVaLs &, const ObsBias &, ObsDiagnostics &) override; + void simulateObsTL(const GeoVaLs &, ioda::ObsVector &) const override; + void simulateObsAD(GeoVaLs &, const ioda::ObsVector &) const override; + + // Other + const oops::Variables & requiredVars() const override {return varin_;} + + int & toFortran() {return keyOperRadarRadialVelocity_;} + const int & toFortran() const {return keyOperRadarRadialVelocity_;} + + private: + void print(std::ostream &) const override; + F90hop keyOperRadarRadialVelocity_; + oops::Variables varin_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_RADARRADIALVELOCITY_OBSRADARRADIALVELOCITYTLAD_H_ diff --git a/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.interface.F90 b/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.interface.F90 new file mode 100644 index 000000000..7adc36152 --- /dev/null +++ b/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.interface.F90 @@ -0,0 +1,133 @@ +! (C) Copyright 2017-2018 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to handle radarradialvelocity observations + +module ufo_radarradialvelocity_tlad_mod_c + + use fckit_configuration_module, only: fckit_configuration + use ufo_radarradialvelocity_tlad_mod + use ufo_geovals_mod_c, only: ufo_geovals_registry + use ufo_geovals_mod, only: ufo_geovals + implicit none + private + +#define LISTED_TYPE ufo_radarradialvelocity_tlad + + !> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + + !> Global registry + type(registry_t) :: ufo_radarradialvelocity_tlad_registry + + ! ------------------------------------------------------------------------------ +contains + ! ------------------------------------------------------------------------------ + !> Linked list implementation +#include "oops/util/linkedList_c.f" + +! ------------------------------------------------------------------------------ + +subroutine ufo_radarradialvelocity_tlad_setup_c(c_key_self, c_conf, c_obsvars, c_geovars) bind(c,name='ufo_radarradialvelocity_tlad_setup_f90') +use oops_variables_mod +implicit none +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), value, intent(in) :: c_conf +type(c_ptr), value, intent(in) :: c_obsvars ! variables to be simulated +type(c_ptr), value, intent(in) :: c_geovars ! variables requested from the model + +type(ufo_radarradialvelocity_tlad), pointer :: self +type(fckit_configuration) :: f_conf + +call ufo_radarradialvelocity_tlad_registry%setup(c_key_self, self) +f_conf = fckit_configuration(c_conf) + +self%obsvars = oops_variables(c_obsvars) +self%geovars = oops_variables(c_geovars) +call self%setup(f_conf) + +end subroutine ufo_radarradialvelocity_tlad_setup_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_radarradialvelocity_tlad_delete_c(c_key_self) bind(c,name='ufo_radarradialvelocity_tlad_delete_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self + +type(ufo_radarradialvelocity_tlad), pointer :: self + +call ufo_radarradialvelocity_tlad_registry%delete(c_key_self, self) + +end subroutine ufo_radarradialvelocity_tlad_delete_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_radarradialvelocity_tlad_settraj_c(c_key_self, c_key_geovals, c_obsspace) bind(c,name='ufo_radarradialvelocity_tlad_settraj_f90') +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace + +type(ufo_radarradialvelocity_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +character(len=*), parameter :: myname_="ufo_radarradialvelocity_tlad_settraj_c" + +call ufo_radarradialvelocity_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) + +call self%settraj(geovals, c_obsspace) + +end subroutine ufo_radarradialvelocity_tlad_settraj_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_radarradialvelocity_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) & + bind(c,name='ufo_radarradialvelocity_simobs_tl_f90') +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nvars, c_nlocs +real(c_double), intent(inout) :: c_hofx(c_nvars, c_nlocs) + +type(ufo_radarradialvelocity_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +character(len=*), parameter :: myname_="ufo_radarradialvelocity_simobs_tl_c" + +call ufo_radarradialvelocity_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) + +call self%simobs_tl(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) + +end subroutine ufo_radarradialvelocity_simobs_tl_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_radarradialvelocity_simobs_ad_c(c_key_self, c_key_geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) & + bind(c,name='ufo_radarradialvelocity_simobs_ad_f90') +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nvars, c_nlocs +real(c_double), intent(in) :: c_hofx(c_nvars, c_nlocs) + +type(ufo_radarradialvelocity_tlad), pointer :: self +type(ufo_geovals), pointer :: geovals + +character(len=*), parameter :: myname_="ufo_radarradialvelocity_simobs_ad_c" + +call ufo_radarradialvelocity_tlad_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) + +call self%simobs_ad(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) + +end subroutine ufo_radarradialvelocity_simobs_ad_c + +! ------------------------------------------------------------------------------ + +end module ufo_radarradialvelocity_tlad_mod_c diff --git a/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.interface.h b/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.interface.h new file mode 100644 index 000000000..200d3542f --- /dev/null +++ b/src/ufo/radarradialvelocity/ObsRadarRadialVelocityTLAD.interface.h @@ -0,0 +1,46 @@ +/* + * (C) Copyright 2017 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_RADARRADIALVELOCITY_OBSRADARRADIALVELOCITYTLAD_INTERFACE_H_ +#define UFO_RADARRADIALVELOCITY_OBSRADARRADIALVELOCITYTLAD_INTERFACE_H_ + +#include "ioda/ObsSpace.h" +#include "oops/base/Variables.h" +#include "ufo/Fortran.h" + +namespace ufo { + +/// Interface to Fortran UFO routines +/*! + * The core of the UFO is coded in Fortran. + * Here we define the interfaces to the Fortran code. + */ + +extern "C" { + +// ----------------------------------------------------------------------------- +// RadarRadialVelocity tl/ad observation operator +// ----------------------------------------------------------------------------- + + void ufo_radarradialvelocity_tlad_setup_f90(F90hop &, const eckit::Configuration &, + const oops::Variables &, oops::Variables &); + void ufo_radarradialvelocity_tlad_delete_f90(F90hop &); + void ufo_radarradialvelocity_tlad_settraj_f90(const F90hop &, const F90goms &, + const ioda::ObsSpace &); + void ufo_radarradialvelocity_simobs_tl_f90(const F90hop &, const F90goms &, + const ioda::ObsSpace &, + const int &, const int &, double &); + void ufo_radarradialvelocity_simobs_ad_f90(const F90hop &, const F90goms &, + const ioda::ObsSpace &, + const int &, const int &, const double &); + +// ----------------------------------------------------------------------------- + +} // extern C + +} // namespace ufo +#endif // UFO_RADARRADIALVELOCITY_OBSRADARRADIALVELOCITYTLAD_INTERFACE_H_ diff --git a/src/ufo/radarradialvelocity/ufo_radarradialvelocity_mod.F90 b/src/ufo/radarradialvelocity/ufo_radarradialvelocity_mod.F90 index 5d816ec15..571bb8c7d 100644 --- a/src/ufo/radarradialvelocity/ufo_radarradialvelocity_mod.F90 +++ b/src/ufo/radarradialvelocity/ufo_radarradialvelocity_mod.F90 @@ -50,7 +50,7 @@ subroutine ufo_radarradialvelocity_setup(self, yaml_conf) call abor1_ftn("ufo_radarradialvelocity: incorrect vertical coordinate specified") endif else ! default - self%v_coord = var_z + self%v_coord = var_zm endif call self%geovars%push_back(self%v_coord) @@ -74,7 +74,7 @@ subroutine ufo_radarradialvelocity_simobs(self, geovals, obss, nvars, nlocs, hof ! Local variables integer :: iobs, ivar, nvars_geovars real(kind_real), dimension(:), allocatable :: obsvcoord - real(kind_real), dimension(:), allocatable :: radarazim, radartilt, radardir, vterminal + real(kind_real), dimension(:), allocatable :: cosazm_costilt, sinazm_costilt, sintilt, vterminal type(ufo_geoval), pointer :: vcoordprofile, profile real(kind_real), allocatable :: wf(:) integer, allocatable :: wi(:) @@ -93,15 +93,15 @@ subroutine ufo_radarradialvelocity_simobs(self, geovals, obss, nvars, nlocs, hof ! Get the observation vertical coordinates allocate(obsvcoord(nlocs)) - allocate(radarazim(nlocs)) - allocate(radartilt(nlocs)) - allocate(radardir(nlocs)) + allocate(cosazm_costilt(nlocs)) + allocate(sinazm_costilt(nlocs)) + allocate(sintilt(nlocs)) allocate(vterminal(nlocs)) - call obsspace_get_db(obss, "MetaData", "height", obsvcoord) - call obsspace_get_db(obss, "MetaData", "radar_azimuth", radarazim) - call obsspace_get_db(obss, "MetaData", "radar_tilt", radartilt) - call obsspace_get_db(obss, "MetaData", "radar_dir3", radardir) - call obsspace_get_db(obss, "MetaData", "vterminal", vterminal) + + call obsspace_get_db(obss, "MetaData", "geometric_height", obsvcoord) + call obsspace_get_db(obss, "MetaData", "cosazm_costilt", cosazm_costilt) + call obsspace_get_db(obss, "MetaData", "sinazm_costilt", sinazm_costilt) + call obsspace_get_db(obss, "MetaData", "sintilt", sintilt) ! put observation operator code here ! Allocate arrays for interpolation weights @@ -136,19 +136,20 @@ subroutine ufo_radarradialvelocity_simobs(self, geovals, obss, nvars, nlocs, hof enddo enddo + vterminal=0.0 do ivar = 1, nvars do iobs=1,nlocs - hofx(ivar,iobs) = vfields(1,iobs)*radarazim(iobs) & - + vfields(2,iobs)*radartilt(iobs) & - + (vfields(3,iobs)-vterminal(iobs))*radardir(iobs) + hofx(ivar,iobs) = vfields(1,iobs)*cosazm_costilt(iobs) & + + vfields(2,iobs)*sinazm_costilt(iobs) & + + (vfields(3,iobs)-vterminal(iobs))*sintilt(iobs) enddo end do ! Cleanup memory deallocate(obsvcoord) - deallocate(radarazim) - deallocate(radartilt) - deallocate(radardir ) + deallocate(cosazm_costilt) + deallocate(sinazm_costilt) + deallocate(sintilt ) deallocate(vterminal) deallocate(wi) deallocate(wf) diff --git a/src/ufo/radarradialvelocity/ufo_radarradialvelocity_tlad_mod.F90 b/src/ufo/radarradialvelocity/ufo_radarradialvelocity_tlad_mod.F90 new file mode 100644 index 000000000..100a97cfa --- /dev/null +++ b/src/ufo/radarradialvelocity/ufo_radarradialvelocity_tlad_mod.F90 @@ -0,0 +1,259 @@ +! (C) Copyright 2017-2019 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +module ufo_radarradialvelocity_tlad_mod + + use oops_variables_mod + use ufo_vars_mod + use ufo_geovals_mod + use vert_interp_mod + use missing_values_mod + + +! ------------------------------------------------------------------------------ + + type, public :: ufo_radarradialvelocity_tlad + private + type(oops_variables), public :: obsvars + type(oops_variables), public :: geovars + integer :: nval, nlocs + character(len=MAXVARLEN), public :: v_coord ! GeoVaL to use to interpolate in vertical + real(kind_real), allocatable :: wf(:) + integer, allocatable :: wi(:) + real(kind_real), dimension(:), allocatable :: cosazm_costilt, sinazm_costilt, sintilt, vterminal + contains + procedure :: setup => radarradialvelocity_tlad_setup_ + procedure :: cleanup => radarradialvelocity_tlad_cleanup_ + procedure :: settraj => radarradialvelocity_tlad_settraj_ + procedure :: simobs_tl => radarradialvelocity_simobs_tl_ + procedure :: simobs_ad => radarradialvelocity_simobs_ad_ + final :: destructor + end type ufo_radarradialvelocity_tlad + + character(len=maxvarlen), dimension(3), parameter :: geovars_default = (/var_u, & + var_v, & + var_w /) + +! ------------------------------------------------------------------------------ +contains +! ------------------------------------------------------------------------------ + +subroutine radarradialvelocity_tlad_setup_(self, yaml_conf) + use fckit_configuration_module, only: fckit_configuration + implicit none + class(ufo_radarradialvelocity_tlad), intent(inout) :: self + type(fckit_configuration), intent(in) :: yaml_conf + + character(kind=c_char,len=:), allocatable :: coord_name + + integer :: ivar, nvars + call self%geovars%push_back(geovars_default) + + if( yaml_conf%has("VertCoord") ) then + call yaml_conf%get_or_die("VertCoord",coord_name) + self%v_coord = coord_name + if( trim(self%v_coord) .ne. var_z ) then + call abor1_ftn("ufo_radarradialvelocity: incorrect vertical coordinate specified") + endif + else ! default + !self%v_coord = var_z + self%v_coord = var_zm + endif + + +end subroutine radarradialvelocity_tlad_setup_ + +! ------------------------------------------------------------------------------ + +subroutine radarradialvelocity_tlad_settraj_(self, geovals, obss) + use obsspace_mod + implicit none + class(ufo_radarradialvelocity_tlad), intent(inout) :: self + type(ufo_geovals), intent(in) :: geovals + type(c_ptr), value, intent(in) :: obss + + !local variables + integer :: iobs, ivar, nvars_geovars + real(kind_real), dimension(:), allocatable :: obsvcoord + !real(kind_real), dimension(:), allocatable :: cosazm_costilt, sinazm_costilt, sintilt, vterminal + type(ufo_geoval), pointer :: vcoordprofile, profile + + real(kind_real), allocatable :: tmp(:) + real(kind_real) :: tmp2 + integer:: i + + real(kind_real), allocatable :: vfields(:,:) ! background fields (u,v,w) interplated vertically to the observation height + + ! Make sure nothing already allocated + call self%cleanup() + + ! Get height profiles from geovals + call ufo_geovals_get_var(geovals, self%v_coord, vcoordprofile) + self%nval = vcoordprofile%nval + + ! Get the observation vertical coordinates + self%nlocs = obsspace_get_nlocs(obss) + allocate(obsvcoord(self%nlocs)) + allocate(self%cosazm_costilt(self%nlocs)) + allocate(self%sinazm_costilt(self%nlocs)) + allocate(self%sintilt(self%nlocs)) + allocate(self%vterminal(self%nlocs)) + + call obsspace_get_db(obss, "MetaData", "geometric_height", obsvcoord) + call obsspace_get_db(obss, "MetaData", "cosazm_costilt", self%cosazm_costilt) + call obsspace_get_db(obss, "MetaData", "sinazm_costilt", self%sinazm_costilt) + call obsspace_get_db(obss, "MetaData", "sintilt", self%sintilt) +! call obsspace_get_db(obss, "MetaData", "vterminal", self%vterminal) + + ! Allocate arrays for interpolation weights + allocate(self%wi(self%nlocs)) + allocate(self%wf(self%nlocs)) + + ! Calculate the interpolation weights + allocate(tmp(vcoordprofile%nval)) + do iobs = 1, self%nlocs + tmp = vcoordprofile%vals(:,iobs) + tmp2 = obsvcoord(iobs) + call vert_interp_weights(vcoordprofile%nval, tmp2, tmp, self%wi(iobs), self%wf(iobs)) + enddo + +! Cleanup memory + deallocate(obsvcoord) + deallocate(tmp) + +end subroutine radarradialvelocity_tlad_settraj_ + +! ------------------------------------------------------------------------------ + +subroutine radarradialvelocity_simobs_tl_(self, geovals, obss, nvars, nlocs, hofx) + implicit none + class(ufo_radarradialvelocity_tlad), intent(in) :: self + type(ufo_geovals), intent(in) :: geovals + integer, intent(in) :: nvars, nlocs + real(c_double), intent(inout) :: hofx(nvars, nlocs) + type(c_ptr), value, intent(in) :: obss + + integer :: iobs, ivar, nvars_geovars + type(ufo_geoval), pointer :: profile + character(len=MAXVARLEN) :: geovar + + real(kind_real), allocatable :: vfields(:,:) ! background fields (u,v,w) interplated vertically to the observation height + + +! Number of variables in geovars (without the vertical coordinate) + nvars_geovars = self%geovars%nvars() - 1 + allocate(vfields(nvars_geovars,nlocs)) + vfields=0.0 + + do ivar = 1, nvars_geovars + geovar = self%geovars%variable(ivar) + +! Get profile for this variable from geovals + call ufo_geovals_get_var(geovals, geovar, profile) + +! Interpolate from geovals to observational location into hofx + do iobs = 1, nlocs + call vert_interp_apply_tl(profile%nval, profile%vals(:,iobs), & + & vfields(ivar,iobs), self%wi(iobs), self%wf(iobs)) + enddo + enddo + + do ivar = 1, nvars + do iobs=1,nlocs + hofx(ivar,iobs) = vfields(1,iobs)*self%cosazm_costilt(iobs) & + + vfields(2,iobs)*self%sinazm_costilt(iobs) + enddo + end do + + deallocate(vfields) + +end subroutine radarradialvelocity_simobs_tl_ + +! ------------------------------------------------------------------------------ + +subroutine radarradialvelocity_simobs_ad_(self, geovals, obss, nvars, nlocs, hofx) + implicit none + class(ufo_radarradialvelocity_tlad), intent(in) :: self + type(ufo_geovals), intent(inout) :: geovals + integer, intent(in) :: nvars, nlocs + real(c_double), intent(in) :: hofx(nvars, nlocs) + type(c_ptr), value, intent(in) :: obss + + integer :: iobs, ivar, nvars_geovars + type(ufo_geoval), pointer :: profile + character(len=MAXVARLEN) :: geovar + real(c_double) :: missing + real(kind_real), allocatable :: vfields(:,:) ! background fields (u,v,w) interplated vertically to the observation height + + missing = missing_value(missing) + +! Number of variables in geovars (without the vertical coordinate) + nvars_geovars = self%geovars%nvars() - 1 + allocate(vfields(nvars_geovars,nlocs)) + + vfields=0.0 + do ivar = 1, nvars + do iobs=1,nlocs + ! no vertical velocity and terminal velocity in GSI rw observer, it can add + ! in future after acceptance test + vfields(1,iobs) = vfields(1,iobs) + hofx(ivar,iobs)*self%cosazm_costilt(iobs) + vfields(2,iobs) = vfields(2,iobs) + hofx(ivar,iobs)*self%sinazm_costilt(iobs) + enddo + end do + + do ivar = 1, nvars_geovars +! Get the name of input variable in geovals + geovar = self%geovars%variable(ivar) + +! Get profile for this variable from geovals + call ufo_geovals_get_var(geovals, geovar, profile) + +! Allocate geovals profile if not yet allocated + if (.not. allocated(profile%vals)) then + profile%nlocs = self%nlocs + profile%nval = self%nval + allocate(profile%vals(profile%nval, profile%nlocs)) + profile%vals(:,:) = 0.0_kind_real + endif + if (.not. geovals%linit ) geovals%linit=.true. + +! Interpolate from geovals to observational location into hofx + do iobs = 1, nlocs + call vert_interp_apply_ad(profile%nval, profile%vals(:,iobs), & + & vfields(ivar,iobs), self%wi(iobs), self%wf(iobs)) + enddo + enddo + + + deallocate(vfields) +end subroutine radarradialvelocity_simobs_ad_ + +! ------------------------------------------------------------------------------ + +subroutine radarradialvelocity_tlad_cleanup_(self) + implicit none + class(ufo_radarradialvelocity_tlad), intent(inout) :: self + self%nval = 0 + self%nlocs = 0 + if (allocated(self%wi)) deallocate(self%wi) + if (allocated(self%wf)) deallocate(self%wf) + if (allocated(self%cosazm_costilt)) deallocate(self%cosazm_costilt) + if (allocated(self%sinazm_costilt)) deallocate(self%sinazm_costilt) + if (allocated(self%sintilt)) deallocate(self%sintilt) + if (allocated(self%vterminal)) deallocate(self%vterminal) +end subroutine radarradialvelocity_tlad_cleanup_ + +! ------------------------------------------------------------------------------ + +subroutine destructor(self) + type(ufo_radarradialvelocity_tlad), intent(inout) :: self + + call self%cleanup() + +end subroutine destructor + +! ------------------------------------------------------------------------------ + +end module ufo_radarradialvelocity_tlad_mod diff --git a/src/ufo/radarreflectivity/CMakeLists.txt b/src/ufo/radarreflectivity/CMakeLists.txt deleted file mode 100644 index 73ad9ce92..000000000 --- a/src/ufo/radarreflectivity/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -# (C) Copyright 2017-2018 UCAR. -# -# This software is licensed under the terms of the Apache Licence Version 2.0 -# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - -set ( radarreflectivity_files - ObsRadarReflectivity.h - ObsRadarReflectivity.cc - ObsRadarReflectivity.interface.F90 - ObsRadarReflectivity.interface.h - ufo_radarreflectivity_mod.F90 -) - -PREPEND( _p_radarreflectivity_files "radarreflectivity" ${radarreflectivity_files} ) - -set ( radarreflectivity_src_files - ${_p_radarreflectivity_files} - PARENT_SCOPE -) diff --git a/src/ufo/radarreflectivity/ufo_radarreflectivity_mod.F90 b/src/ufo/radarreflectivity/ufo_radarreflectivity_mod.F90 deleted file mode 100644 index d1fd79f36..000000000 --- a/src/ufo/radarreflectivity/ufo_radarreflectivity_mod.F90 +++ /dev/null @@ -1,131 +0,0 @@ -! (C) Copyright 2017-2018 UCAR -! -! This software is licensed under the terms of the Apache Licence Version 2.0 -! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - -!> Fortran module for radarreflectivity observation operator - -module ufo_radarreflectivity_mod - - use ufo_vars_mod - use oops_variables_mod - - implicit none - private - -!> Fortran derived type for the observation type - - type, public :: ufo_radarreflectivity - private - type(oops_variables), public :: obsvars - type(oops_variables), public :: geovars - character(len=MAXVARLEN), public :: v_coord ! GeoVaL to use to interpolate in vertical - contains - procedure :: setup => ufo_radarreflectivity_setup - procedure :: simobs => ufo_radarreflectivity_simobs - end type ufo_radarreflectivity - - character(len=maxvarlen), dimension(1), parameter :: geovars_default = (/var_refl/) - -contains - -! ------------------------------------------------------------------------------ -subroutine ufo_radarreflectivity_setup(self, yaml_conf) - use fckit_configuration_module, only: fckit_configuration - use iso_c_binding - implicit none - class(ufo_radarreflectivity), intent(inout) :: self - type(fckit_configuration), intent(in) :: yaml_conf - - character(kind=c_char,len=:), allocatable :: coord_name - - call self%geovars%push_back(geovars_default) - - if( yaml_conf%has("VertCoord") ) then - call yaml_conf%get_or_die("VertCoord",coord_name) - self%v_coord = coord_name - if( trim(self%v_coord) .ne. var_z ) then - call abor1_ftn("ufo_radarreflectivity: incorrect vertical coordinate specified") - endif - else ! default - self%v_coord = var_z - endif - - call self%geovars%push_back(self%v_coord) - -end subroutine ufo_radarreflectivity_setup - -! ------------------------------------------------------------------------------ -! Code in this routine is for radarreflectivity only -subroutine ufo_radarreflectivity_simobs(self, geovals, obss, nvars, nlocs, hofx) - use kinds - use vert_interp_mod - use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var - use obsspace_mod - implicit none - class(ufo_radarreflectivity), intent(in) :: self - integer, intent(in) :: nvars, nlocs - type(ufo_geovals), intent(in) :: geovals - real(c_double), intent(inout) :: hofx(nvars, nlocs) - type(c_ptr), value, intent(in) :: obss - - ! Local variables - integer :: iobs, ivar - real(kind_real), dimension(:), allocatable :: obsvcoord - type(ufo_geoval), pointer :: vcoordprofile, profile - real(kind_real), allocatable :: wf(:) - integer, allocatable :: wi(:) - - character(len=MAXVARLEN) :: geovar - - real(kind_real), allocatable :: tmp(:) - real(kind_real) :: tmp2 - - !@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - - ! Get height profiles from geovals - call ufo_geovals_get_var(geovals, self%v_coord, vcoordprofile) - - - ! Get the observation vertical coordinates - allocate(obsvcoord(nlocs)) - call obsspace_get_db(obss, "MetaData", "height", obsvcoord) - - ! Allocate arrays for interpolation weights - - allocate(wi(nlocs)) - allocate(wf(nlocs)) - - ! Calculate the interpolation weights - allocate(tmp(vcoordprofile%nval)) - do iobs = 1, nlocs - tmp = vcoordprofile%vals(:,iobs) - tmp2 = obsvcoord(iobs) - call vert_interp_weights(vcoordprofile%nval, tmp2, tmp, wi(iobs), wf(iobs)) - enddo - - do ivar = 1, nvars - ! Get the name of input variable in geovals - geovar = self%geovars%variable(ivar) - - ! Get profile for this variable from geovals - call ufo_geovals_get_var(geovals, geovar, profile) - - ! Interpolate from geovals to observational location into hofx - do iobs = 1, nlocs - call vert_interp_apply(profile%nval, profile%vals(:,iobs), & - & hofx(ivar,iobs), wi(iobs), wf(iobs)) - enddo - enddo - - ! Cleanup memory - deallocate(obsvcoord) - deallocate(wi) - deallocate(wf) - -end subroutine ufo_radarreflectivity_simobs - - -! ------------------------------------------------------------------------------ - -end module ufo_radarreflectivity_mod diff --git a/src/ufo/rttov/ObsRadianceRTTOVTLAD.cc b/src/ufo/rttov/ObsRadianceRTTOVTLAD.cc index fca6421d8..4f50815d6 100644 --- a/src/ufo/rttov/ObsRadianceRTTOVTLAD.cc +++ b/src/ufo/rttov/ObsRadianceRTTOVTLAD.cc @@ -29,7 +29,7 @@ static LinearObsOperatorMaker makerRTTOVTL_("RTTOV"); ObsRadianceRTTOVTLAD::ObsRadianceRTTOVTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOperRadianceRTTOV_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOperRadianceRTTOV_(0), varin_() { // parse channels from the config and create variable names const oops::Variables & observed = odb.obsvariables(); @@ -53,7 +53,7 @@ ObsRadianceRTTOVTLAD::~ObsRadianceRTTOVTLAD() { void ObsRadianceRTTOVTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics & ydiags) { - ufo_radiancerttov_tlad_settraj_f90(keyOperRadianceRTTOV_, geovals.toFortran(), odb_, + ufo_radiancerttov_tlad_settraj_f90(keyOperRadianceRTTOV_, geovals.toFortran(), obsspace(), ydiags.toFortran()); oops::Log::trace() << "ObsRadianceRTTOVTLAD::setTrajectory done" << std::endl; } @@ -61,7 +61,7 @@ void ObsRadianceRTTOVTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias // ----------------------------------------------------------------------------- void ObsRadianceRTTOVTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_radiancerttov_simobs_tl_f90(keyOperRadianceRTTOV_, geovals.toFortran(), odb_, + ufo_radiancerttov_simobs_tl_f90(keyOperRadianceRTTOV_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); oops::Log::trace() << "ObsRadianceRTTOVTLAD::simulateObsTL done" << std::endl; } @@ -69,7 +69,7 @@ void ObsRadianceRTTOVTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVecto // ----------------------------------------------------------------------------- void ObsRadianceRTTOVTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_radiancerttov_simobs_ad_f90(keyOperRadianceRTTOV_, geovals.toFortran(), odb_, + ufo_radiancerttov_simobs_ad_f90(keyOperRadianceRTTOV_, geovals.toFortran(), obsspace(), ovec.nvars(), ovec.nlocs(), ovec.toFortran()); oops::Log::trace() << "ObsRadianceRTTOVTLAD::simulateObsAD done" << std::endl; } diff --git a/src/ufo/rttov/ObsRadianceRTTOVTLAD.h b/src/ufo/rttov/ObsRadianceRTTOVTLAD.h index 9a64c267a..8b0185d9e 100644 --- a/src/ufo/rttov/ObsRadianceRTTOVTLAD.h +++ b/src/ufo/rttov/ObsRadianceRTTOVTLAD.h @@ -57,7 +57,6 @@ class ObsRadianceRTTOVTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOperRadianceRTTOV_; - const ioda::ObsSpace& odb_; oops::Variables varin_; }; diff --git a/src/ufo/rttov/ufo_radiancerttov_mod.F90 b/src/ufo/rttov/ufo_radiancerttov_mod.F90 index af4502a24..a2d9eee96 100644 --- a/src/ufo/rttov/ufo_radiancerttov_mod.F90 +++ b/src/ufo/rttov/ufo_radiancerttov_mod.F90 @@ -93,8 +93,10 @@ subroutine ufo_radiancerttov_delete(self) end subroutine ufo_radiancerttov_delete ! ------------------------------------------------------------------------------ - subroutine ufo_radiancerttov_simobs(self, geovals, obss, nvars, nlocs, hofx, hofxdiags) + subroutine ufo_radiancerttov_simobs(self, geovals, obss, nvars, nlocs, & + hofx, hofxdiags, ob_info) use fckit_mpi_module, only: fckit_mpi_comm + use ufo_rttovonedvarcheck_ob_mod implicit none @@ -105,239 +107,223 @@ subroutine ufo_radiancerttov_simobs(self, geovals, obss, nvars, nlocs, hofx, hof real(c_double), intent(inout) :: hofx(nvars,nlocs) type(ufo_geovals), intent(inout) :: hofxdiags !non-h(x) diagnostics + type(ufo_rttovonedvarcheck_ob), optional, intent(inout) :: ob_info real(c_double) :: missing type(fckit_mpi_comm) :: f_comm ! Local Variables - character(*), parameter :: ROUTINE_NAME = 'ufo_radiancerttov_simobs' - type(ufo_geoval), pointer :: temp + character(*), parameter :: routine_name = 'ufo_radiancerttov_simobs' + type(rttov_chanprof), allocatable :: chanprof(:) + type(ufo_geoval), pointer :: geoval_temp - integer :: nprofiles - integer(kind=jpim) :: errorstatus ! Return error status of RTTOV subroutine calls + integer :: nprofiles, nlevels + integer(kind=jpim) :: errorstatus ! Error status of RTTOV subroutine calls - integer :: i_inst, nlevels, nchan_total, ichan, iprof, prof - integer :: nprof_sim, nprof_max_sim + integer :: i_inst, iprof_rttov, iprof, ichan, ichan_sim + integer :: nprof_sim, nprof_max_sim, nchan_total integer :: prof_start, prof_end logical :: jacobian_needed - logical, allocatable :: skip_profiles(:) - - logical :: obs_info - - logical :: layer_quantities include 'rttov_direct.interface' include 'rttov_k.interface' - include 'rttov_print_profile.interface' - include 'rttov_user_profile_checkinput.interface' - !DAR: What is this? + write(message,'(A, A, I0, A, I0, A)') trim(routine_name), ': Simulating observations' + call fckit_log%debug(message) + + !Initialisations + missing = missing_value(missing) + hofx = missing + + !Return the name and name length of obsspace communicator (from ioda) call obsspace_get_comm(obss, f_comm) - ! Get number of profile and layers from geovals - ! --------------------------------------------- + !! Parse hofxdiags%variables into independent/dependent variables and channel assumed formats: + ! Note this sets the jacobian_needed flag + !! jacobian var --> _jacobian__ + !! non-jacobian var --> _ + call parse_hofxdiags(hofxdiags, jacobian_needed) + ! Get number of profiles and levels from geovals nprofiles = geovals % nlocs - if (ufo_vars_getindex(geovals%variables, var_ts) > 0) then - call ufo_geovals_get_var(geovals, var_ts, temp) - nlevels = temp % nval - layer_quantities = .false. + call ufo_geovals_get_var(geovals, var_ts, geoval_temp) + nlevels = geoval_temp % nval + nullify(geoval_temp) + + ! Allocate RTTOV profiles for ALL geovals for the direct calculation + write(message,'(A, A, I0, A, I0, A)') & + trim(routine_name), ': Allocating ', nprofiles, ' profiles with ', nlevels, ' levels' + call fckit_log%debug(message) + + call self % RTprof % alloc_profs(errorstatus, self % conf, nprofiles, nlevels, init=.true., asw=1) + + !Assign the atmospheric and surface data from the GeoVaLs + write(message,'(A, A, I0, A, I0, A)') & + trim(routine_name), ': Creating RTTOV profiles from geovals' + call fckit_log%debug(message) + if(present(ob_info)) then + call self % RTprof % setup(geovals,obss,self % conf,ob_info=ob_info) + else + call self % RTprof % setup(geovals,obss,self % conf) endif - nullify(temp) + !DAR: Removing sensor_loop until it's demonstrated to be needed and properly tested + ! at the moment self % channels is a single 1D array so cannot adequately contain more than one set of channels +! Sensor_Loop:do i_inst = 1, self % conf % nSensors + i_inst = 1 - missing = missing_value(missing) - hofx = missing - - errorstatus = 0_jpim - nchan_total = 0 - - !DARFIX: This isn't ideal because it's not going to work for multiple instruments but we'll deal with that later + ! Number of channels to be simulated for this instrument (from the configuration, not necessarily the full instrument complement) nchan_inst = size(self % channels) - !! Parse hofxdiags%variables into independent/dependent variables and channel - !! assumed formats: - ! Note this sets jacobian_needed - !! jacobian var --> _jacobian__ - !! non-jacobian var --> _ - call parse_hofxdiags(hofxdiags, jacobian_needed) + ! Maximum number of profiles to be processed by RTTOV per pass + if(self % conf % prof_by_prof) then + nprof_max_sim = 1 + else + nprof_max_sim = max(1,self % conf % nchan_max_sim / nchan_inst) + endif + nprof_sim = min(nprof_max_sim, nprofiles) - Sensor_Loop:do i_inst = 1, self % conf % nSensors + ! Determine the total number of radiances to simulate (nchan_sim). + nchan_sim = nprof_sim * size(self%channels) - ! Ensure the options and coefficients are consistent - call rttov_user_options_checkinput(errorstatus, self % conf % rttov_opts, & - self % conf % rttov_coef_array(i_inst)) + ! Allocate structures for RTTOV direct code (and, if needed, K code) + write(message,'(A,A,I0,A,I0,A)') & + trim(routine_name), ': Allocating resources for RTTOV direct code: ', nprof_sim, ' and ', nchan_sim, ' channels' + call fckit_log%debug(message) + call self % RTprof % alloc_direct(errorstatus, self % conf, nprof_sim, nchan_sim, nlevels, init=.true., asw=1) - if (errorstatus /= errorstatus_success) then - write(message,'(A, I6)') 'after rttov_user_options_checkinput: error = ',& - errorstatus - call fckit_log%info(message) - end if + if (jacobian_needed) then + write(message,'(A,A,I0,A,I0,A)') & + trim(routine_name), ': Allocating resources for RTTOV K code: ', nprof_sim, ' and ', nchan_sim, ' channels' + call fckit_log%debug(message) - ! keep journal of which profiles have no obs data these will be skipped - allocate(Skip_Profiles(nprofiles)) - Skip_Profiles(:) = .false. - call ufo_rttov_skip_profiles(nProfiles,nchan_inst,self%channels,obss,Skip_Profiles) + call self % RTprof % alloc_profs_K(errorstatus, self % conf, nchan_sim, nlevels, init=.true., asw=1) + call self % RTprof % alloc_k(errorstatus, self % conf, nprof_sim, nchan_sim, nlevels, init=.true., asw=1) + endif - ! Determine the total number of radiances to simulate (nchan_sim). - ! In this example we simulate all specified channels for each profile, but - ! in general one can simulate a different number of channels for each profile. + ! Used for keeping track of profiles for setting emissivity + allocate(self % RTprof % chanprof ( nprofiles * nchan_inst )) - nprof_max_sim = self % conf % nchan_max_sim / nchan_inst - nprof_sim = min(nprof_max_sim, nprofiles) + prof_start = 1; prof_end = nprofiles + nchan_total = 0 - prof_start = 1 - prof_end = nprofiles + RTTOV_loop : do while (prof_start <= prof_end) - !DARFIX should actually be packed count of skip_profiles * SIZE(channels) + ! Reduce number of simulated profiles/channel if at end of the of profiles to be processed nprof_sim = min(nprof_sim, prof_end - prof_start + 1) nchan_sim = nprof_sim * size(self%channels) - ! -------------------------------------------------------------------------- - ! Allocate RTTOV input and output structures - ! -------------------------------------------------------------------------- - if (.not. jacobian_needed) then - ! allocate RTTOV resources - call self % RTprof % alloc(errorstatus, self % conf, nprof_sim, nchan_sim, nlevels, init=.true., asw=1) - else - call self % RTprof % alloc(errorstatus, self % conf, nprof_sim, nchan_sim, nlevels, init=.true., asw=2) - endif + ! allocate and initialise local chanprof structure + allocate(chanprof ( nchan_sim )) + chanprof(:) % prof = 0 + chanprof(:) % chan = 0 - self % RTProf % profiles(:) % skin % surftype = -1_jpim + ! index for simulated channel + ichan_sim = 0_jpim - do while (prof_start <= prof_end) - self % RTprof % chanprof(:) % prof = 0 - self % RTprof % chanprof(:) % chan = 0 + ! Build the list of profile/channel indices in chanprof + do iprof_rttov = 1, nprof_sim - nchan_sim = 0_jpim - nprof_sim = min(nprof_sim, prof_end - prof_start + 1) + ! iprof is the index for the full set of RTTOV profiles + iprof = prof_start + iprof_rttov - 1 - ! -------------------------------------------------------------------------- - ! Build the list of profile/channel indices in chanprof - ! -------------------------------------------------------------------------- + do ichan = 1, nchan_inst + ichan_sim = ichan_sim + 1_jpim + chanprof(ichan_sim) % prof = iprof_rttov ! this refers to the slice of the RTprofile array passed to RTTOV + chanprof(ichan_sim) % chan = self % channels(ichan) + self % RTprof % chanprof(nchan_total + ichan_sim) % prof = iprof ! this refers to the index of the profile from the geoval + self % RTprof % chanprof(nchan_total + ichan_sim) % chan = self % channels(ichan) + end do + nchan_sim = ichan_sim - do iprof = 1, min(nprof_sim, prof_end - prof_start + 1) - if(.not. Skip_Profiles(prof_start + iprof - 1)) then - do ichan = 1, nchan_inst + if(self % conf % RTTOV_profile_checkinput) call self % RTprof % check(self % conf, iprof, i_inst) + if(any(self % conf % inspect == iprof)) call self % RTprof % print(self % conf, iprof, i_inst) + + end do - nchan_sim = nchan_sim + 1_jpim - self % RTprof % chanprof(nchan_sim) % prof = iprof - self % RTprof % chanprof(nchan_sim) % chan = self % channels(ichan) - end do - else - if (debug) write(*,*) 'skipping ', iprof, prof_start + iprof - 1 - end if - end do + ! Set surface emissivity + call self % RTProf % init_emissivity(self % conf, prof_start) - !Assign the data from the GeoVaLs - !-------------------------------- + ! -------------------------------------------------------------------------- + ! Call RTTOV model + ! -------------------------------------------------------------------------- + !N.B. different from TL/AD as we don't need to retain the full profiles_k so data can be overwritten + + if (jacobian_needed) then + call rttov_k( & + errorstatus, &! out error flag + chanprof(1:nchan_sim), &! in LOCAL channel and profile index structure + self % conf % rttov_opts, &! in options structure + self % RTProf % profiles(prof_start:prof_start + nprof_sim -1), &! in profile array + self % RTProf % profiles_k(1:nchan_sim), &! in profile array + self % conf % rttov_coef_array(i_inst), &! in coefficients structure + self % RTProf % transmission, &! inout computed transmittances + self % RTProf % transmission_k, &! inout computed transmittances + self % RTProf % radiance, &! inout computed radiances + self % RTProf % radiance_k, &! inout computed radiances + calcemis = self % RTProf % calcemis(1:nchan_sim), &! in flag for internal emissivity calcs + emissivity = self % RTProf % emissivity(1:nchan_sim), &!, &! inout input/output emissivities per channel + emissivity_k = self % RTProf % emissivity_k(1:nchan_sim))!, &! inout input/output emissivities per channel - obs_info = .false. ! this will be replaced by an optional structure coming from 1DVar - if(obs_info) then - call load_atm_data_rttov(geovals,obss,self % RTprof % profiles,prof_start,self % conf,layer_quantities,obs_info=obs_info) - call load_geom_data_rttov(obss,self % RTprof % profiles,prof_start,obs_info=obs_info) - else - call load_atm_data_rttov(geovals,obss,self % RTprof % profiles,prof_start,self%conf,layer_quantities) - call load_geom_data_rttov(obss,self % RTprof % profiles,prof_start) + if ( errorstatus /= errorstatus_success ) then + write(message,'(A, 2I6)') 'after rttov_k: error ', errorstatus, i_inst + call abor1_ftn(message) end if - ! -------------------------------------------------------------------------- - ! Set surface emissivity - ! -------------------------------------------------------------------------- - - call self % RTProf % init_emissivity(self % conf) - - if(self % conf % RTTOV_profile_checkinput) then - if (self % conf % inspect > 0) then - call rttov_print_profile(self % RTprof % profiles(self % conf % inspect)) - endif - - ! no error checking, could check multiple profiles in loop but what would be the point - call rttov_user_profile_checkinput(rttov_errorstatus, & - self % conf % rttov_opts, & - self % conf % rttov_coef_array(i_inst), & - self % RTprof % profiles(self % conf % inspect)) - endif - - - ! -------------------------------------------------------------------------- - ! Call RTTOV model - ! -------------------------------------------------------------------------- - - if (jacobian_needed) then - - call rttov_k( & - errorstatus, &! out error flag - self % RTProf % chanprof(nchan_total + 1:nchan_total + nchan_sim), &! in LOCAL channel and profile index structure - self % conf % rttov_opts, &! in options structure - self % RTProf % profiles, &! in profile array - self % RTProf % profiles_k(nchan_total + 1 : nchan_total + nchan_sim), &! in profile array - self % conf % rttov_coef_array(i_inst), &! in coefficients structure - self % RTProf % transmission, &! inout computed transmittances - self % RTProf % transmission_k, &! inout computed transmittances - self % RTProf % radiance, &! inout computed radiances - self % RTProf % radiance_k, &! inout computed radiances - calcemis = self % RTProf % calcemis, &! in flag for internal emissivity calcs - emissivity = self % RTProf % emissivity, &!, &! inout input/output emissivities per channel - emissivity_k = self % RTProf % emissivity_k)!, &! inout input/output emissivities per channel - - if ( errorstatus /= errorstatus_success ) then - write(message,'(A, 2I6)') 'after rttov_k: error ', errorstatus, i_inst - call abor1_ftn(message) - end if - - else - call rttov_direct( & - errorstatus, &! out error flag - self % RTprof % chanprof(1:nchan_sim), &! in channel and profile index structure - self % conf % rttov_opts, &! in options structure - self % RTProf % profiles(1:nprof_sim), &! in profile array - self % conf % rttov_coef_array(i_inst), &! in coefficients structure - self % RTProf % transmission, &! inout computed transmittances - self % RTProf % radiance, &! inout computed radiances - calcemis = self % RTProf % calcemis(1:nchan_sim), &! in flag for internal emissivity calcs - emissivity = self % RTProf % emissivity(1:nchan_sim))!, &! inout input/output emissivities per channel - - if ( errorstatus /= errorstatus_success ) then - write(message,'(A, 2I6)') 'after rttov_direct: error ', errorstatus, i_inst - call abor1_ftn(message) - end if - endif ! jacobian_needed - - !DARFIX: This should be available only when debugging - ! Write out progress through batch - if (debug) write(*,'(A1, i0, A, i0)',ADVANCE="NO") achar(13), prof_start+nprof_sim-1, ' locations processed out of ', geovals%nlocs - - ! Put simulated brightness temperature into hofx - ! ---------------------------------------------- - do ichan=1, nchan_sim, size(self%channels) - prof = self % RTProf % chanprof(ichan)%prof - hofx(1:size(self%channels),prof_start + prof - 1) = self % RTprof % radiance % bt(ichan:ichan+size(self%channels)-1) - enddo - - ! Put simulated diagnostics into hofxdiags - ! ---------------------------------------------- - if(hofxdiags%nvar > 0) call populate_hofxdiags(self % RTProf, self % RTProf % chanprof, hofxdiags) - - nchan_total = nchan_total + nchan_sim - prof_start = prof_start + nprof_sim + else + call rttov_direct( & + errorstatus, &! out error flag + chanprof(1:nchan_sim), &! in channel and profile index structure + self % conf % rttov_opts, &! in options structure + self % RTProf % profiles(prof_start:prof_start + nprof_sim -1), &! in profile array + self % conf % rttov_coef_array(i_inst), &! in coefficients structure + self % RTProf % transmission, &! inout computed transmittances + self % RTProf % radiance, &! inout computed radiances + calcemis = self % RTProf % calcemis(1:nchan_sim), &! in flag for internal emissivity calcs + emissivity = self % RTProf % emissivity(1:nchan_sim))!, &! inout input/output emissivities per channel + + if ( errorstatus /= errorstatus_success ) then + write(message,'(A, 2I6)') 'after rttov_direct: error ', errorstatus, i_inst + call abor1_ftn(message) + end if + endif ! jacobian_needed - end do + ! Put simulated brightness temperature into hofx + do ichan = 1, nchan_sim, size(self%channels) + iprof = self % RTProf % chanprof(nchan_total + ichan)%prof + hofx(1:size(self%channels),iprof) = self % RTprof % radiance % bt(ichan:ichan+size(self%channels)-1) + enddo - ! Deallocate structures for rttov_direct + ! Put simulated diagnostics into hofxdiags + if(hofxdiags%nvar > 0) call populate_hofxdiags(self % RTProf, chanprof, self % conf, hofxdiags) - call self % RTprof % alloc(errorstatus, self % conf, nprof_sim, nchan_sim, nlevels, asw=0) + ! increment profile and channel counters + nchan_total = nchan_total + nchan_sim + prof_start = prof_start + nprof_sim - if (errorstatus /= errorstatus_success) then - write(message,'(A, 2I6)') & - 'after rttov_alloc_direct (deallocation): errorstatus, i_inst =', & - errorstatus, i_inst - call abor1_ftn(message) - end if + ! deallocate local chanprof so it can be re-allocated with a different number of channels if reqd. + deallocate(chanprof) - end do Sensor_Loop - write(*,*) + end do RTTOV_loop + + ! Deallocate structures for rttov_direct + if(jacobian_needed) then + call self % RTprof % alloc_k(errorstatus, self % conf, -1, -1, -1, asw=0) + call self % RTprof % alloc_profs_K(errorstatus, self % conf, -1, -1, asw=0) + endif + call self % RTprof % alloc_direct(errorstatus, self % conf, -1, -1, -1, asw=0) + call self % RTprof % alloc_profs(errorstatus, self % conf, -1, -1, asw=0) + + deallocate(self % RTprof % chanprof) + + if (errorstatus /= errorstatus_success) then + write(message,'(A, 2I6)') & + 'after rttov_alloc_direct (deallocation): errorstatus, i_inst =', errorstatus, i_inst + call abor1_ftn(message) + end if + + !end do Sensor_Loop end subroutine ufo_radiancerttov_simobs diff --git a/src/ufo/rttov/ufo_radiancerttov_tlad_mod.F90 b/src/ufo/rttov/ufo_radiancerttov_tlad_mod.F90 index 1c1269228..d4b7b9f86 100644 --- a/src/ufo/rttov/ufo_radiancerttov_tlad_mod.F90 +++ b/src/ufo/rttov/ufo_radiancerttov_tlad_mod.F90 @@ -20,7 +20,7 @@ module ufo_radiancerttov_tlad_mod use ufo_radiancerttov_utils_mod use rttov_types - use rttov_const, only : errorstatus_success + use rttov_const ! errorstatus and gas_id use rttov_unix_env implicit none @@ -40,7 +40,6 @@ module ufo_radiancerttov_tlad_mod integer :: nlevels logical :: ltraj - logical, allocatable :: Skip_Profiles(:) contains procedure :: setup => ufo_radiancerttov_tlad_setup @@ -125,8 +124,6 @@ subroutine ufo_radiancerttov_tlad_delete(self) call rttov_conf_delete(self % conf) call rttov_conf_delete(self % conf_traj) - if (allocated(self%Skip_Profiles)) deallocate(self%Skip_Profiles) - end subroutine ufo_radiancerttov_tlad_delete ! ------------------------------------------------------------------------------ @@ -141,193 +138,179 @@ subroutine ufo_radiancerttov_tlad_settraj(self, geovals, obss, hofxdiags) type(c_ptr), value, intent(in) :: obss type(ufo_geovals), intent(inout) :: hofxdiags !non-h(x) diagnostics + real(c_double) :: missing type(fckit_mpi_comm) :: f_comm ! Local Variables - character(*), parameter :: ROUTINE_NAME = 'ufo_radiancerttov_tlad_settraj' - type(ufo_geoval), pointer :: temp + character(*), parameter :: routine_name = 'ufo_radiancerttov_tlad_settraj' + type(rttov_chanprof), allocatable :: chanprof(:) + type(ufo_geoval), pointer :: geoval_temp integer(kind=jpim) :: errorstatus ! Return error status of RTTOV subroutine calls - integer :: nchan_max_sim, nchan_count, nchan_total - integer :: nprof_sim, nprof_max_sim - - integer :: iprof, ichan, i_inst + integer :: i_inst, nchan_total, ichan, iprof, prof, iprof_rttov + integer :: nprof_sim, nprof_max_sim, ichan_sim integer :: prof_start, prof_end - logical :: layer_quantities logical :: jacobian_needed include 'rttov_k.interface' - include 'rttov_print_profile.interface' - include 'rttov_user_profile_checkinput.interface' - !DAR: What is this? + !Initialisations + missing = missing_value(missing) + + !Return the name and name length of obsspace communicator (from ioda) call obsspace_get_comm(obss, f_comm) + + !! Parse hofxdiags%variables into independent/dependent variables and channel assumed formats: + ! Note this sets the jacobian_needed flag + !! jacobian var --> _jacobian__ + !! non-jacobian var --> _ + call parse_hofxdiags(hofxdiags, jacobian_needed) - ! Get number of profile and layers from geovals - ! --------------------------------------------- + ! Get number of profiles and levels from geovals self % nprofiles = geovals % nlocs - if (ufo_vars_getindex(geovals%variables, var_ts) > 0) then - call ufo_geovals_get_var(geovals, var_ts, temp) - self % nlevels = temp % nval ! lfric passing nlevels - layer_quantities = .false. - endif - - nullify(temp) - - errorstatus = 0_jpim - nchan_count = 0 - nchan_total = 0 - + call ufo_geovals_get_var(geovals, var_ts, geoval_temp) + self % nlevels = geoval_temp % nval + nullify(geoval_temp) + + ! Allocate RTTOV profiles for ALL geovals for the direct calculation + write(message,'(A, A, I0, A, I0, A)') & + trim(routine_name), ': Allocating ', self % nprofiles, ' profiles with ', self % nlevels, ' levels' + call fckit_log%debug(message) + call self % RTprof_K % alloc_profs(errorstatus, self % conf, self % nprofiles, self % nlevels, init=.true., asw=1) + + !Assign the atmospheric and surface data from the GeoVaLs + write(message,'(A, A, I0, A, I0, A)') trim(routine_name), ': Creating RTTOV profiles from geovals' + call fckit_log%debug(message) + call self % RTprof_K % setup(geovals,obss,self % conf) + + !DAR: Removing sensor_loop until it's demonstrated to be needed and properly tested + ! at the moment self % channels is a single 1D array so cannot adequately contain more than one set of channels +! Sensor_Loop:do i_inst = 1, self % conf % nSensors + i_inst = 1 + + ! Number of channels to be simulated for this instrument (from the configuration, not necessarily the full instrument complement) nchan_inst = size(self % channels) - nchan_max_sim = self % nprofiles * nchan_inst ! Maximum number of channels to pass to RTTOV to simulate + ! Allocate memory for *ALL* RTTOV_K channels + write(message,'(A,A,I0,A)') & + trim(routine_name), ': Allocating Trajectory resources for RTTOV K: ', self % nprofiles * nchan_inst, ' total channels' + call self % RTprof_K % alloc_profs_K(errorstatus, self % conf, self % nprofiles * nchan_inst, self % nlevels, init=.true., asw=1) - !! Parse hofxdiags%variables into independent/dependent variables and channel - !! assumed formats: - !! jacobian var --> _jacobian__ - !! non-jacobian var --> _ - call parse_hofxdiags(hofxdiags, jacobian_needed) + ! Used for keeping track of profiles for setting emissivity + allocate(self % RTprof_K % chanprof ( self % nprofiles * nchan_inst )) - Sensor_Loop:do i_inst = 1, self % conf % nSensors - - ! Ensure the options and coefficients are consistent - call rttov_user_options_checkinput(errorstatus, self % conf % rttov_opts, & - self % conf % rttov_coef_array(i_inst)) + ! Maximum number of profiles to be processed by RTTOV per pass + if(self % conf % prof_by_prof) then + nprof_max_sim = 1 + else + nprof_max_sim = max(1,self % conf % nchan_max_sim / nchan_inst) + endif + nprof_sim = min(nprof_max_sim, self % nprofiles) - if (errorstatus /= errorstatus_success) then - write(message,'(A, A,I6)') trim(ROUTINE_NAME), 'after rttov_user_options_checkinput: error = ',& - errorstatus - call fckit_log%info(message) - end if + ! Determine the total number of radiances to simulate (nchan_sim). + nchan_sim = nprof_sim * size(self%channels) - ! keep journal of which profiles have no obs data these will be skipped - allocate(self % Skip_Profiles(self % nprofiles)) - call ufo_rttov_skip_profiles(self%nProfiles,nchan_inst,self%channels,obss,self%Skip_Profiles) + ! Allocate structures for RTTOV direct and K code + write(message,'(A,A,I0,A,I0,A)') & + trim(routine_name), ': Allocating resources for RTTOV direct (K): ', nprof_sim, ' and ', nchan_sim, ' channels' + call fckit_log%debug(message) + call self % RTprof_K % alloc_direct(errorstatus, self % conf, nprof_sim, nchan_sim, self % nlevels, init=.true., asw=1) - ! Determine the total number of radiances to simulate (nchanprof). - ! In this example we simulate all specified channels for each profile, but - ! in general one can simulate a different number of channels for each profile. + write(message,'(A,A,I0,A,I0,A)') & + trim(routine_name), ': Allocating resources for RTTOV K code: ', nprof_sim, ' and ', nchan_sim, ' channels' + call fckit_log%debug(message) + call self % RTprof_K % alloc_k(errorstatus, self % conf, nprof_sim, nchan_sim, self % nlevels, init=.true., asw=1) - nprof_max_sim = nchan_max_sim / nchan_inst - nprof_sim = min(nprof_max_sim, self % nprofiles) + prof_start = 1; prof_end = self % nprofiles + nchan_total = 0 - prof_start = 1 - prof_end = self % nprofiles + RTTOV_loop : do while (prof_start <= prof_end) - !DARFIX should actually be packed count of skip_profiles * SIZE(channels) + ! Reduce number of simulated profiles/channel if at end of the of profiles to be processed nprof_sim = min(nprof_sim, prof_end - prof_start + 1) nchan_sim = nprof_sim * size(self%channels) - ! -------------------------------------------------------------------------- - ! Allocate RTTOV input and output structures - ! -------------------------------------------------------------------------- - - call self % RTprof_K % alloc(errorstatus, self % conf, nprof_sim, nchan_sim, self % nlevels, init=.true., asw=2) - - do while ( prof_start <= prof_end) + ! allocate and initialise local chanprof structure + allocate(chanprof ( nchan_sim )) + chanprof(:) % prof = 0 + chanprof(:) % chan = 0 - ! -------------------------------------------------------------------------- - ! Build the list of profile/channel indices in chanprof - ! -------------------------------------------------------------------------- + ! index for simulated channel + ichan_sim = 0_jpim - nchan_sim = 0_jpim - nprof_sim = min(nprof_sim, prof_end - prof_start + 1) + ! Build the list of profile/channel indices in chanprof + do iprof_rttov = 1, nprof_sim - do iprof = 1, min(nprof_sim, prof_end - prof_start + 1) - if(.not. self % Skip_Profiles(prof_start + iprof - 1)) then - do ichan = 1, nchan_inst - nchan_sim = nchan_sim + 1_jpim + ! iprof is the index for the full set of RTTOV profiles + iprof = prof_start + iprof_rttov - 1 - self % RTProf_K % chanprof(nchan_total + nchan_sim) % prof = iprof - self % RTprof_K % chanprof(nchan_total + nchan_sim) % chan = self % channels(ichan) - end do - endif + do ichan = 1, nchan_inst + ichan_sim = ichan_sim + 1_jpim + chanprof(ichan_sim) % prof = iprof_rttov ! this refers to the slice of the RTprofile array passed to RTTOV + chanprof(ichan_sim) % chan = self % channels(ichan) + self % RTprof_K % chanprof(nchan_total + ichan_sim) % prof = iprof + self % RTprof_K % chanprof(nchan_total + ichan_sim) % chan = self % channels(ichan) end do + nchan_sim = ichan_sim - !Assign the data from the GeoVaLs - !-------------------------------- - - call load_atm_data_rttov(geovals,obss,self % RTprof_K % profiles,prof_start,self % conf,layer_quantities) - call load_geom_data_rttov(obss,self % RTprof_K % profiles,prof_start) - - ! -------------------------------------------------------------------------- - ! Set surface emissivity - ! -------------------------------------------------------------------------- - call self % RTProf_K % init_emissivity(self % conf) - - ! Inintialize the K-matrix INPUT so that the results are dTb/dx - ! ------------------------------------------------------------- - - self % RTprof_K % emissivity_k(:) % emis_out = 0 - self % RTprof_K % emissivity_k(:) % emis_in = 0 - self % RTprof_K % emissivity(:) % emis_out = 0 - self % RTprof_K % radiance_k % bt(:) = 1 - self % RTprof_K % radiance_k % total(:) = 1 - - if(self % conf % RTTOV_profile_checkinput) then - if (self % conf % inspect > 0) then - ! no error checking, could check multiple profiles in loop but what would be the point - call rttov_print_profile(self % RTprof_K % profiles(self % conf % inspect)) - - call rttov_user_profile_checkinput(rttov_errorstatus, & - self % conf % rttov_opts, & - self % conf % rttov_coef_array(i_inst), & - self % RTprof_K % profiles(self % conf % inspect)) - endif - endif - - ! -------------------------------------------------------------------------- - ! Call RTTOV K model - ! -------------------------------------------------------------------------- - - call rttov_k( & - errorstatus, &! out error flag - self % RTprof_K % chanprof(nchan_total + 1:nchan_total + nchan_sim), &! in channel and profile index structure - self % conf % rttov_opts, &! in options structure - self % RTprof_K % profiles, &! in profile array - self % RTprof_K % profiles_k(nchan_total + 1 : nchan_total + nchan_sim), &! in profile array - self % conf % rttov_coef_array(i_inst), &! in coefficients structure - self % RTprof_K % transmission, &! inout computed transmittances - self % RTprof_K % transmission_k, &! inout computed transmittances - self % RTprof_K % radiance, &! inout computed radiances - self % RTprof_K % radiance_k, &! inout computed radiances - calcemis = self % RTprof_K % calcemis, &! in flag for internal emissivity calcs - emissivity = self % RTprof_K % emissivity, &!, &! inout input/output emissivities per channel - emissivity_k = self % RTprof_K % emissivity_k)!, &! inout input/output emissivities per channel - - if (self % conf % inspect > 0) then - ! no error checking, could check multiple channels here using inspect_k array (NOT IMPLEMENTED) - call rttov_print_profile(self % RTprof_K % profiles_k(self % conf % inspect)) - endif - - if ( errorstatus /= errorstatus_success ) then - write(message,'(A, A, 2I6)') trim(ROUTINE_NAME), 'after rttov_k: error ', errorstatus, i_inst - call abor1_ftn(message) - end if - - write(*,'(A1, i0, A, i0)',ADVANCE="NO") achar(13), prof_start+nprof_sim-1, ' locations processed out of ', geovals%nlocs - - ! Put simulated diagnostics into hofxdiags - ! ---------------------------------------------- - if(hofxdiags%nvar > 0) call populate_hofxdiags(self % RTprof_K, self % RTprof_K % chanprof, hofxdiags) - - prof_start = prof_start + nprof_sim - nchan_total = nchan_total + nchan_sim - - self % nchan_total = nchan_total + if(self % conf % RTTOV_profile_checkinput) call self % RTprof_K % check(self % conf, iprof, i_inst) + if(any(self % conf % inspect == iprof)) call self % RTprof_K % print(self % conf, iprof, i_inst) + end do - ! Deallocate structures for rttov_direct - end do Sensor_Loop - write(*,*) + ! Set surface emissivity + call self % RTProf_K % init_emissivity(self % conf, prof_start) + ! -------------------------------------------------------------------------- + ! Call RTTOV K model + ! -------------------------------------------------------------------------- + + call rttov_k( & + errorstatus, &! out error flag + chanprof(1:nchan_sim), &! in channel and profile index structure + self % conf % rttov_opts, &! in options structure + self % RTprof_K % profiles(prof_start:prof_start + nprof_sim - 1), &! in profile array + self % RTprof_K % profiles_k(nchan_total + 1 : nchan_total + nchan_sim), &! in profile array + self % conf % rttov_coef_array(i_inst), &! in coefficients structure + self % RTprof_K % transmission, &! inout computed transmittances + self % RTprof_K % transmission_k, &! inout computed transmittances + self % RTprof_K % radiance, &! inout computed radiances + self % RTprof_K % radiance_k, &! inout computed radiances + calcemis = self % RTprof_K % calcemis(1:nchan_sim), &! in flag for internal emissivity calcs + emissivity = self % RTprof_K % emissivity(1:nchan_sim), &!, &! inout input/output emissivities per channel + emissivity_k = self % RTprof_K % emissivity_k(1:nchan_sim))!, &! inout input/output emissivities per channel + + if ( errorstatus /= errorstatus_success ) then + write(message,'(A, A, 2I6)') trim(routine_name), 'after rttov_k: error ', errorstatus, i_inst + call abor1_ftn(message) + end if + + ! Put simulated diagnostics into hofxdiags + ! ---------------------------------------------- + if(hofxdiags%nvar > 0) call populate_hofxdiags(self % RTprof_K, chanprof, self % conf, hofxdiags) + + ! increment profile and channel counters + nchan_total = nchan_total + nchan_sim + prof_start = prof_start + nprof_sim + + self % nchan_total = nchan_total + deallocate (chanprof) + end do RTTOV_loop + + ! end do Sensor_Loop + ! Deallocate structures for rttov_direct + call self % RTprof_K % alloc_k(errorstatus, self % conf, -1, -1, -1, asw=0) + call self % RTprof_K % alloc_direct(errorstatus, self % conf, -1, -1, -1, asw=0) + call self % RTprof_K % alloc_profs(errorstatus, self % conf, -1, -1, asw=0) + + ! Set flag that the tracectory was set ! ------------------------------------ self % ltraj = .true. - - end subroutine ufo_radiancerttov_tlad_settraj + +end subroutine ufo_radiancerttov_tlad_settraj ! ------------------------------------------------------------------------------ subroutine ufo_radiancerttov_simobs_tl(self, geovals, obss, nvars, nlocs, hofx) @@ -379,12 +362,10 @@ subroutine ufo_radiancerttov_simobs_tl(self, geovals, obss, nvars, nlocs, hofx) do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then - do jchan = 1, size(self%channels) - hofx(jchan,prof) = hofx(jchan,prof) + & - sum(self % RTprof_K % profiles_k(ichan+jchan-1) % t(self % nlevels:1:-1) * geoval_d % vals(1:geoval_d % nval,prof)) - enddo - endif + do jchan = 1, size(self%channels) + hofx(jchan,prof) = hofx(jchan,prof) + & + sum(self % RTprof_K % profiles_k(ichan+jchan-1) % t(self % nlevels:1:-1) * geoval_d % vals(1:geoval_d % nval,prof)) + enddo end do do jspec = 1, self%conf%ngas @@ -401,21 +382,21 @@ subroutine ufo_radiancerttov_simobs_tl(self, geovals, obss, nvars, nlocs, hofx) ! This is where CO2 and friends will live as well as CLW do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then do jchan = 1, size(self%channels) if(self%conf%Absorbers(jspec) == var_q) then hofx(jchan,prof) = hofx(jchan,prof) + & - sum(self % RTprof_K % profiles_k(ichan+jchan-1) % q(self % nlevels:1:-1) * geoval_d % vals(1:geoval_d % nval,prof)) + sum(self % RTprof_K % profiles_k(ichan+jchan-1) % q(self % nlevels:1:-1) * & + geoval_d % vals(1:geoval_d % nval,prof)) * self%conf%scale_fac(gas_id_watervapour) elseif(self%conf%Absorbers(jspec) == var_mixr) then hofx(jchan,prof) = hofx(jchan,prof) + & - sum(self % RTprof_K % profiles_k(ichan+jchan-1) % q(self % nlevels:1:-1) * geoval_d % vals(1:geoval_d % nval,prof)) / & - g_to_kg + sum(self % RTprof_K % profiles_k(ichan+jchan-1) % q(self % nlevels:1:-1) * & + geoval_d % vals(1:geoval_d % nval,prof)) * self%conf%scale_fac(gas_id_watervapour) / g_to_kg elseif(self%conf%Absorbers(jspec) == var_clw) then hofx(jchan,prof) = hofx(jchan,prof) + & - sum(self % RTprof_K % profiles_k(ichan+jchan-1) % clw(self % nlevels:1:-1) * geoval_d % vals(1:geoval_d % nval,prof)) + sum(self % RTprof_K % profiles_k(ichan+jchan-1) % clw(self % nlevels:1:-1) * & + geoval_d % vals(1:geoval_d % nval,prof)) endif enddo - endif end do enddo @@ -435,12 +416,10 @@ subroutine ufo_radiancerttov_simobs_tl(self, geovals, obss, nvars, nlocs, hofx) do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then - do jchan = 1, size(self%channels) - hofx(jchan,prof) = hofx(jchan,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % t * geoval_d % vals(1,prof) - enddo - endif + do jchan = 1, size(self%channels) + hofx(jchan,prof) = hofx(jchan,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % t * geoval_d % vals(1,prof) + enddo end do !q2m @@ -448,12 +427,11 @@ subroutine ufo_radiancerttov_simobs_tl(self, geovals, obss, nvars, nlocs, hofx) do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then - do jchan = 1, size(self%channels) - hofx(jchan,prof) = hofx(jchan,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % q * geoval_d % vals(1,prof) - enddo - endif + do jchan = 1, size(self%channels) + hofx(jchan,prof) = hofx(jchan,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % q * & + geoval_d % vals(1,prof) * self%conf%scale_fac(gas_id_watervapour) + enddo end do !windspeed @@ -462,13 +440,11 @@ subroutine ufo_radiancerttov_simobs_tl(self, geovals, obss, nvars, nlocs, hofx) do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then - do jchan = 1, size(self%channels) - hofx(jchan,prof) = hofx(jchan,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % u * geoval_d % vals(1,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % v * geoval_d2 % vals(1,prof) - enddo - endif + do jchan = 1, size(self%channels) + hofx(jchan,prof) = hofx(jchan,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % u * geoval_d % vals(1,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % v * geoval_d2 % vals(1,prof) + enddo end do !Tskin @@ -476,12 +452,10 @@ subroutine ufo_radiancerttov_simobs_tl(self, geovals, obss, nvars, nlocs, hofx) do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then - do jchan = 1, size(self%channels) - hofx(jchan,prof) = hofx(jchan,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % skin % t * geoval_d % vals(1,prof) - enddo - endif + do jchan = 1, size(self%channels) + hofx(jchan,prof) = hofx(jchan,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % skin % t * geoval_d % vals(1,prof) + enddo end do end subroutine ufo_radiancerttov_simobs_tl @@ -538,14 +512,12 @@ subroutine ufo_radiancerttov_simobs_ad(self, geovals, obss, nvars, nlocs, hofx) do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then - do jchan = 1, size(self%channels) - if (hofx(jchan, prof) /= missing) then - geoval_d % vals(:,prof) = geoval_d % vals(:,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % t(self % nlevels:1:-1) * hofx(jchan,prof) - endif - enddo - endif + do jchan = 1, size(self%channels) + if (hofx(jchan, prof) /= missing) then + geoval_d % vals(:,prof) = geoval_d % vals(:,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % t(self % nlevels:1:-1) * hofx(jchan,prof) + endif + enddo end do ! Absorbers @@ -565,23 +537,23 @@ subroutine ufo_radiancerttov_simobs_ad(self, geovals, obss, nvars, nlocs, hofx) do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then - do jchan = 1, size(self%channels) - if (hofx(jchan, prof) /= missing) then - - if(self%conf%Absorbers(jspec) == var_q) then - geoval_d % vals(:,prof) = geoval_d % vals(:,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % q(self % nlevels:1:-1) * hofx(jchan,prof) - elseif(self%conf%Absorbers(jspec) == var_mixr) then - geoval_d % vals(:,prof) = geoval_d % vals(:,prof) + & - (self % RTprof_K % profiles_k(ichan+jchan-1) % q(self % nlevels:1:-1) / g_to_kg) * hofx(jchan,prof) - elseif(self%conf%Absorbers(jspec) == var_clw) then - geoval_d % vals(:,prof) = geoval_d % vals(:,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % clw(self % nlevels:1:-1) * hofx(jchan,prof) - endif + do jchan = 1, size(self%channels) + if (hofx(jchan, prof) /= missing) then + + if(self%conf%Absorbers(jspec) == var_q) then + geoval_d % vals(:,prof) = geoval_d % vals(:,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % q(self % nlevels:1:-1) * hofx(jchan,prof) * & + self%conf%scale_fac(gas_id_watervapour) + elseif(self%conf%Absorbers(jspec) == var_mixr) then + geoval_d % vals(:,prof) = geoval_d % vals(:,prof) + & + (self % RTprof_K % profiles_k(ichan+jchan-1) % q(self % nlevels:1:-1) * hofx(jchan,prof)) * & + self%conf%scale_fac(gas_id_watervapour) / g_to_kg + elseif(self%conf%Absorbers(jspec) == var_clw) then + geoval_d % vals(:,prof) = geoval_d % vals(:,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % clw(self % nlevels:1:-1) * hofx(jchan,prof) endif - enddo - endif + endif + enddo enddo enddo @@ -608,14 +580,12 @@ subroutine ufo_radiancerttov_simobs_ad(self, geovals, obss, nvars, nlocs, hofx) do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then - do jchan = 1, size(self%channels) - if (hofx(jchan, prof) /= missing) then - geoval_d % vals(1,prof) = geoval_d % vals(1,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % t * hofx(jchan,prof) - endif - enddo - endif + do jchan = 1, size(self%channels) + if (hofx(jchan, prof) /= missing) then + geoval_d % vals(1,prof) = geoval_d % vals(1,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % t * hofx(jchan,prof) + endif + enddo end do !q2m @@ -630,14 +600,13 @@ subroutine ufo_radiancerttov_simobs_ad(self, geovals, obss, nvars, nlocs, hofx) do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then - do jchan = 1, size(self%channels) - if (hofx(jchan, prof) /= missing) then - geoval_d % vals(1,prof) = geoval_d % vals(1,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % q * hofx(jchan,prof) - endif - enddo - endif + do jchan = 1, size(self%channels) + if (hofx(jchan, prof) /= missing) then + geoval_d % vals(1,prof) = geoval_d % vals(1,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % q * & + hofx(jchan,prof) * self%conf%scale_fac(gas_id_watervapour) + endif + enddo end do !windspeed @@ -658,17 +627,15 @@ subroutine ufo_radiancerttov_simobs_ad(self, geovals, obss, nvars, nlocs, hofx) do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then - do jchan = 1, size(self%channels) - if (hofx(jchan, prof) /= missing) then - geoval_d % vals(1,prof) = geoval_d % vals(1,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % u * hofx(jchan,prof) - - geoval_d2 % vals(1,prof) = geoval_d2 % vals(1,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % v * hofx(jchan,prof) - endif - enddo - endif + do jchan = 1, size(self%channels) + if (hofx(jchan, prof) /= missing) then + geoval_d % vals(1,prof) = geoval_d % vals(1,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % u * hofx(jchan,prof) + + geoval_d2 % vals(1,prof) = geoval_d2 % vals(1,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % s2m % v * hofx(jchan,prof) + endif + enddo end do !Tskin @@ -684,14 +651,12 @@ subroutine ufo_radiancerttov_simobs_ad(self, geovals, obss, nvars, nlocs, hofx) do ichan = 1, self % nchan_total, size(self%channels) prof = self % RTprof_K % chanprof(ichan) % prof - if (.not. self % Skip_Profiles(prof)) then - do jchan = 1, size(self%channels) - if (hofx(jchan, prof) /= missing) then - geoval_d % vals(1,prof) = geoval_d % vals(1,prof) + & - self % RTprof_K % profiles_k(ichan+jchan-1) % skin % t * hofx(jchan,prof) - endif - enddo - endif + do jchan = 1, size(self%channels) + if (hofx(jchan, prof) /= missing) then + geoval_d % vals(1,prof) = geoval_d % vals(1,prof) + & + self % RTprof_K % profiles_k(ichan+jchan-1) % skin % t * hofx(jchan,prof) + endif + enddo end do ! Once all geovals set replace flag diff --git a/src/ufo/rttov/ufo_radiancerttov_utils_mod.F90 b/src/ufo/rttov/ufo_radiancerttov_utils_mod.F90 index 1aa7dc8ee..b318a45c8 100644 --- a/src/ufo/rttov/ufo_radiancerttov_utils_mod.F90 +++ b/src/ufo/rttov/ufo_radiancerttov_utils_mod.F90 @@ -12,16 +12,20 @@ module ufo_radiancerttov_utils_mod use iso_c_binding use kinds use missing_values_mod + use, intrinsic :: iso_fortran_env, only : stderr=>error_unit, & + stdout=>output_unit use rttov_types, only : rttov_options, rttov_profile, rttov_coefs, & rttov_radiance, rttov_transmission, rttov_emissivity, & rttov_chanprof - use rttov_const ! gas_ids + use rttov_const ! gas_ids and gas_units use ufo_vars_mod + use ufo_constants_mod use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var use ufo_basis_mod, only: ufo_basis + use ufo_utils_mod, only: Ops_SatRad_Qsplit, Ops_Qsat, Ops_QsatWat, cmp_strings use obsspace_mod implicit none @@ -30,24 +34,26 @@ module ufo_radiancerttov_utils_mod public rttov_conf public rttov_conf_setup public rttov_conf_delete - public load_atm_data_rttov - public load_geom_data_rttov public parse_hofxdiags public populate_hofxdiags - public ufo_rttov_skip_profiles integer, parameter, public :: max_string=800 - integer, parameter, public :: MAXVARIN = 50 + integer, parameter, public :: maxvarin = 50 !DARFIX this should go somewhere else - character(len=maxvarlen), public :: varin_temp(MAXVARIN) + character(len=maxvarlen), public :: varin_temp(maxvarin) character(len=max_string), public :: message integer, public :: nvars_in integer, public :: rttov_errorstatus - character(len=MAXVARLEN), allocatable :: ystr_diags(:), xstr_diags(:) + ! ystr_diags contains the name of the rttov variable for ouput e.g. + ! transmitance or optical depth. For jacobian output var_tb + character(len=maxvarlen), allocatable :: ystr_diags(:) + ! xstr_diags contains the model variable for jacobian output e.g. + ! var_t or empty if jacobian output is not required + character(len=maxvarlen), allocatable :: xstr_diags(:) integer, allocatable :: ch_diags(:) real(c_double) :: missing @@ -61,20 +67,22 @@ module ufo_radiancerttov_utils_mod var_sfc_vegtyp, var_sfc_soiltyp, var_sfc_sdepth/) !var_ps - character(len=maxvarlen), dimension(11), target :: varin_default_satrad = & + + character(len=maxvarlen), dimension(10), target :: varin_default_satrad = & (/var_prs, var_ts, var_q, var_sfc_t2m, & var_u, var_v, var_sfc_p2m, var_sfc_q2m, & - var_sfc_tskin, var_water_type_rttov, var_surf_type_rttov /) + var_sfc_tskin, var_surf_type_rttov /) character(len=maxvarlen), pointer, public :: varin_default(:) ! copy of ABSORBER_ID_NAME defined in rttov_const character(len=*), parameter :: & - RTTOV_Absorbers(ngases_max+1) = & - [gas_name(1:ngases_max),'CLW'] + RTTOV_Absorbers(ngases_max+2) = & + [gas_name(1:ngases_max),'CLW', & + 'CIW'] integer, parameter :: & - RTTOV_Absorber_Id(ngases_max+1) = & + RTTOV_Absorber_Id(ngases_max+2) = & [gas_id_mixed, & gas_id_watervapour, & gas_id_ozone, & @@ -83,16 +91,29 @@ module ufo_radiancerttov_utils_mod gas_id_n2o, & gas_id_co, & gas_id_ch4, & - gas_id_so2, 99] + gas_id_so2, 0, 0] + + real(kind_real), parameter :: & + gas_unit_conv(0:ngases_max) = & + [1.0_kind_real, & ! 0 index for use with CLW/CIW + 1.0_kind_real, & ! 'mixed' gases - RTTOV internal absorber only, no conversion + q_mixratio_to_ppmv, & + o3_mixratio_to_ppmv, & + 1.0_kind_real, & ! WV continuum - RTTOV internal absorber only, no conversion + co2_mixratio_to_ppmv, & + n2o_mixratio_to_ppmv, & + co_mixratio_to_ppmv, & + ch4_mixratio_to_ppmv, & + so2_mixratio_to_ppmv] character(len=MAXVARLEN), parameter :: null_str = '' !DARFIX: need to get correct names (correct units for RTTOV) in ufo_vars_mod character(len=MAXVARLEN), parameter :: & - UFO_Absorbers(ngases_max+1) = & + UFO_Absorbers(ngases_max+2) = & [ null_str, var_mixr, var_oz, null_str, var_co2, 'mole_fraction_of_nitrous_oxide_in_air', & 'mole_fraction_of_carbon_monoxide_in_air', 'mole_fraction_of_methane_in_air', & - 'mole_fraction_of_sulfur_dioxide_in_air', var_clw] + 'mole_fraction_of_sulfur_dioxide_in_air', var_clw, var_cli] integer, public :: nchan_inst ! number of channels being simulated (may be less than full instrument) integer, public :: nchan_sim ! total number of 'obs' = nprofiles * nchannels @@ -102,7 +123,7 @@ module ufo_radiancerttov_utils_mod integer :: iprof type, public :: ufo_rttov_io - logical, pointer :: calcemis(:) ! Flag to indicate calculation of emissivity within RTTOV + logical, pointer :: calcemis(:) ! Flag to indicate calculation of emissivity within RTTOV type(rttov_emissivity), pointer :: emissivity(:) ! Input/output surface emissivity type(rttov_profile), pointer :: profiles(:) ! Input profiles @@ -115,10 +136,16 @@ module ufo_radiancerttov_utils_mod type(rttov_transmission) :: transmission_k ! Output transmittances type(rttov_radiance) :: radiance_k ! Output radiances - contains + contains - procedure :: alloc => alloc_rttov + procedure :: alloc_direct => ufo_rttov_alloc_direct + procedure :: alloc_k => ufo_rttov_alloc_k + procedure :: alloc_profs => ufo_rttov_alloc_profiles + procedure :: alloc_profs_K => ufo_rttov_alloc_profiles_K procedure :: init_emissivity => ufo_rttov_init_emissivity + procedure :: setup => ufo_rttov_setup_rtprof + procedure :: check => ufo_rttov_check_rtprof + procedure :: print => ufo_rttov_print_rtprof end type ufo_rttov_io @@ -129,20 +156,27 @@ module ufo_radiancerttov_utils_mod character(len=MAXVARLEN), allocatable :: Absorbers(:) integer, allocatable :: Absorber_Id(:) + real(kind_real) :: scale_fac(0:ngases_max) + logical :: RTTOV_GasUnitConv character(len=255), allocatable :: SENSOR_ID(:) character(len=255) :: COEFFICIENT_PATH type(rttov_coefs), allocatable :: rttov_coef_array(:) - character(len=8) :: RTTOV_default_opts = 'RTTOV' + character(len=10) :: RTTOV_default_opts = 'RTTOV' type(rttov_options) :: rttov_opts logical :: rttov_is_setup = .false. logical :: SatRad_compatibility = .true. - logical :: UseQtsplitRain, SplitQtotal = .false. ! true for MW, false otherwise + logical :: UseRHwaterForQC = .true. ! only used with SatRad compatibility + logical :: UseColdSurfaceCheck = .true. ! only used with SatRad compatibility + logical :: SplitQtotal = .false. ! true for SatRad compatibility with MW + logical :: UseQtsplitRain = .false. logical :: RTTOV_profile_checkinput = .false. - integer :: inspect + logical :: prof_by_prof = .true. + + integer, allocatable :: inspect(:) integer :: nchan_max_sim contains @@ -170,6 +204,10 @@ subroutine rttov_conf_setup(conf, f_confOpts, f_confOper) character(len=:), allocatable :: str_array(:) logical :: varin_satrad = .false. + integer :: i,k,n, i_inst + + include 'rttov_user_options_checkinput.interface' + !Number of sensors, each call to RTTOV will be for a single sensor !type (zenith/scan angle will be different) conf % nSensors = 1 @@ -182,14 +220,14 @@ subroutine rttov_conf_setup(conf, f_confOpts, f_confOper) if (f_confOper%has("GeoVal_type")) then call f_confOper%get_or_die("GeoVal_type",str) - if (trim(str) == 'MetO' .or. trim(str) == 'SatRad') then + if (cmp_strings(str, 'MetO') .or. cmp_strings(str, 'SatRad')) then varin_default => varin_default_satrad varin_satrad = .true. - elseif(trim(str) == 'CRTM') then + elseif(cmp_strings(str, 'CRTM')) then varin_default => varin_default_crtm varin_satrad = .false. else - write(message,*) trim(ROUTINE_NAME),' error: ',trim(str),' is not a supported GeoVal type' + write(message,*) trim(routine_name),' error: ',trim(str),' is not a supported GeoVal type' call abor1_ftn(message) endif else @@ -214,7 +252,7 @@ subroutine rttov_conf_setup(conf, f_confOpts, f_confOper) ! check for duplications do jspec = 2, conf%ngas if ( any(conf%Absorbers(jspec-1) == conf%Absorbers(jspec:conf%ngas)) ) then - write(message,*) trim(ROUTINE_NAME),' error: ',trim(conf%Absorbers(jspec)),' is duplicated in Absorbers' + write(message,*) trim(routine_name),' error: ',trim(conf%Absorbers(jspec)),' is duplicated in Absorbers' call abor1_ftn(message) end if end do @@ -224,7 +262,7 @@ subroutine rttov_conf_setup(conf, f_confOpts, f_confOper) ivar = ufo_vars_getindex(RTTOV_Absorbers, conf%Absorbers(jspec)) if (ivar < 1 .or. ivar > size(UFO_Absorbers)) then - write(message,*) trim(ROUTINE_NAME),' error: ',trim(conf%Absorbers(jspec)),' not supported by UFO_Absorbers' + write(message,*) trim(routine_name),' error: ',trim(conf%Absorbers(jspec)),' not supported by UFO_Absorbers' call abor1_ftn(message) end if @@ -238,6 +276,18 @@ subroutine rttov_conf_setup(conf, f_confOpts, f_confOper) conf%Absorber_Id(jspec) = RTTOV_Absorber_Id(ivar) end do + if(f_confOpts % has("RTTOV_GasUnitConv")) then + call f_confOpts % get_or_die("RTTOV_GasUnitConv",conf % RTTOV_GasUnitConv) !test, OPS, RTTOV + else + conf % RTTOV_GasUnitConv = .false. ! no unit conversion done for RTTOV by default + endif + +! set scalar mixing ratio conversion if converting units prior to use in RTTOV + if(conf%RTTOV_GasUnitConv) then + conf%scale_fac = gas_unit_conv + else + conf%scale_fac = 1.0 + endif ! Allocate SENSOR_ID allocate(conf % SENSOR_ID(conf % nSensors)) @@ -259,6 +309,20 @@ subroutine rttov_conf_setup(conf, f_confOpts, f_confOper) call f_confOpts % get_or_die("SatRad_compatibility",conf % SatRad_compatibility) endif + if(f_confOpts % has("UseRHwaterForQC")) then + call f_confOpts % get_or_die("UseRHwaterForQC",conf % UseRHwaterForQC) + endif + + if(f_confOpts % has("UseColdSurfaceCheck")) then + call f_confOpts % get_or_die("UseColdSurfaceCheck",conf % UseColdSurfaceCheck) + endif + + if(f_confOpts % has("prof_by_prof")) then + call f_confOpts % get_or_die("prof_by_prof",conf % prof_by_prof) + else + conf % prof_by_prof = .false. + endif + if(f_confOpts % has("max_channels_per_batch")) then call f_confOpts % get_or_die("max_channels_per_batch",conf % nchan_max_sim) else @@ -270,31 +334,54 @@ subroutine rttov_conf_setup(conf, f_confOpts, f_confOper) end if !DARFIX THIS ONLY WORKS FOR ONE INSTRUMENT - ! if (conf % rttov_coef_array(1) % coef % id_sensor == sensor_id_mw) then - if(conf % rttov_opts % rt_mw % clw_data .and. conf % SatRad_compatibility) then + if(conf % rttov_opts % rt_mw % clw_data .and. & + conf % SatRad_compatibility) then + conf % SplitQtotal = .true. conf % UseQtsplitRain = .true. - conf % splitQtotal = .true. endif - conf % rttov_opts % rt_ir % ozone_data = .false. + conf % rttov_opts % rt_ir % ozone_data = .false. conf % rttov_opts % rt_ir % co2_data = .false. conf % rttov_opts % rt_ir % n2o_data = .false. conf % rttov_opts % rt_ir % ch4_data = .false. conf % rttov_opts % rt_ir % so2_data = .false. - else - conf % UseQtsplitRain = .false. - conf % splitQtotal = .false. + endif + + ! Ensure the RTTOV options and coefficients are consistent + do i_inst = 1, SIZE(conf % rttov_coef_array(:)) + call rttov_user_options_checkinput(rttov_errorstatus, conf % rttov_opts, conf % rttov_coef_array(i_inst)) + + if (rttov_errorstatus /= errorstatus_success) then + write(message,'(A, A, I6, I6)') trim(routine_name), ': Error in rttov_user_options_checkinput: ', rttov_errorstatus, i_inst + call abor1_ftn(message) + end if + enddo + + ! Default is false; satrad compatibility and mw default is true + if(f_confOpts % has("QtSplitRain")) then + call f_confOpts % get_or_die("QtSplitRain", conf % UseQtsplitRain) endif if(f_confOpts % has("RTTOV_profile_checkinput")) then - call f_confOpts % get_or_die("RTTOV_profile_checkinput",conf % RTTOV_profile_checkinput) !test, OPS, RTTOV + call f_confOpts % get_or_die("RTTOV_profile_checkinput",conf % RTTOV_profile_checkinput) endif - conf % inspect = 0 if (f_confOpts%has("InspectProfileNumber")) then - call f_confOpts % get_or_die("InspectProfileNumber",conf % inspect) - conf % RTTOV_profile_checkinput = .true. + call f_confOpts % get_or_die("InspectProfileNumber",str) + + n=0; k=1 + do + i = index(str(k:),',') + if (i==0) exit + n = n + 1 + k = k + i + end do + + allocate(conf % inspect(n+1)) + read(str, *) conf % inspect + else + allocate(conf % inspect(0)) endif end subroutine rttov_conf_setup @@ -310,867 +397,936 @@ subroutine rttov_conf_delete(conf) deallocate(conf%Absorbers) deallocate(conf%Absorber_Id) + ! needed to prevent bugs caused when more than one obs spaces in a yaml file + if (allocated(ystr_diags)) deallocate (ystr_diags) + if (allocated(xstr_diags)) deallocate (xstr_diags) + if (allocated(ch_diags)) deallocate (ch_diags) + end subroutine rttov_conf_delete ! ----------------------------------------------------------------------------- - subroutine load_atm_data_rttov(geovals,obss,profiles,prof_start,conf,layer_quantities,obs_info) - use ufo_constants_mod, only : half, deg2rad, min_q, m_to_km, g_to_kg, pa_to_hpa + ! ------------------------------------------------------------------------------ + subroutine set_options_rttov(self, f_confOpts) implicit none - type(ufo_geovals), intent(in) :: geovals - type(c_ptr), value, intent(in) :: obss - type(rttov_profile), intent(inout) :: profiles(:) - integer, intent(in) :: prof_start - type(rttov_conf), intent(in) :: conf - logical, intent(inout) :: layer_quantities - logical, optional, intent(in) :: obs_info + class(rttov_conf), intent(inout) :: self + type(fckit_configuration), intent(in) :: f_confOpts ! RTcontrol - ! Local variables - integer :: jspec - integer :: nlevels - integer :: nprofiles + include 'rttov_print_opts.interface' - type(ufo_geoval), pointer :: geoval - character(MAXVARLEN) :: varname + call self % set_defaults(self%RTTOV_default_opts) ! test, OPS, RTTOV - real(kind_real) :: ifrac, sfrac, lfrac, wfrac - real(kind_real) :: itmp, stmp, ltmp + !< Switch to enable atmospheric refraction + if (f_confOpts % has("RTTOV_addrefrac")) then + call f_confOpts % get_or_die("RTTOV_addrefrac", self % rttov_opts % rt_all % addrefrac) + end if - real(kind_real), allocatable :: TmpVar(:), windsp(:), p(:) - logical :: variable_present + !< Switch for input units in AD/K models + if (f_confOpts % has("RTTOV_switchrad")) then + call f_confOpts % get_or_die("RTTOV_switchrad", self % rttov_opts % rt_all % switchrad) + end if - integer :: top_level, bottom_level, stride - real(kind_real) :: Tstar, NewT - integer :: level_1000hPa, level_950hpa + !< Switch to enable use of 2m q variable + if (f_confOpts % has("RTTOV_use_q2m")) then + call f_confOpts % get_or_die("RTTOV_use_q2m", self % rttov_opts % rt_all % use_q2m) + end if - real(kind_real), allocatable :: q_temp(:), clw_temp(:), ciw_temp(:), Qtotal(:) + !< Switch for setting Lambertian reflection (IR and MW) + if (f_confOpts % has("RTTOV_do_lambertian")) then + call f_confOpts % get_or_die("RTTOV_do_lambertian", self % rttov_opts % rt_all % do_lambertian) + end if - if(present(obs_info)) then - nlocs_total = 1 - else - nlocs_total = obsspace_get_nlocs(obss) + !< Switch for fixed/parameterised effective angle for Lambertian option + if (f_confOpts % has("RTTOV_lambertian_fixed_angle")) then + call f_confOpts % get_or_die("RTTOV_lambertian_fixed_angle", self % rttov_opts % rt_all % lambertian_fixed_angle) end if - nprofiles = min(size(profiles), geovals%nlocs - prof_start + 1) + !< Switch to ignore atmospheric curvature + if (f_confOpts % has("RTTOV_plane_parallel")) then + call f_confOpts % get_or_die("RTTOV_plane_parallel", self % rttov_opts % rt_all % plane_parallel) + end if - nlevels = size(profiles(1)%p) + !< Linear-in-tau or layer-mean for downwelling radiances + if (f_confOpts % has("RTTOV_rad_down_lin_tau")) then + call f_confOpts % get_or_die("RTTOV_rad_down_lin_tau", self % rttov_opts % rt_all % rad_down_lin_tau) + end if - ! Assume that the pressure profile coming from the geovals is increasing in pressure (ToA->surface)... - top_level = 1 - bottom_level = nlevels - stride=1 + !< Switch to apply dtau test in transmit/integrate calculations + if (f_confOpts % has("RTTOV_dtau_test")) then + call f_confOpts % get_or_die("RTTOV_dtau_test", self % rttov_opts % rt_all % dtau_test) + end if - if (ufo_vars_getindex(geovals%variables, var_prs) > 0) then - call ufo_geovals_get_var(geovals, var_prs, geoval) + !< FASTEM version (0-6); 0 => TESSEM2 + if (f_confOpts % has("RTTOV_fastem_version")) then + call f_confOpts % get_or_die("RTTOV_fastem_version", self % rttov_opts % rt_mw % fastem_version) + end if - allocate(p(nlevels)) - do iprof = 1, nprofiles - p = geoval%vals(geoval%nval-nlevels+1:geoval%nval,prof_start + iprof - 1) * Pa_to_hPa ! for RTTOV - if (iprof == 1) then - if (p(1) > p(2)) then ! ...but be ready to switch. Assume if one profile is 'upside-down' then they all are - top_level = nlevels - bottom_level = 1 - stride = -1 - endif - endif - profiles(iprof)%p(top_level:bottom_level:stride) = p(:) - end do - deallocate(p) + !< Supply a foam fraction to FASTEM + if (f_confOpts % has("RTTOV_supply_foam_fraction")) then + call f_confOpts % get_or_die("RTTOV_supply_foam_fraction", self % rttov_opts % rt_mw % supply_foam_fraction) + end if - ! DARFIX - it might be that all of the temperatures are on layers so it may be that we need to do interpolation - ! but it might be better to force this using an option if we know where the geovals are coming from - else - call ufo_geovals_get_var(geovals, var_prsi, geoval) - allocate(p(nlevels-1)) - do iprof = 1, nprofiles - p = geoval%vals(geoval%nval-(nlevels-1)+1:geoval%nval,prof_start + iprof - 1) * Pa_to_hPa - if (iprof == 1) then - if (p(1) > p(2)) then !upside-down - top_level = nlevels - 1 - bottom_level = 1 - stride = -1 - else - top_level = 1 - bottom_level = nlevels - 1 - stride = 1 - endif - endif - profiles(iprof)%p(top_level+stride:bottom_level:stride) = half * (p(top_level:bottom_level-stride:stride) + p(top_level+stride:bottom_level:stride)) - profiles(iprof)%p(1) = max( profiles(iprof)%p(2) - half * (profiles(iprof)%p(3) - profiles(iprof)%p(2)),half * profiles(iprof)%p(2)) - profiles(iprof)%p(nlevels) = profiles(iprof)%p(nlevels-1) - half * (profiles(iprof)%p(nlevels-2) - profiles(iprof)%p(nlevels-1)) - end do - deallocate(p) - endif + !< Switch to enable input of cloud liquid water profile + if (f_confOpts % has("RTTOV_clw_data")) then + call f_confOpts % get_or_die("RTTOV_clw_data", self % rttov_opts % rt_mw % clw_data) + end if -! Get temperature - varname = var_ts - call ufo_geovals_get_var(geovals, varname, geoval) + !< MW CLW scheme: 1 => Liebe, 2 => Rosenkranz, 3 => TKC + if (f_confOpts % has("RTTOV_clw_scheme")) then + call f_confOpts % get_or_die("RTTOV_clw_scheme", self % rttov_opts % rt_mw % clw_scheme) + end if -! Check if temperatures are provided on levels as required for RTTOV, otherwise assume that temperatures are layer quantities -! (and all future atmospheric variables) and do some interpolation to prepare for RTTOV. -! TODO: Put a warning in here that this is happening - if( size(geoval%vals(:,1)) == nlevels) then - do iprof = 1, nprofiles - profiles(iprof)%t(top_level:bottom_level:stride) = geoval%vals(:,prof_start + iprof - 1) ! K - end do - else - layer_quantities = .true. - bottom_level = nlevels-1 + !< Apply MW CLW calculations on coef/user levels (true/false resp.) + if (f_confOpts % has("RTTOV_clw_calc_on_coef_lev")) then + call f_confOpts % get_or_die("RTTOV_clw_calc_on_coef_lev", self % rttov_opts % rt_mw % clw_calc_on_coef_lev) + end if - do iprof = 1, nprofiles - profiles(iprof)%t(top_level+stride:bottom_level:stride) = half * (profiles(iprof)%t(top_level:bottom_level-stride:stride) + & - profiles(iprof)%t(top_level+stride:bottom_level:stride)) - profiles(iprof)%t(1) = profiles(iprof)%t(2) - profiles(iprof)%t(nlevels) = profiles(iprof)%t(nlevels-1) - half * (profiles(iprof)%t(nlevels-2) - profiles(iprof)%t(nlevels-1)) - end do - endif + !< Lower pressure limit for MW CLW calculations (hPa) + if (f_confOpts % has("RTTOV_clw_cloud_top")) then + call f_confOpts % get_or_die("RTTOV_clw_cloud_top", self % rttov_opts % rt_mw % clw_cloud_top) + end if -! Get absorbers. Assume mass mixing ratio (moist air). The distinction has been made that q will be in kg/kg as per OPS convention -! And mixr will be in g/kg. -! For now all other gases will be in kg/kg and this needs to be handled carefully. + !< Apply band-correction for Planck radiance and BT calculations + if (f_confOpts % has("RTTOV_apply_band_correction")) then + call f_confOpts % get_or_die("RTTOV_apply_band_correction", self % rttov_opts % rt_mw % apply_band_correction) + end if - profiles(1:nprofiles)%gas_units = 1 + !< Switch to enable RTTOV interpolator + if (f_confOpts % has("RTTOV_addinterp")) then + call f_confOpts % get_or_die("RTTOV_addinterp", self % rttov_opts % interpolation % addinterp) + end if - do jspec = 1, conf%ngas - call ufo_geovals_get_var(geovals,conf%Absorbers(jspec) , geoval) + !< Interpolation mode (1-5, see user guide) + if (f_confOpts % has("RTTOV_interp_mode")) then + call f_confOpts % get_or_die("RTTOV_interp_mode", self % rttov_opts % interpolation % interp_mode) + end if - select case (conf%Absorbers(jspec)) - case (var_mixr) - do iprof = 1, nProfiles - profiles(iprof)%q(top_level:bottom_level:stride) = geoval%vals(:,prof_start + iprof - 1) * g_to_kg - end do - case (var_q) - do iprof = 1, nProfiles - profiles(iprof)%q(top_level:bottom_level:stride) = geoval%vals(:,prof_start + iprof - 1) ! kg/kg - end do - case (var_oz) - if (associated(profiles(1)%o3)) then - do iprof = 1, nProfiles - profiles(iprof)%o3(top_level:bottom_level:stride) = geoval%vals(:,prof_start + iprof - 1) ! kg/kg - end do - endif - case (var_co2) - if (associated(profiles(1)%co2)) then - do iprof = 1, nProfiles - profiles(iprof)%co2(top_level:bottom_level:stride) = geoval%vals(:,prof_start + iprof - 1) ! kg/kg - end do - endif - case (var_clw) - if (associated(profiles(1)%clw)) then - do iprof = 1, nProfiles - profiles(iprof)%clw(top_level:bottom_level:stride) = geoval%vals(:,prof_start + iprof - 1) ! kg/kg - end do - endif - case default + !< Switch to make pressure an active variable in TL/AD/K models + if (f_confOpts % has("RTTOV_lgradp")) then + call f_confOpts % get_or_die("RTTOV_lgradp", self % rttov_opts % interpolation % lgradp) + end if - end select + !< Switch to assume space boundary at top-most input pressure l + if (f_confOpts % has("RTTOV_spacetop")) then + call f_confOpts % get_or_die("RTTOV_spacetop", self % rttov_opts % interpolation % spacetop) + end if - end do + !< Switch to extrapolate input profiles using regression limits + if (f_confOpts % has("RTTOV_reg_limit_extrap")) then + call f_confOpts % get_or_die("RTTOV_reg_limit_extrap", self % rttov_opts % interpolation % reg_limit_extrap) + end if -! Get near-surface variables (s2m) + if (f_confOpts % has("RTTOV_fix_hgpl")) then + call f_confOpts % get_or_die("RTTOV_fix_hgpl", self % rttov_opts % config % fix_hgpl) + end if - varname = var_sfc_p2m - if (ufo_vars_getindex(geovals%variables, varname) > 0) then - call ufo_geovals_get_var(geovals, varname, geoval) + if (f_confOpts % has("RTTOV_verbose")) then + call f_confOpts % get_or_die("RTTOV_verbose", self % rttov_opts % config % verbose) + end if - profiles(1:nprofiles)%s2m%p = geoval%vals(1,prof_start:prof_start + nprofiles - 1) * Pa_to_hPa - else - write(message,'(A)') 'No near-surface pressure. Using bottom pressure level' - call fckit_log%info(message) + if (f_confOpts % has("RTTOV_do_checkinput")) then + call f_confOpts % get_or_die("RTTOV_do_checkinput", self % rttov_opts % config % do_checkinput) + end if - do iprof = 1, nprofiles - profiles(iprof)%s2m%p = profiles(iprof)%p(nlevels) - enddo - endif + if (f_confOpts % has("RTTOV_apply_reg_limits")) then + call f_confOpts % get_or_die("RTTOV_apply_reg_limits", self % rttov_opts % config % apply_reg_limits) + end if - varname = var_sfc_t2m ! 2m temperature - if (ufo_vars_getindex(geovals%variables, varname) > 0) then - call ufo_geovals_get_var(geovals, varname, geoval) - profiles(1:nprofiles)%s2m%t = geoval%vals(1,prof_start:prof_start + nprofiles - 1) - else - write(message,'(A)') 'No near-surface temperature. Using bottom temperature level' - call fckit_log%info(message) + !< Solar sea BRDF model (1-2) + if (f_confOpts % has("RTTOV_solar_sea_brdf_model")) then + call f_confOpts % get_or_die("RTTOV_solar_sea_brdf_model", self % rttov_opts % rt_ir % solar_sea_brdf_model) + end if - do iprof = 1, nprofiles - profiles(iprof)%s2m%t = profiles(iprof)%t(nlevels) - enddo - endif + !< IR sea emissivity model (1-2) + if (f_confOpts % has("RTTOV_ir_sea_emis_model")) then + call f_confOpts % get_or_die("RTTOV_ir_sea_emis_model", self % rttov_opts % rt_ir % ir_sea_emis_model) + end if - varname = var_sfc_q2m ! 2m specific humidity (kg/kg) - if (ufo_vars_getindex(geovals%variables, varname) > 0) then - call ufo_geovals_get_var(geovals, varname, geoval) ! lfric + !< Switch to enable solar simulations + if (f_confOpts % has("RTTOV_addsolar")) then + call f_confOpts % get_or_die("RTTOV_addsolar", self % rttov_opts % rt_ir % addsolar) + end if - profiles(1:nprofiles)%s2m%q = geoval%vals(1,prof_start:prof_start + nprofiles - 1) - else - write(message,'(A)') 'No near-surface specific humidity. Using bottom q level' - call fckit_log%info(message) + !< Switch to enable Rayleigh single-scattering for VIS/NIR channel + if (f_confOpts % has("RTTOV_rayleigh_single_scatt")) then + call f_confOpts % get_or_die("RTTOV_rayleigh_single_scatt", self % rttov_opts % rt_ir % rayleigh_single_scatt) + end if - do iprof = 1, nprofiles - profiles(iprof)%s2m%q = profiles(iprof)%q(nlevels) - enddo - endif - - varname = var_u ! Eastward-wind in m/s - if (ufo_vars_getindex(geovals%variables, varname) > 0) then - call ufo_geovals_get_var(geovals, varname, geoval) + !< Switch to enable NLTE bias correction + if (f_confOpts % has("RTTOV_do_nlte_correction")) then + call f_confOpts % get_or_die("RTTOV_do_nlte_correction", self % rttov_opts % rt_ir % do_nlte_correction) + end if - profiles(1:nprofiles)%s2m%u = geoval%vals(1,prof_start:prof_start + nprofiles - 1) - !assume if eastward then northward too + !< Switch to enable IR aerosol calculations + if (f_confOpts % has("RTTOV_addaerosl")) then + call f_confOpts % get_or_die("RTTOV_addaerosl", self % rttov_opts % rt_ir % addaerosl) + end if - varname = var_v ! Northward-wind in m/s - call ufo_geovals_get_var(geovals, varname, geoval) + !< Switch to supply aerosol optical properties explicitly per channel + if (f_confOpts % has("RTTOV_user_aer_opt_param")) then + call f_confOpts % get_or_die("RTTOV_user_aer_opt_param", self % rttov_opts % rt_ir % user_aer_opt_param) + end if - profiles(1:nprofiles)%s2m%v = geoval%vals(1,prof_start:prof_start + nprofiles - 1) - else !! use windspeed and direction instead - allocate(windsp(nprofiles)) - call ufo_geovals_get_var(geovals, var_sfc_wspeed, geoval) + !< Switch to enable IR cloudy calculations + if (f_confOpts % has("RTTOV_addclouds")) then + call f_confOpts % get_or_die("RTTOV_addclouds", self % rttov_opts % rt_ir % addclouds) + end if - windsp(1:nprofiles) = geoval%vals(1,prof_start:prof_start + nprofiles - 1) + !< Switch to supply cloud optical properties explicitly per channel + if (f_confOpts % has("RTTOV_user_cld_opt_param")) then + call f_confOpts % get_or_die("RTTOV_user_cld_opt_param", self % rttov_opts % rt_ir % user_cld_opt_param) + end if - call ufo_geovals_get_var(geovals, var_sfc_wdir, geoval) + !< Switch to supply grid-box average cloud concentration or cloud + if (f_confOpts % has("RTTOV_grid_box_avg_cloud")) then + call f_confOpts % get_or_die("RTTOV_grid_box_avg_cloud", self % rttov_opts % rt_ir % grid_box_avg_cloud) + end if - do iprof = 1, nprofiles - profiles(iprof)%s2m%u = windsp(iprof) * cos(geoval%vals(1,prof_start + iprof - 1) * deg2rad) - profiles(iprof)%s2m%v = windsp(iprof) * sin(geoval%vals(1,prof_start + iprof - 1) * deg2rad) - enddo - deallocate(windsp) - endif + !! concentration in cloudy fraction of each layer + !< Ignore cloud streams with weights lower than this + if (f_confOpts % has("RTTOV_cldstr_threshold")) then + call f_confOpts % get_or_die("RTTOV_cldstr_threshold", self % rttov_opts % rt_ir % cldstr_threshold) + end if -! Get surface type from geoval if it exists else diagnose surface type from water fraction. -! If RTTOV surface type exists then it is expected that the related variables will also be available -! e.g. water type, Tskin -! DARFIX : This will be moved out to a separate subroutine in the next release to support all instruments -! particularly where we have additional surface metadata for certain instruments + !< Switch for simplified cloud stream option - USE WITH CAUTION + if (f_confOpts % has("RTTOV_cldstr_simple")) then + call f_confOpts % get_or_die("RTTOV_cldstr_simple", self % rttov_opts % rt_ir % cldstr_simple) + end if - varname = var_surf_type_rttov ! RTTOV surface type: 0 (land), 1 (water), 2 (sea-ice) - if (ufo_vars_getindex(geovals%variables, varname) > 0) then - call ufo_geovals_get_var(geovals, varname, geoval) - profiles(1:nprofiles)%skin%surftype = int(geoval%vals(1,prof_start:prof_start + nprofiles - 1),kind=jpim) + !< Upper pressure limit for cldstr_simple option (hPa) + if (f_confOpts % has("RTTOV_cldstr_low_cloud_top")) then + call f_confOpts % get_or_die("RTTOV_cldstr_low_cloud_top", self % rttov_opts % rt_ir % cldstr_low_cloud_top) + end if - varname = var_water_type_rttov ! RTTOV water type: 0 (fresh), 1 (sea) - call ufo_geovals_get_var(geovals, varname, geoval) - profiles(1:nprofiles)%skin%watertype = int(geoval%vals(1,prof_start:prof_start + nprofiles - 1),kind=jpim) + !< IR scattering model to use + if (f_confOpts % has("RTTOV_ir_scatt_model")) then + call f_confOpts % get_or_die("RTTOV_ir_scatt_model", self % rttov_opts % rt_ir % ir_scatt_model) + end if - varname = var_sfc_tskin !Skin (surface) temperature (K) - call ufo_geovals_get_var(geovals, varname, geoval) - profiles(1:nprofiles)%skin%t = geoval%vals(1,prof_start:prof_start + nprofiles - 1) + !< VIS/NIR scattering model to use + if (f_confOpts % has("RTTOV_vis_scatt_model")) then + call f_confOpts % get_or_die("RTTOV_vis_scatt_model", self % rttov_opts % rt_ir % vis_scatt_model) + end if - - else + !< Number of DOM streams, must be even and not less than 2 + if (f_confOpts % has("RTTOV_dom_nstreams")) then + call f_confOpts % get_or_die("RTTOV_dom_nstreams", self % rttov_opts % rt_ir % dom_nstreams) + end if - ! Try to diagnose RTTOV surface from water fraction - !DARFIX: this is not consistent with CRTM in any way. Need to find out how they select surface type. + !< Convergence criterion for termination of DOM azimuthal loop + if (f_confOpts % has("RTTOV_dom_accuracy")) then + call f_confOpts % get_or_die("RTTOV_dom_accuracy", self % rttov_opts % rt_ir % dom_accuracy) + end if - varname = var_sfc_wfrac - if (ufo_vars_getindex(geovals%variables, varname) > 0) then - call ufo_geovals_get_var(geovals, varname, geoval) + !< DOM ignores levels below this optical depth: + if (f_confOpts % has("RTTOV_dom_opdep_threshold")) then + call f_confOpts % get_or_die("RTTOV_dom_opdep_threshold", self % rttov_opts % rt_ir % dom_opdep_threshold) + end if - do iprof = 1, nprofiles - !Land point or sea point - wfrac = geoval%vals(1,iprof) - if (wfrac > half) then - profiles(iprof)%skin%surftype = surftype_sea - call ufo_geovals_get_var(geovals, var_sfc_wtmp, geoval) - profiles(iprof)%skin%t = geoval%vals(1,prof_start + iprof - 1) - else - !maybe it's predominantly land or ice - ! !determine land, snow and ice fractions and temperatures to determine average temperature - profiles(iprof)%skin%surftype = surftype_land ! land + !< Switch to enable input of O3 profile + if (f_confOpts % has("RTTOV_ozone_data")) then + call f_confOpts % get_or_die("RTTOV_ozone_data", self % rttov_opts % rt_ir % ozone_data) + end if + !< Switch to enable input of CO2 profile + if (f_confOpts % has("RTTOV_co2_data")) then + call f_confOpts % get_or_die("RTTOV_co2_data", self % rttov_opts % rt_ir % co2_data) + end if - call ufo_geovals_get_var(geovals, var_sfc_lfrac, geoval) - lfrac = geoval%vals(1,prof_start + iprof - 1) +!< Switch to enable input of N2O profile + if (f_confOpts % has("RTTOV_n2o_data")) then + call f_confOpts % get_or_die("RTTOV_n2o_data", self % rttov_opts % rt_ir % n2o_data) + end if - call ufo_geovals_get_var(geovals, var_sfc_sfrac, geoval) - sfrac = geoval%vals(1,prof_start + iprof - 1) + !< Switch to enable input of CO profile + if (f_confOpts % has("RTTOV_co_data")) then + call f_confOpts % get_or_die("RTTOV_co_data", self % rttov_opts % rt_ir % co_data) + end if - call ufo_geovals_get_var(geovals, var_sfc_ifrac, geoval) - ifrac = geoval%vals(1,prof_start + iprof - 1) - - call ufo_geovals_get_var(geovals, var_sfc_ltmp, geoval) - ltmp = geoval%vals(1,prof_start + iprof - 1) - - call ufo_geovals_get_var(geovals, var_sfc_stmp, geoval) - stmp = geoval%vals(1,prof_start + iprof - 1) - - call ufo_geovals_get_var(geovals, var_sfc_itmp, geoval) - itmp = geoval%vals(1,prof_start + iprof - 1) - - !Skin temperature is a combination of (i)ce temp, (l)and temp and (s)now temp - profiles(iprof)%skin%t = (lfrac * ltmp + sfrac * stmp + ifrac * itmp) / (lfrac + sfrac + ifrac) - endif - end do - endif - endif + !< Switch to enable input of CH4 profile + if (f_confOpts % has("RTTOV_ch4_data")) then + call f_confOpts % get_or_die("RTTOV_ch4_data", self % rttov_opts % rt_ir % ch4_data) + end if - !DAR: Salinity fixed for now too - profiles(1:nprofiles)%skin%salinity = 35.0_kind_real + !< Switch to enable input of SO2 profile + if (f_confOpts % has("RTTOV_so2_data")) then + call f_confOpts % get_or_die("RTTOV_so2_data", self % rttov_opts % rt_ir % so2_data) + end if - !DAR: Default fastem parameters. We are not using FASTEM over land so these are unused - do iprof = 1,nprofiles -! profiles(iprof)%skin%fastem = [3.0, 5.0, 15.0, 0.1, 0.3] - profiles(iprof)%skin%fastem = [0,0,0,0,0] - end do + call rttov_print_opts(self % rttov_opts,lu = stderr) + end subroutine set_options_rttov -! --------------------------- -! SatRad profile manipulation -! --------------------------- + ! ------------------------------------------------------------------------------ - do iprof = 1, nProfiles - if(conf % SatRad_compatibility) then + subroutine setup_rttov(self, f_confOpts, asw) + class(rttov_conf), intent(inout) :: self + type(fckit_configuration), intent(in) :: f_confOpts ! RTcontrol + integer, intent(in) :: asw !allocate switch - !---- - ! Reset low level temperatures over seaice and cold, low land as per Ops_SatRad_SetUpRTprofBg.F90 - ! N.B. I think this should be flagged so it's clear that the background has been modified - !---- - - if(profiles(iprof)%skin%surftype /= surftype_sea) then - if(profiles(iprof)%skin%t < 271.4_kind_real .and. & - profiles(iprof)%s2m%p > 950.0_kind_real) then + character(len=255) :: coef_filename + character(len=4) :: coef_ext + integer :: i_inst - level_1000hpa = minloc(abs(profiles(iprof)%p - 1000.0_kind_real),DIM=1) - level_950hpa = minloc(abs(profiles(iprof)%p - 950.0_kind_real),DIM=1) + include 'rttov_read_coefs.interface' - NewT = profiles(iprof)%t(level_950hpa) - if(profiles(iprof)%s2m%p > 1000.0_kind_real) & - NewT = max(NewT,profiles(iprof)%t(level_1000hPa)) - NewT = min(NewT, 271.4_kind_real) + coef_ext = '.dat' + if (.not. self%rttov_is_setup ) then + if(asw == 1) then - profiles(iprof)%t(level_1000hPa) = max(profiles(iprof)%t(level_1000hPa), NewT) - profiles(iprof)%s2m%t = max(profiles(iprof)%s2m%t, NewT) - Tstar = profiles(iprof)%skin%t - profiles(iprof)%skin%t = max(profiles(iprof)%skin%t, NewT) - endif - endif + ! -------------------------------------------------------------------------- + ! 1. Setup rttov options + ! -------------------------------------------------------------------------- + call self % set_options(f_confOpts) - !min_q fix - where(profiles(iprof)%q < min_q) profiles(iprof)%q = min_q - if(profiles(iprof)%s2m%q < min_q) profiles(iprof)%s2m%q = min_q - - endif + ! -------------------------------------------------------------------------- + ! 2. Read coefficients + ! -------------------------------------------------------------------------- + allocate(self % rttov_coef_array(self % nSensors)) - enddo + do i_inst = 1, self%nSensors + coef_filename = & + trim(self % COEFFICIENT_PATH) // 'rtcoef_' // trim(self%SENSOR_ID(i_inst)) // trim(coef_ext) -! ---------------------------- -! Interpolate layer quantities -! ---------------------------- -! Continuation of the profile interpolation if absorber profiles are layer quantities (RTTOV expects levels). -! This is done once any profile manipulation (for q primarily) has been done. + call rttov_read_coefs(rttov_errorstatus, & !out + self % rttov_coef_array(i_inst), & !inout + self % rttov_opts, & !in + file_coef = coef_filename) !in - if(layer_quantities) then - do iprof=1,nprofiles - profiles(iprof)%q(top_level+stride:bottom_level:stride) = half * (profiles(iprof)%q(top_level:bottom_level-stride:stride) + & - profiles(iprof)%q(top_level+stride:bottom_level:stride)) - profiles(iprof)%q(1) = profiles(iprof)%q(2) - profiles(iprof)%q(nlevels) = profiles(iprof)%q(nlevels-1) - half * (profiles(iprof)%q(nlevels-2) - profiles(iprof)%q(nlevels-1)) + if (rttov_errorstatus /= errorstatus_success) then + write(message,*) 'fatal error reading coefficients' + call abor1_ftn(message) + else + write(message,*) 'successfully read' // coef_filename + call fckit_log%info(message) + end if - if (associated(profiles(1)%o3)) then - profiles(iprof)%o3(top_level+stride:bottom_level:stride) = half * & - (profiles(iprof)%o3(top_level:bottom_level-stride:stride) + profiles(iprof)%o3(top_level+stride:bottom_level:stride)) - profiles(iprof)%o3(1) = profiles(iprof)%o3(2) - profiles(iprof)%o3(nlevels) = profiles(iprof)%o3(nlevels-1) - & - half * (profiles(iprof)%o3(nlevels-2) - profiles(iprof)%o3(nlevels-1)) - endif + end do - if (associated(profiles(1)%clw)) then - profiles(iprof)%clw(top_level+stride:bottom_level:stride) = half * & - (profiles(iprof)%clw(top_level:bottom_level-stride:stride) + profiles(iprof)%clw(top_level+stride:bottom_level:stride)) - profiles(iprof)%clw(1) = profiles(iprof)%clw(2) - profiles(iprof)%clw(nlevels) = profiles(iprof)%clw(nlevels-1) - & - half * (profiles(iprof)%clw(nlevels-2) - profiles(iprof)%clw(nlevels-1)) - endif - enddo + self % rttov_is_setup =.true. + else !asw == 0 + deallocate(self % rttov_coef_array) + self%rttov_is_setup =.false. + end if endif + end subroutine setup_rttov + ! ------------------------------------------------------------------------------ - !non mwscatt only at the moment - if(conf % SplitQtotal) then + subroutine get_var_name(n,varname) - allocate(Qtotal(nlevels), q_temp(nlevels), clw_temp(nlevels), ciw_temp(nlevels)) + integer, intent(in) :: n + character(len=*), intent(out) :: varname - do iprof = 1, nprofiles - ! compute bg qtotal using q and clw only - ! currently ice is ignored - Qtotal(:) = profiles(iprof) % q(:) + profiles(iprof) % clw(:) + character(len=6) :: chan - ! generate first guess cloud and q based on the qtotal physics - call Ops_SatRad_Qsplit (1, & ! in - profiles(iprof) % p(:), & ! in - profiles(iprof) % t(:), & ! in - Qtotal(:), & ! in - q_temp(:), & ! out - clw_temp(:), & ! out - ciw_temp(:), & ! out - UseQtSplitRain = conf % UseQtSplitRain) + write(chan, '(I0)') n + varname = 'brightness_temperature_' // trim(chan) - ! store in the profile - profiles(iprof) % clw(:) = clw_temp(:) - profiles(iprof) % q(:) = q_temp(:) + end subroutine get_var_name - ! !store non active variable - ! IF (ALLOCATED(CloudIce)) THEN - ! CloudIce(toplevel_q_1dvar:) = ciw_temp(:) + !ufo_rttov_alloc is a wrapper for RTTOV12/13 allocation + subroutine ufo_rttov_setup_rtprof(self,geovals,obss,conf,ob_info) - ! ENDIF - enddo + use ufo_constants_mod, only : half, deg2rad, min_q, m_to_km, g_to_kg, pa_to_hpa + use ufo_rttovonedvarcheck_ob_mod - deallocate(Qtotal, q_temp, clw_temp, ciw_temp) + implicit none - end if + class(ufo_rttov_io), target, intent(inout) :: self + type(ufo_geovals), intent(in) :: geovals + type(c_ptr), value, intent(in) :: obss + type(rttov_conf), intent(in) :: conf + type(ufo_rttovonedvarcheck_ob), optional, intent(inout) :: ob_info - if(present(obs_info)) then - ! profiles(1)%elevation = obs_info%elevation / 1000.0_kind_real ! m -> km - ! profiles(1)%latitude = obs_info%latitude - ! profiles(1)%longitude = obs_info%longitude + ! Local variables + type(rttov_profile), pointer :: profiles(:) - else + integer :: jspec + integer :: nlevels + integer :: nprofiles - allocate(TmpVar(nlocs_total)) - variable_present = obsspace_has(obss, "MetaData", "elevation") - if (variable_present) then - call obsspace_get_db(obss, "MetaData", "elevation", TmpVar) - profiles(1:nprofiles)%elevation = TmpVar(prof_start:prof_start + nprofiles - 1) * m_to_km !for RTTOV - else - variable_present = obsspace_has(obss, "MetaData", "surface_height") - if (variable_present) then - call obsspace_get_db(obss, "MetaData", "surface_height", TmpVar) - profiles(1:nprofiles)%elevation = TmpVar(prof_start:prof_start + nprofiles - 1) * m_to_km !for RTTOV - else ! from geoval - if (ufo_vars_getindex(geovals%variables, 'surface_altitude') > 0) then - call ufo_geovals_get_var(geovals, 'surface_altitude', geoval) - profiles(1:nprofiles)%elevation = geoval%vals(1,prof_start:prof_start + nprofiles - 1) * m_to_km - else - write(message,'(A)') & - 'MetaData elevation not in database: check implicit filtering' - call fckit_log%info(message) - endif - endif - end if - - variable_present = obsspace_has(obss, "MetaData", "latitude") - if (variable_present) then - call obsspace_get_db(obss, "MetaData", "latitude", TmpVar ) - profiles(1:nprofiles)%latitude = TmpVar(prof_start:prof_start + nprofiles - 1) - else - write(message,'(A)') & - 'MetaData latitude not in database: check implicit filtering' - call fckit_log%info(message) - end if + type(ufo_geoval), pointer :: geoval + character(MAXVARLEN) :: varname - variable_present = obsspace_has(obss, "MetaData", "longitude") - if (variable_present) then - call obsspace_get_db(obss, "MetaData", "longitude", TmpVar ) - profiles(1:nprofiles)%longitude = TmpVar(prof_start:prof_start + nprofiles - 1) - else - write(message,'(A)') & - 'MetaData longitude not in database: check implicit filtering' - call fckit_log%info(message) - end if + real(kind_real) :: ifrac, sfrac, lfrac, wfrac + real(kind_real) :: itmp, stmp, ltmp + real(kind_real) :: s2m_t(1), s2m_p(1) - end if + real(kind_real), allocatable :: TmpVar(:), windsp(:), p(:) + logical :: variable_present - end subroutine load_atm_data_rttov + integer :: top_level, bottom_level, stride + real(kind_real) :: NewT + integer :: level_1000hPa, level_950hpa - ! Internal subprogam to load some test geometry data - subroutine load_geom_data_rttov(obss,profiles,prof_start1,obs_info) + real(kind_real), allocatable :: q_temp(:), clw_temp(:), ciw_temp(:), Qtotal(:), qsaturated(:) - use obsspace_mod, only : obsspace_get_nlocs, obsspace_get_db + real(kind_real) :: scale_fac - implicit none + profiles => self % profiles +!DAR: This will be extended for RTTOV_SCATT +!profiles_scatt = > self % profiles_scatt + if(present(ob_info)) then + nlocs_total = 1 + else + nlocs_total = obsspace_get_nlocs(obss) + end if - type(c_ptr), value, intent(in) :: obss - type(rttov_profile), intent(inout) :: profiles(:) - integer, optional, intent(in) :: prof_start1 - logical, optional, intent(in) :: obs_info ! DAR temporary + nprofiles = min(size(profiles), geovals%nlocs) - real(kind_real), allocatable :: TmpVar(:) + nlevels = size(profiles(1)%p) - integer :: prof_start - integer :: nprofiles - integer :: nlevels + ! Assume that the pressure profile coming from the geovals is increasing in pressure (ToA->surface)... + top_level = 1 + bottom_level = nlevels + stride=1 - if(present(prof_start1)) then - prof_start = prof_start1 - else - prof_start = 1 - end if + if (ufo_vars_getindex(geovals%variables, var_prs) > 0) then + call ufo_geovals_get_var(geovals, var_prs, geoval) - if(present(obs_info)) then + allocate(p(nlevels)) + do iprof = 1, nprofiles + p = geoval%vals(geoval%nval-nlevels+1:geoval%nval, iprof) * Pa_to_hPa ! for RTTOV + if (iprof == 1) then + if (p(1) > p(2)) then ! ...but be ready to switch. Assume if one profile is 'upside-down' then they all are + top_level = nlevels + bottom_level = 1 + stride = -1 + endif + endif + profiles(iprof)%p(top_level:bottom_level:stride) = p(:) + end do + deallocate(p) + endif - ! nprofiles = 1 - ! nlevels = SIZE(profiles(1)%p) +! Not in use until rttov_scatt +! !pressure half-levels +! IF (ufo_vars_getindex(geovals%variables, var_prsi) > 0 .and. & +! conf % do_mw_scatt) THEN +! CALL ufo_geovals_get_var(geovals, var_prsi, geoval) +! ALLOCATE(ph(nlevels+1)) +! do iprof = 1, nprofiles +! ph = geoval%vals(geoval%nval-(nlevels+1)+1:geoval%nval, iprof) * Pa_to_hPa +! if (iprof == 1) then +! if (ph(1) > ph(2)) then !upside-down +! top_level = nlevels - 1 +! bottom_level = 1 +! stride = -1 +! else +! top_level = 1 +! bottom_level = nlevels - 1 +! stride = 1 +! endif +! endif +! ! profiles(iprof)%p(top_level+stride:bottom_level:stride) = half * & +! ! (p(top_level:bottom_level-stride:stride) + p(top_level+stride:bottom_level:stride)) +! ! profiles(iprof)%p(1) = max( profiles(iprof)%p(2) - half * & +! ! (profiles(iprof)%p(3) - profiles(iprof)%p(2)),half * profiles(iprof)%p(2)) +! ! profiles(iprof)%p(nlevels) = profiles(iprof)%p(nlevels-1) - & +! ! half * (profiles(iprof)%p(nlevels-2) - profiles(iprof)%p(nlevels-1)) +! end do +! deallocate(ph) +! endif - ! profiles(1)%zenangle = obs_info%sensor_zenith_angle - ! profiles(1)%azangle = obs_info%sensor_azimuth_angle - ! profiles(1)%sunzenangle = obs_info%solar_zenith_angle - ! profiles(1)%sunazangle = obs_info%solar_azimuth_angle +! Get temperature + varname = var_ts + call ufo_geovals_get_var(geovals, varname, geoval) - else +! Check if temperatures are provided on levels as required for RTTOV, otherwise assume that temperatures are layer quantities +! (and all future atmospheric variables) and do some interpolation to prepare for RTTOV. +! TODO: Put a warning in here that this is happening - nprofiles = min(size(profiles), nlocs_total - prof_start + 1) !nlocs_total set in read_atm + do iprof = 1, nprofiles + profiles(iprof)%t(top_level:bottom_level:stride) = geoval%vals(:, iprof) ! K + end do - allocate(TmpVar(nlocs_total)) +! Get absorbers. + if (conf % RTTOV_GasUnitConv) then + !gas_units = 0 is ppmv dry. Conversion will be done prior to use by RTTOV. Currently only scalar conversion performed. + !this matches satrad conversion + profiles(1:nprofiles)%gas_units = gas_unit_ppmvdry + else + !gas_units = 1 is kg/kg moist. Conversion will be done internally by RTTOV + profiles(1:nprofiles)%gas_units = gas_unit_specconc + endif - nlevels = size(profiles(1)%p) + do jspec = 1, conf%ngas - !RTTOV convention 0-max (coef dependent). Nadir = 0 deg - call obsspace_get_db(obss, "MetaData", "sensor_zenith_angle", TmpVar) - if (any(TmpVar(prof_start:prof_start + nprofiles - 1) < 0.)) then - profiles(1:nprofiles)%zenangle = abs(TmpVar(prof_start:prof_start + nprofiles - 1)) - ! DAR need to make a modification to azangle here then? - else - profiles(1:nprofiles)%zenangle = abs(TmpVar(prof_start:prof_start + nprofiles - 1)) - endif + scale_fac = conf%scale_fac(conf%absorber_id(jspec)) - !RTTOV convention is 0-360 deg. E=+90 - call obsspace_get_db(obss, "MetaData", "sensor_azimuth_angle", TmpVar) - profiles(1:nprofiles)%azangle = TmpVar(prof_start:prof_start + nprofiles - 1) + call ufo_geovals_get_var(geovals,conf%Absorbers(jspec) , geoval) - call obsspace_get_db(obss, "MetaData", "solar_zenith_angle", TmpVar) - if (any(TmpVar(prof_start:prof_start + nprofiles - 1) < 0.)) then - profiles(1:nprofiles)%sunzenangle = abs(TmpVar(prof_start:prof_start + nprofiles - 1)) - ! DAR need to make a modification to sunazangle here then? - else - profiles(1:nprofiles)%sunzenangle = abs(TmpVar(prof_start:prof_start + nprofiles - 1)) - endif + select case (conf%Absorbers(jspec)) + case (var_mixr) ! mixr assumed to be in g/kg + do iprof = 1, nProfiles + profiles(iprof)%q(top_level:bottom_level:stride) = geoval%vals(:, iprof) * scale_fac * g_to_kg + end do + case (var_q) + do iprof = 1, nProfiles + profiles(iprof)%q(top_level:bottom_level:stride) = geoval%vals(:, iprof) * scale_fac + end do + case (var_oz) + if (associated(profiles(1)%o3)) then + do iprof = 1, nProfiles + profiles(iprof)%o3(top_level:bottom_level:stride) = geoval%vals(:, iprof) * scale_fac + end do + endif + case (var_co2) + if (associated(profiles(1)%co2)) then + do iprof = 1, nProfiles + profiles(iprof)%co2(top_level:bottom_level:stride) = geoval%vals(:, iprof) * scale_fac + end do + endif + case (var_clw) + if (associated(profiles(1)%clw)) then + do iprof = 1, nProfiles + profiles(iprof)%clw(top_level:bottom_level:stride) = geoval%vals(:, iprof) ! always kg/kg + end do + endif + case default - call obsspace_get_db(obss, "MetaData", "solar_azimuth_angle", TmpVar) - profiles(1:nprofiles)%sunazangle = TmpVar(prof_start:prof_start + nprofiles - 1) + end select - deallocate(TmpVar) + end do - end if +! Get near-surface variables (s2m) - end subroutine load_geom_data_rttov + varname = var_sfc_p2m + if (ufo_vars_getindex(geovals%variables, varname) > 0) then + call ufo_geovals_get_var(geovals, varname, geoval) - ! ------------------------------------------------------------------------------ + profiles(1:nprofiles)%s2m%p = geoval%vals(1,:) * Pa_to_hPa + else + write(message,'(A)') 'No near-surface pressure. Using bottom pressure level' + call fckit_log%info(message) - subroutine set_options_rttov(self, f_confOpts) - implicit none + do iprof = 1, nprofiles + profiles(iprof)%s2m%p = profiles(iprof)%p(nlevels) + enddo + endif - class(rttov_conf), intent(inout) :: self - type(fckit_configuration), intent(in) :: f_confOpts ! RTcontrol + varname = var_sfc_t2m ! 2m temperature + if (ufo_vars_getindex(geovals%variables, varname) > 0) then + call ufo_geovals_get_var(geovals, varname, geoval) + profiles(1:nprofiles)%s2m%t = geoval%vals(1,1:nprofiles) + else + write(message,'(A)') 'No near-surface temperature. Using bottom temperature level' + call fckit_log%info(message) + do iprof = 1, nprofiles + profiles(iprof)%s2m%t = profiles(iprof)%t(nlevels) + enddo + endif - include 'rttov_print_opts.interface' + varname = var_sfc_q2m ! 2m specific humidity + if (ufo_vars_getindex(geovals%variables, varname) > 0) then + call ufo_geovals_get_var(geovals, varname, geoval) ! lfric - call self % set_defaults(self%RTTOV_default_opts) ! test, OPS, RTTOV + profiles(1:nprofiles)%s2m%q = geoval%vals(1,1:nprofiles) * conf%scale_fac(gas_id_watervapour) + else + write(message,'(A)') 'No near-surface specific humidity. Using bottom q level' + call fckit_log%info(message) - !< Switch to enable atmospheric refraction - if (f_confOpts % has("RTTOV_addrefrac")) then - call f_confOpts % get_or_die("RTTOV_addrefrac", self % rttov_opts % rt_all % addrefrac) - end if + do iprof = 1, nprofiles + profiles(iprof)%s2m%q = profiles(iprof)%q(nlevels) + enddo + endif - !< Switch for input units in AD/K models - if (f_confOpts % has("RTTOV_switchrad")) then - call f_confOpts % get_or_die("RTTOV_switchrad", self % rttov_opts % rt_all % switchrad) - end if + varname = var_u ! Eastward-wind in m/s + if (ufo_vars_getindex(geovals%variables, varname) > 0) then + call ufo_geovals_get_var(geovals, varname, geoval) - !< Switch to enable use of 2m q variable - if (f_confOpts % has("RTTOV_use_q2m")) then - call f_confOpts % get_or_die("RTTOV_use_q2m", self % rttov_opts % rt_all % use_q2m) - end if + profiles(1:nprofiles)%s2m%u = geoval%vals(1,1:nprofiles) + !assume if eastward then northward too - !< Switch for setting Lambertian reflection (IR and MW) - if (f_confOpts % has("RTTOV_do_lambertian")) then - call f_confOpts % get_or_die("RTTOV_do_lambertian", self % rttov_opts % rt_all % do_lambertian) - end if + varname = var_v ! Northward-wind in m/s + call ufo_geovals_get_var(geovals, varname, geoval) - !< Switch for fixed/parameterised effective angle for Lambertian option - if (f_confOpts % has("RTTOV_lambertian_fixed_angle")) then - call f_confOpts % get_or_die("RTTOV_lambertian_fixed_angle", self % rttov_opts % rt_all % lambertian_fixed_angle) - end if + profiles(1:nprofiles)%s2m%v = geoval%vals(1,1:nprofiles) + else !! use windspeed and direction instead + allocate(windsp(nprofiles)) + call ufo_geovals_get_var(geovals, var_sfc_wspeed, geoval) - !< Switch to ignore atmospheric curvature - if (f_confOpts % has("RTTOV_plane_parallel")) then - call f_confOpts % get_or_die("RTTOV_plane_parallel", self % rttov_opts % rt_all % plane_parallel) - end if + windsp(1:nprofiles) = geoval%vals(1,1:nprofiles) - !< Linear-in-tau or layer-mean for downwelling radiances - if (f_confOpts % has("RTTOV_rad_down_lin_tau")) then - call f_confOpts % get_or_die("RTTOV_rad_down_lin_tau", self % rttov_opts % rt_all % rad_down_lin_tau) - end if + call ufo_geovals_get_var(geovals, var_sfc_wdir, geoval) - !< Switch to apply dtau test in transmit/integrate calculations - if (f_confOpts % has("RTTOV_dtau_test")) then - call f_confOpts % get_or_die("RTTOV_dtau_test", self % rttov_opts % rt_all % dtau_test) - end if + do iprof = 1, nprofiles + profiles(iprof)%s2m%u = windsp(iprof) * cos(geoval%vals(1, iprof) * deg2rad) + profiles(iprof)%s2m%v = windsp(iprof) * sin(geoval%vals(1, iprof) * deg2rad) + enddo + deallocate(windsp) + endif - !< FASTEM version (0-6); 0 => TESSEM2 - if (f_confOpts % has("RTTOV_fastem_version")) then - call f_confOpts % get_or_die("RTTOV_fastem_version", self % rttov_opts % rt_mw % fastem_version) - end if +! Get surface type from geoval if it exists else diagnose surface type from water fraction. +! If RTTOV surface type exists then it is expected that the related variables will also be available +! e.g. water type, Tskin +! DARFIX : This will be moved out to a separate subroutine in the next release to support all instruments +! particularly where we have additional surface metadata for certain instruments - !< Supply a foam fraction to FASTEM - if (f_confOpts % has("RTTOV_supply_foam_fraction")) then - call f_confOpts % get_or_die("RTTOV_supply_foam_fraction", self % rttov_opts % rt_mw % supply_foam_fraction) - end if + varname = var_surf_type_rttov ! RTTOV surface type: 0 (land), 1 (water), 2 (sea-ice) + if (ufo_vars_getindex(geovals%variables, varname) > 0) then + call ufo_geovals_get_var(geovals, varname, geoval) + profiles(1:nprofiles)%skin%surftype = int(geoval%vals(1,1:nprofiles), kind=jpim) - !< Switch to enable input of cloud liquid water profile - if (f_confOpts % has("RTTOV_clw_data")) then - call f_confOpts % get_or_die("RTTOV_clw_data", self % rttov_opts % rt_mw % clw_data) - end if + !varname = var_water_type_rttov ! RTTOV water type: 0 (fresh), 1 (sea) + !call ufo_geovals_get_var(geovals, varname, geoval) + !profiles(1:nprofiles)%skin%watertype = int(geoval%vals(1,1:nprofiles),kind=jpim) + profiles(1:nprofiles) % skin % watertype = 1 ! always assume ocean - !< MW CLW scheme: 1 => Liebe, 2 => Rosenkranz, 3 => TKC - if (f_confOpts % has("RTTOV_clw_scheme")) then - call f_confOpts % get_or_die("RTTOV_clw_scheme", self % rttov_opts % rt_mw % clw_scheme) - end if + varname = var_sfc_tskin !Skin (surface) temperature (K) + call ufo_geovals_get_var(geovals, varname, geoval) + profiles(1:nprofiles)%skin%t = geoval%vals(1,1:nprofiles) + + else - !< Apply MW CLW calculations on coef/user levels (true/false resp.) - if (f_confOpts % has("RTTOV_clw_calc_on_coef_lev")) then - call f_confOpts % get_or_die("RTTOV_clw_calc_on_coef_lev", self % rttov_opts % rt_mw % clw_calc_on_coef_lev) - end if + ! Try to diagnose RTTOV surface from water fraction + !DARFIX: this is not consistent with CRTM in any way. Need to find out how they select surface type. - !< Lower pressure limit for MW CLW calculations (hPa) - if (f_confOpts % has("RTTOV_clw_cloud_top")) then - call f_confOpts % get_or_die("RTTOV_clw_cloud_top", self % rttov_opts % rt_mw % clw_cloud_top) - end if + varname = var_sfc_wfrac + if (ufo_vars_getindex(geovals%variables, varname) > 0) then + call ufo_geovals_get_var(geovals, varname, geoval) - !< Apply band-correction for Planck radiance and BT calculations - if (f_confOpts % has("RTTOV_apply_band_correction")) then - call f_confOpts % get_or_die("RTTOV_apply_band_correction", self % rttov_opts % rt_mw % apply_band_correction) - end if + do iprof = 1, nprofiles + !Land point or sea point + wfrac = geoval%vals(1,iprof) + if (wfrac > half) then + profiles(iprof)%skin%surftype = surftype_sea + call ufo_geovals_get_var(geovals, var_sfc_wtmp, geoval) + profiles(iprof)%skin%t = geoval%vals(1, iprof) + else + !maybe it's predominantly land or ice + ! !determine land, snow and ice fractions and temperatures to determine average temperature + profiles(iprof)%skin%surftype = surftype_land ! land - !< Switch to enable RTTOV interpolator - if (f_confOpts % has("RTTOV_addinterp")) then - call f_confOpts % get_or_die("RTTOV_addinterp", self % rttov_opts % interpolation % addinterp) - end if + call ufo_geovals_get_var(geovals, var_sfc_lfrac, geoval) + lfrac = geoval%vals(1, iprof) - !< Interpolation mode (1-5, see user guide) - if (f_confOpts % has("RTTOV_interp_mode")) then - call f_confOpts % get_or_die("RTTOV_interp_mode", self % rttov_opts % interpolation % interp_mode) - end if + call ufo_geovals_get_var(geovals, var_sfc_sfrac, geoval) + sfrac = geoval%vals(1, iprof) - !< Switch to make pressure an active variable in TL/AD/K models - if (f_confOpts % has("RTTOV_lgradp")) then - call f_confOpts % get_or_die("RTTOV_lgradp", self % rttov_opts % interpolation % lgradp) - end if + call ufo_geovals_get_var(geovals, var_sfc_ifrac, geoval) + ifrac = geoval%vals(1, iprof) + + call ufo_geovals_get_var(geovals, var_sfc_ltmp, geoval) + ltmp = geoval%vals(1, iprof) + + call ufo_geovals_get_var(geovals, var_sfc_stmp, geoval) + stmp = geoval%vals(1, iprof) + + call ufo_geovals_get_var(geovals, var_sfc_itmp, geoval) + itmp = geoval%vals(1, iprof) + + !Skin temperature is a combination of (i)ce temp, (l)and temp and (s)now temp + profiles(iprof)%skin%t = (lfrac * ltmp + sfrac * stmp + ifrac * itmp) / (lfrac + sfrac + ifrac) + endif + end do + endif + endif - !< Switch to assume space boundary at top-most input pressure l - if (f_confOpts % has("RTTOV_spacetop")) then - call f_confOpts % get_or_die("RTTOV_spacetop", self % rttov_opts % interpolation % spacetop) - end if + !MCC: wind fetch fixed for now too + profiles(1:nprofiles) % s2m % wfetc = 100000.0_kind_real ! wind fetch (m) taken + ! from users guide - !< Switch to extrapolate input profiles using regression limits - if (f_confOpts % has("RTTOV_reg_limit_extrap")) then - call f_confOpts % get_or_die("RTTOV_reg_limit_extrap", self % rttov_opts % interpolation % reg_limit_extrap) - end if + !DAR: Salinity fixed for now too + profiles(1:nprofiles)%skin%salinity = 35.0_kind_real - if (f_confOpts % has("RTTOV_fix_hgpl")) then - call f_confOpts % get_or_die("RTTOV_fix_hgpl", self % rttov_opts % config % fix_hgpl) - end if + !DAR: Default fastem parameters. We are not using FASTEM over land so these are unused + do iprof = 1,nprofiles +! profiles(iprof)%skin%fastem = [3.0, 5.0, 15.0, 0.1, 0.3] + profiles(iprof)%skin%fastem = [0,0,0,0,0] + end do - if (f_confOpts % has("RTTOV_verbose")) then - call f_confOpts % get_or_die("RTTOV_verbose", self % rttov_opts % config % verbose) - end if +! --------------------------- +! SatRad profile manipulation +! --------------------------- - if (f_confOpts % has("RTTOV_do_checkinput")) then - call f_confOpts % get_or_die("RTTOV_do_checkinput", self % rttov_opts % config % do_checkinput) - end if + if(conf % SatRad_compatibility) then + do iprof = 1, nProfiles + !---- + ! Reset low level temperatures over seaice and cold, low land as per Ops_SatRad_SetUpRTprofBg.F90 + ! N.B. I think this should be flagged so it's clear that the background has been modified + !---- + + if(profiles(iprof)%skin%surftype /= surftype_sea .and. & + conf % UseColdSurfaceCheck) then + if(profiles(iprof)%skin%t < 271.4_kind_real .and. & + profiles(iprof)%s2m%p > 950.0_kind_real) then - if (f_confOpts % has("RTTOV_apply_reg_limits")) then - call f_confOpts % get_or_die("RTTOV_apply_reg_limits", self % rttov_opts % config % apply_reg_limits) - end if + level_1000hpa = minloc(abs(profiles(iprof)%p - 1000.0_kind_real),DIM=1) + level_950hpa = minloc(abs(profiles(iprof)%p - 950.0_kind_real),DIM=1) - !< Solar sea BRDF model (1-2) - if (f_confOpts % has("RTTOV_solar_sea_brdf_model")) then - call f_confOpts % get_or_die("RTTOV_solar_sea_brdf_model", self % rttov_opts % rt_ir % solar_sea_brdf_model) - end if + NewT = profiles(iprof)%t(level_950hpa) + if(profiles(iprof)%s2m%p > 1000.0_kind_real) & + NewT = max(NewT,profiles(iprof)%t(level_1000hPa)) + NewT = min(NewT, 271.4_kind_real) - !< IR sea emissivity model (1-2) - if (f_confOpts % has("RTTOV_ir_sea_emis_model")) then - call f_confOpts % get_or_die("RTTOV_ir_sea_emis_model", self % rttov_opts % rt_ir % ir_sea_emis_model) - end if + profiles(iprof)%t(level_1000hPa) = max(profiles(iprof)%t(level_1000hPa), NewT) + profiles(iprof)%s2m%t = max(profiles(iprof)%s2m%t, NewT) + profiles(iprof)%skin%t = max(profiles(iprof)%skin%t, NewT) + endif + endif - !< Switch to enable solar simulations - if (f_confOpts % has("RTTOV_addsolar")) then - call f_confOpts % get_or_die("RTTOV_addsolar", self % rttov_opts % rt_ir % addsolar) - end if + ! ----------------------------------------------- + ! Make sure q does not exceed saturation + ! ----------------------------------------------- + allocate(qsaturated(nlevels)) + if (conf % UseRHwaterForQC) then + call Ops_QsatWat (qsaturated(:), & ! out + profiles(iprof) % t(:), & ! in + profiles(iprof) % p(:) / Pa_to_hPa, & ! in convert hPa to Pa + nlevels) ! in + else + call Ops_Qsat (qsaturated(:), & ! out + profiles(iprof) % t(:), & ! in + profiles(iprof) % p(:) / Pa_to_hPa, & ! in convert hPa to Pa + nlevels) ! in + end if - !< Switch to enable Rayleigh single-scattering for VIS/NIR channel - if (f_confOpts % has("RTTOV_rayleigh_single_scatt")) then - call f_confOpts % get_or_die("RTTOV_rayleigh_single_scatt", self % rttov_opts % rt_ir % rayleigh_single_scatt) - end if + qsaturated = qsaturated * conf%scale_fac(gas_id_watervapour) + +!qsaturated is assumed to be in kg/kg + where (profiles(iprof)%q > qsaturated) + profiles(iprof)%q = qsaturated + end where + deallocate(qsaturated) + + ! ----------------------------------------------- + ! Make sure q2m does not exceed saturation + ! ----------------------------------------------- + allocate(qsaturated(1)) + s2m_t(1) = profiles(iprof)%s2m%t + s2m_p(1) = profiles(iprof)%s2m%p / Pa_to_hPa + if (conf % UseRHwaterForQC) then + call Ops_QsatWat (qsaturated(1:1), & ! out + s2m_t(1:1), & ! in + s2m_p(1:1), & ! in + 1) ! in + else + call Ops_Qsat (qsaturated(1:1), & ! out + s2m_t(1:1), & ! in + s2m_p(1:1), & ! in + 1) ! in + end if - !< Switch to enable NLTE bias correction - if (f_confOpts % has("RTTOV_do_nlte_correction")) then - call f_confOpts % get_or_die("RTTOV_do_nlte_correction", self % rttov_opts % rt_ir % do_nlte_correction) - end if + qsaturated(1) = qsaturated(1) * conf%scale_fac(gas_id_watervapour) - !< Switch to enable IR aerosol calculations - if (f_confOpts % has("RTTOV_addaerosl")) then - call f_confOpts % get_or_die("RTTOV_addaerosl", self % rttov_opts % rt_ir % addaerosl) - end if + if (profiles(iprof)%s2m%q > qsaturated(1)) profiles(iprof)%s2m%q = qsaturated(1) + deallocate(qsaturated) - !< Switch to supply aerosol optical properties explicitly per channel - if (f_confOpts % has("RTTOV_user_aer_opt_param")) then - call f_confOpts % get_or_die("RTTOV_user_aer_opt_param", self % rttov_opts % rt_ir % user_aer_opt_param) - end if + ! Constrain small values to min_q fix + where(profiles(iprof)%q < min_q * conf%scale_fac(gas_id_watervapour) ) profiles(iprof)%q = min_q * conf%scale_fac(gas_id_watervapour) + if(profiles(iprof)%s2m%q < min_q * conf%scale_fac(gas_id_watervapour)) profiles(iprof)%s2m%q = min_q * conf%scale_fac(gas_id_watervapour) - !< Switch to enable IR cloudy calculations - if (f_confOpts % has("RTTOV_addclouds")) then - call f_confOpts % get_or_die("RTTOV_addclouds", self % rttov_opts % rt_ir % addclouds) - end if + enddo + endif - !< Switch to supply cloud optical properties explicitly per channel - if (f_confOpts % has("RTTOV_user_cld_opt_param")) then - call f_confOpts % get_or_die("RTTOV_", self % rttov_opts % rt_ir % user_cld_opt_param) - end if + !non mwscatt only at the moment + if(conf % SplitQtotal) then + allocate(Qtotal(nlevels), q_temp(nlevels), clw_temp(nlevels), ciw_temp(nlevels)) + do iprof = 1, nprofiles + ! compute bg qtotal using q and clw only + ! currently ice is ignored - !< Switch to supply grid-box average cloud concentration or cloud - if (f_confOpts % has("RTTOV_grid_box_avg_cloud")) then - call f_confOpts % get_or_die("RTTOV_grid_box_avg_cloud", self % rttov_opts % rt_ir % grid_box_avg_cloud) - end if + Qtotal(:) = profiles(iprof) % q(:) / conf%scale_fac(gas_id_watervapour) ! kg/kg + Qtotal(:) = max(Qtotal(:), min_q) + Qtotal(:) = Qtotal(:) + profiles(iprof) % clw(:) - !! concentration in cloudy fraction of each layer - !< Ignore cloud streams with weights lower than this - if (f_confOpts % has("RTTOV_cldstr_threshold")) then - call f_confOpts % get_or_die("RTTOV_cldstr_threshold", self % rttov_opts % rt_ir % cldstr_threshold) - end if + ! generate first guess cloud and q based on the qtotal physics + call Ops_SatRad_Qsplit (1, & ! in + profiles(iprof) % p(:) / Pa_to_hPa, & ! in convert hPa to Pa + profiles(iprof) % t(:), & ! in + Qtotal(:), & ! in + q_temp(:), & ! out + clw_temp(:), & ! out + ciw_temp(:), & ! out + UseQtSplitRain = conf % UseQtSplitRain) - !< Switch for simplified cloud stream option - USE WITH CAUTION - if (f_confOpts % has("RTTOV_cldstr_simple")) then - call f_confOpts % get_or_die("RTTOV_cldstr_simple", self % rttov_opts % rt_ir % cldstr_simple) - end if + ! store the profile + profiles(iprof) % clw(:) = clw_temp(:) + profiles(iprof) % q(:) = q_temp(:) * conf%scale_fac(gas_id_watervapour) - !< Upper pressure limit for cldstr_simple option (hPa) - if (f_confOpts % has("RTTOV_cldstr_low_cloud_top")) then - call f_confOpts % get_or_die("RTTOV_cldstr_low_cloud_top", self % rttov_opts % rt_ir % cldstr_low_cloud_top) - end if + ! !store non active variable + ! IF (ALLOCATED(CloudIce)) THEN - !< IR scattering model to use - if (f_confOpts % has("RTTOV_ir_scatt_model")) then - call f_confOpts % get_or_die("RTTOV_ir_scatt_model", self % rttov_opts % rt_ir % ir_scatt_model) - end if + ! CloudIce(toplevel_q_1dvar:) = ciw_temp(:) - !< VIS/NIR scattering model to use - if (f_confOpts % has("RTTOV_vis_scatt_model")) then - call f_confOpts % get_or_die("RTTOV_vis_scatt_model", self % rttov_opts % rt_ir % vis_scatt_model) - end if + ! ENDIF + enddo - !< Number of DOM streams, must be even and not less than 2 - if (f_confOpts % has("RTTOV_dom_nstreams")) then - call f_confOpts % get_or_die("RTTOV_dom_nstreams", self % rttov_opts % rt_ir % dom_nstreams) - end if + deallocate(Qtotal, q_temp, clw_temp, ciw_temp) - !< Convergence criterion for termination of DOM azimuthal loop - if (f_confOpts % has("RTTOV_dom_accuracy")) then - call f_confOpts % get_or_die("RTTOV_dom_accuracy", self % rttov_opts % rt_ir % dom_accuracy) end if - !< DOM ignores levels below this optical depth: - if (f_confOpts % has("RTTOV_dom_opdep_threshold")) then - call f_confOpts % get_or_die("RTTOV_dom_opdep_threshold", self % rttov_opts % rt_ir % dom_opdep_threshold) - end if +! Set geometry for RTTOV calculation - !< Switch to enable input of O3 profile - if (f_confOpts % has("RTTOV_ozone_data")) then - call f_confOpts % get_or_die("RTTOV_ozone_data", self % rttov_opts % rt_ir % ozone_data) - end if - !< Switch to enable input of CO2 profile - if (f_confOpts % has("RTTOV_co2_data")) then - call f_confOpts % get_or_die("RTTOV_co2_data", self % rttov_opts % rt_ir % co2_data) - end if + if(present(ob_info)) then - !< Switch to enable input of N2O profile - if (f_confOpts % has("RTTOV_n2o_data")) then - call f_confOpts % get_or_die("RTTOV_n2o_data", self % rttov_opts % rt_ir % n2o_data) - end if + profiles(1) % elevation = ob_info % elevation / 1000.0 ! m -> km + profiles(1) % latitude = ob_info % latitude + profiles(1) % longitude = ob_info % longitude + if (ob_info % retrievecloud) then + profiles(1) % ctp = ob_info % cloudtopp + profiles(1) % cfraction = ob_info % cloudfrac + end if - !< Switch to enable input of CO profile - if (f_confOpts % has("RTTOV_co_data")) then - call f_confOpts % get_or_die("RTTOV_co_data", self % rttov_opts % rt_ir % co_data) - end if + nlocs_total = 1 + nprofiles = 1 + nlevels = size(profiles(1) % p) - !< Switch to enable input of CH4 profile - if (f_confOpts % has("RTTOV_ch4_data")) then - call f_confOpts % get_or_die("RTTOV_ch4_data", self % rttov_opts % rt_ir % ch4_data) - end if + profiles(1) % zenangle = ob_info % sensor_zenith_angle + profiles(1) % azangle = ob_info % sensor_azimuth_angle + profiles(1) % sunzenangle = ob_info % solar_zenith_angle + profiles(1) % sunazangle = ob_info % solar_azimuth_angle - !< Switch to enable input of SO2 profile - if (f_confOpts % has("RTTOV_so2_data")) then - call f_confOpts % get_or_die("RTTOV_so2_data", self % rttov_opts % rt_ir % so2_data) - end if + else - ! OPEN(666,file='RTTOV_options.log') - ! CALL rttov_print_opts(self % rttov_opts,lu=666) - ! CLOSE(666) - end subroutine set_options_rttov + allocate(TmpVar(nlocs_total)) - ! ------------------------------------------------------------------------------ +!Set RT profile elevation (ob has priority, otherwise model height from geoval) + if (obsspace_has(obss, "MetaData", "elevation")) then + call obsspace_get_db(obss, "MetaData", "elevation", TmpVar) + profiles(1:nprofiles)%elevation = TmpVar(1:nprofiles) * m_to_km !for RTTOV + else if (obsspace_has(obss, "MetaData", "surface_height")) then + call obsspace_get_db(obss, "MetaData", "surface_height", TmpVar) + profiles(1:nprofiles)%elevation = TmpVar(1:nprofiles) * m_to_km !for RTTOV + else if (obsspace_has(obss, "MetaData", "model_orography")) then + call obsspace_get_db(obss, "MetaData", "model_orography", TmpVar) + profiles(1:nprofiles)%elevation = TmpVar(1:nprofiles) * m_to_km !for RTTOV + else if (ufo_vars_getindex(geovals%variables, 'surface_altitude') > 0) then + call ufo_geovals_get_var(geovals, 'surface_altitude', geoval) + profiles(1:nprofiles)%elevation = geoval%vals(1, 1:nprofiles) * m_to_km + else + write(message,'(A)') 'MetaData elevation not in database: check implicit filtering' + call fckit_log%info(message) + endif - subroutine setup_rttov(self, f_confOpts, asw) - class(rttov_conf), intent(inout) :: self - type(fckit_configuration), intent(in) :: f_confOpts ! RTcontrol - integer, intent(in) :: asw !allocate switch +!lat/lon + variable_present = obsspace_has(obss, "MetaData", "latitude") + if (variable_present) then + call obsspace_get_db(obss, "MetaData", "latitude", TmpVar ) + profiles(1:nprofiles)%latitude = TmpVar(1:nprofiles) + else + write(message,'(A)') & + 'MetaData latitude not in database: check implicit filtering' + call fckit_log%info(message) + end if - character(len=255) :: coef_filename - character(len=4) :: coef_ext - integer :: i_inst + variable_present = obsspace_has(obss, "MetaData", "longitude") + if (variable_present) then + call obsspace_get_db(obss, "MetaData", "longitude", TmpVar ) + profiles(1:nprofiles)%longitude = TmpVar(1:nprofiles) + else + write(message,'(A)') & + 'MetaData longitude not in database: check implicit filtering' + call fckit_log%info(message) + end if - include 'rttov_read_coefs.interface' +!Set RTTOV viewing geometry - rttov_errorstatus = 0 - coef_ext = '.dat' + ! sensor zenith - RTTOV convention 0-max (coef dependent). Nadir = 0 deg + ! mandatory so assume it's present + call obsspace_get_db(obss, "MetaData", "sensor_zenith_angle", TmpVar) + profiles(1:nprofiles)%zenangle = abs(TmpVar(1:nprofiles)) - if(asw == 1) then + ! sensor azimuth - convention is 0-360 deg. E=+90 + variable_present = obsspace_has(obss, "MetaData", "sensor_azimuth_angle") + if (variable_present) then + call obsspace_get_db(obss, "MetaData", "sensor_azimuth_angle", TmpVar) + profiles(1:nprofiles)%azangle = TmpVar(1:nprofiles) + else + write(message,'(A)') 'MetaData azimuth angle not in database: setting to zero' + call fckit_log%info(message) + profiles(1:nprofiles)%azangle = zero + end if - ! -------------------------------------------------------------------------- - ! 1. Setup rttov options - ! -------------------------------------------------------------------------- - call self % set_options(f_confOpts) + ! solar zenith + variable_present = obsspace_has(obss, "MetaData", "solar_zenith_angle") + if (variable_present) then + call obsspace_get_db(obss, "MetaData", "solar_zenith_angle", TmpVar) + profiles(1:nprofiles)%sunzenangle = TmpVar(1:nprofiles) + else + write(message,'(A)') 'MetaData solar zenith angle not in database: setting to zero' + call fckit_log%info(message) + profiles(1:nprofiles)%sunzenangle = zero + end if - ! -------------------------------------------------------------------------- - ! 2. Read coefficients - ! -------------------------------------------------------------------------- - allocate(self % rttov_coef_array(self % nSensors)) + ! solar azimuth + variable_present = obsspace_has(obss, "MetaData", "solar_azimuth_angle") + if (variable_present) then + call obsspace_get_db(obss, "MetaData", "solar_azimuth_angle", TmpVar) + profiles(1:nprofiles)%sunazangle = TmpVar(1:nprofiles) + else + write(message,'(A)') 'MetaData solar azimuth angle not in database: setting to zero' + call fckit_log%info(message) + profiles(1:nprofiles)%sunazangle = zero + end if - do i_inst = 1, self%nSensors - coef_filename = & - trim(self % COEFFICIENT_PATH) // 'rtcoef_' // trim(self%SENSOR_ID(i_inst)) // trim(coef_ext) + deallocate(TmpVar) + + end if - call rttov_read_coefs(rttov_errorstatus, & !out - self % rttov_coef_array(i_inst), & !inout - self % rttov_opts, & !in - file_coef = coef_filename) !in +! deallocate(profiles) +! nullify(profiles) +! deallocate(geoval) +! nullify(geoval) - if (rttov_errorstatus /= errorstatus_success) then - write(*,*) 'fatal error reading coefficients' - else - write(*,*) 'successfully read' // coef_filename - end if + end subroutine ufo_rttov_setup_rtprof - end do + subroutine ufo_rttov_check_rtprof(self, conf, iprof, i_inst) + implicit none + + class(ufo_rttov_io), target, intent(inout) :: self + type(rttov_conf), intent(in) :: conf + integer, intent(in) :: iprof + integer, intent(in) :: i_inst - self % rttov_is_setup =.true. - else !asw == 0 - deallocate(self % rttov_coef_array) - self%rttov_is_setup =.false. - end if + character(10) :: prof_str - end subroutine setup_rttov + include 'rttov_print_profile.interface' + include 'rttov_user_profile_checkinput.interface' - ! ------------------------------------------------------------------------------ + call rttov_user_profile_checkinput(rttov_errorstatus, & + conf % rttov_opts, & + conf % rttov_coef_array(i_inst), & + self % profiles(iprof)) - subroutine get_var_name(n,varname) + ! print erroneous profile to stderr + if(rttov_errorstatus /= errorstatus_success) then + write(prof_str,'(i0)') iprof + self % profiles(iprof) % id = prof_str + call rttov_print_profile(self % profiles(iprof), lu = stderr) + endif + + end subroutine ufo_rttov_check_rtprof - integer, intent(in) :: n - character(len=*), intent(out) :: varname + subroutine ufo_rttov_print_rtprof(self, conf, iprof, i_inst) + implicit none + + class(ufo_rttov_io), target, intent(inout) :: self + type(rttov_conf), intent(in) :: conf + integer, intent(in) :: iprof + integer, intent(in) :: i_inst - character(len=6) :: chan + character(10) :: prof_str - write(chan, '(I0)') n - varname = 'brightness_temperature_' // trim(chan) + include 'rttov_print_profile.interface' + write(*,*) 'profile ', iprof + if (any(conf % inspect == iprof)) then + write(prof_str,'(i0)') iprof + self % profiles(iprof) % id = prof_str + call rttov_print_profile(self % profiles(iprof), lu = stdout) + endif - end subroutine get_var_name + end subroutine ufo_rttov_print_rtprof !ufo_rttov_alloc is a wrapper for RTTOV12/13 allocation - subroutine alloc_rttov(self, errorstatus, conf, nprofiles, nchannels, nlevels, init, asw) + subroutine ufo_rttov_alloc_direct(self, errorstatus, conf, nprofiles, nchannels, nlevels, init, asw) implicit none - class(ufo_rttov_io), intent(inout) :: self + class(ufo_rttov_io), target, intent(inout) :: self type(rttov_conf), intent(in) :: conf integer, intent(out) :: errorstatus ! Return error status of RTTOV subroutine calls @@ -1181,164 +1337,232 @@ subroutine alloc_rttov(self, errorstatus, conf, nprofiles, nchannels, nlevels, i integer , intent(in) :: nlevels logical :: init1 - integer :: asw_direct, asw_k - - integer, save :: called_from include 'rttov_alloc_direct.interface' - include 'rttov_alloc_k.interface' - asw_direct = -1 - asw_k = -1 + if (present(init)) then + init1 = init + else + init1 = .true. + endif + ! Allocate structures for rttov_direct + !profiles are already allocated + call rttov_alloc_direct( & + errorstatus, & + asw, & ! 1 => allocate + nprofiles, & + nchannels, & + nlevels, & + opts = conf % rttov_opts, & + coefs = conf % rttov_coef_array(1), & + transmission = self % transmission, & + radiance = self % radiance, & + calcemis = self % calcemis, & + emissivity = self % emissivity, & + init = init1) + + if (errorstatus /= errorstatus_success) then + write(message,'(A, I6)') 'after rttov_alloc_direct error = ', errorstatus + call abor1_ftn(message) + end if + if(asw == 1) then - asw_direct = 1 - called_from = 1 - endif - if(asw == 2) then - asw_k = 1 - called_from = 2 + !additional initialisation + self % calcemis = .false. + self % emissivity % emis_in = -1.0_kind_real + self % emissivity % emis_out = -1.0_kind_real endif - + + end subroutine ufo_rttov_alloc_direct + + !ufo_rttov_alloc is a wrapper for RTTOV12/13 allocation + subroutine ufo_rttov_alloc_k(self, errorstatus, conf, nprofiles, nchannels, nlevels, init, asw) + implicit none + + class(ufo_rttov_io), target, intent(inout) :: self + type(rttov_conf), intent(in) :: conf + + integer, intent(out) :: errorstatus ! Return error status of RTTOV subroutine calls + integer, intent(in) :: asw ! allocate switch + logical, optional, intent(in) :: init ! initialise, default yes + integer , intent(in) :: nchannels + integer , intent(in) :: nprofiles + integer , intent(in) :: nlevels + + logical :: init1 + + include 'rttov_alloc_k.interface' + if (present(init)) then init1 = init - else + else init1 = .true. endif - if(asw_direct /= -1 .and. called_from == 1) then - ! Allocate structures for rttov_direct - call rttov_alloc_direct( & - errorstatus, & - asw_direct, & ! 1 => allocate - nprofiles, & - nchannels, & - nlevels, & - self % chanprof, & - conf % rttov_opts, & - self % profiles, & - conf % rttov_coef_array(1), & - self % transmission, & - self % radiance, & - calcemis = self % calcemis, & - emissivity = self % emissivity, & - init=init1) - + call rttov_alloc_k( & + errorstatus, & + asw, & ! 1 => allocate + nprofiles, & + nchannels, & + nlevels, & + opts = conf % rttov_opts, & + coefs = conf % rttov_coef_array(1), & + transmission_k = self % transmission_k, & + radiance_k = self % radiance_k, & + emissivity_k = self % emissivity_k, & + init = init1) + if (errorstatus /= errorstatus_success) then - write(message,'(A, I6)') 'after rttov_alloc_direct error = ', errorstatus + write(message,'(A, I6)') 'after rttov_alloc_k error = ', errorstatus call abor1_ftn(message) end if - endif + if (asw > 0 ) then + + ! Inintialize the K-matrix INPUT so that the results are dTb/dx + ! ------------------------------------------------------------- + + self % emissivity_k(:) % emis_out = 0 + self % emissivity_k(:) % emis_in = 0 + self % emissivity(:) % emis_out = 0 + self % radiance_k % bt(:) = 1 + self % radiance_k % total(:) = 1 - if (asw_k /= -1 .and. called_from == 2) then - ! Allocate structures for rttov_k - call rttov_alloc_k( & - errorstatus, & - asw_k, & ! 1 => allocate - nprofiles, & - nchannels, & - NLEVELS, & - self % chanprof, & - conf % rttov_opts, & - self % profiles, & - self % profiles_k, & - conf % rttov_coef_array(1), & - self % transmission, & - self % transmission_k, & - self % radiance, & - self % radiance_k, & - calcemis = self % calcemis, & - emissivity = self % emissivity, & - emissivity_k = self % emissivity_k, & - init=init1) + endif if (errorstatus /= errorstatus_success) then write(message,'(A, I6)') 'after rttov_alloc_k error = ', errorstatus call abor1_ftn(message) end if + + end subroutine ufo_rttov_alloc_k + + !ufo_rttov_alloc is a wrapper for RTTOV12/13 allocation + subroutine ufo_rttov_alloc_profiles(self, errorstatus, conf, nprofiles, nlevels, init, asw) + implicit none + + class(ufo_rttov_io), target, intent(inout) :: self + type(rttov_conf), intent(in) :: conf + + integer, intent(out) :: errorstatus ! Return error status of RTTOV subroutine calls + integer, intent(in) :: asw ! allocate switch + logical, optional, intent(in) :: init ! initialise, default yes + integer , intent(in) :: nprofiles + integer , intent(in) :: nlevels + + logical :: init1 + + include 'rttov_alloc_prof.interface' + + if (present(init)) then + init1 = init + else + init1 = .true. endif - if ( asw /= 0 ) then - self % calcemis = .false. - self % emissivity % emis_in = -1.0_kind_real - self % emissivity % emis_out = -1.0_kind_real + if (asw == 1) allocate (self % profiles(nprofiles)) + + ! Allocate structures for rttov_direct + call rttov_alloc_prof( & + errorstatus, & + nprofiles, & + self % profiles, & + nlevels, & + conf % rttov_opts, & + asw, & + coefs = conf % rttov_coef_array(1), & + init=init1) + + if (errorstatus /= errorstatus_success) then + write(message,'(A, I6)') 'after rttov_alloc_profiles error = ', errorstatus + call abor1_ftn(message) + end if + + if (asw == 0) then + deallocate (self % profiles) + else self % profiles(:) % skin % surftype = -1_jpim endif - end subroutine alloc_rttov + end subroutine ufo_rttov_alloc_profiles - subroutine ufo_rttov_skip_profiles(nprofiles,nchannels,channels,obss,Skip_Profiles) - ! Profiles are skipped when the ObsValue of all channels is missing. - ! TODO: Use complete QC information - ! It would be more comprehensive to use EffectiveQC or EffectiveError. That - ! would require those ObsSpace values to be initialized before calls to - ! this subroutine within ufo_radiancerttov_simobs+ufo_radiancerttov_tlad_settraj. + !ufo_rttov_alloc is a wrapper for RTTOV12/13 allocation + subroutine ufo_rttov_alloc_profiles_k(self, errorstatus, conf, nprofiles, nlevels, init, asw) implicit none - integer, intent(in) :: nProfiles, nChannels - type(c_ptr), value, intent(in) :: obss - integer(c_int), intent(in) :: channels(:) - logical, intent(inout) :: Skip_Profiles(:) + class(ufo_rttov_io), target, intent(inout) :: self + type(rttov_conf), intent(in) :: conf - integer :: jprofile, jchannel - character(len=MAXVARLEN) :: varname - real(kind_real) :: ObsVal(nprofiles,nchannels) - !real(kind_real) :: EffObsErr(n_Profiles,n_Channels) - !integer :: EffQC(n_Profiles,n_Channels) + integer, intent(out) :: errorstatus ! Return error status of RTTOV subroutine calls + integer, intent(in) :: asw ! allocate switch + logical, optional, intent(in) :: init ! initialise, default yes + integer , intent(in) :: nprofiles + integer , intent(in) :: nlevels - ! Set missing value - missing = missing_value(missing) + logical :: init1 - ObsVal = missing - ! EffObsErr = missing - ! EffQC = 0 + include 'rttov_alloc_prof.interface' - do jchannel = 1, nchannels - call get_var_name(channels(jchannel),varname) - call obsspace_get_db(obss, "ObsValue", varname, ObsVal(:,jchannel)) - ! call obsspace_get_db(obss, "EffectiveError", varname, EffObsErr(:,jchannel)) - ! call obsspace_get_db(obss, "EffectiveQC{iter}", varname, EffQC(:,jchannel)) - enddo + if (present(init)) then + init1 = init + else + init1 = .true. + endif - !Loop over all n_Profiles, i.e. number of locations - do jprofile = 1, nprofiles - Skip_Profiles(jprofile) = all(ObsVal(jprofile,:) == missing) .or. all(ObsVal(jprofile,:) > 400) - ! .OR. all(EffObsErr(jprofile,:) == missing) & - ! .OR. all(EffQC(jprofile,:) /= 0) - end do + if (asw == 1) then + allocate (self % profiles_k(nprofiles)) + endif + + ! Allocate structures for rttov_direct + call rttov_alloc_prof( & + errorstatus, & + nprofiles, & + self % profiles_k, & + nlevels, & + conf % rttov_opts, & + asw, & + coefs = conf % rttov_coef_array(1), & + init=init1) + + if (errorstatus /= errorstatus_success) then + write(message,'(A, I6)') 'after rttov_alloc_profiles error = ', errorstatus + call abor1_ftn(message) + end if + + !don't deallocate profiles_k! That's the trajectory. - end subroutine ufo_rttov_skip_profiles + end subroutine ufo_rttov_alloc_profiles_k - subroutine ufo_rttov_init_emissivity(self, conf) + subroutine ufo_rttov_init_emissivity(self, conf, prof_start) class(ufo_rttov_io), intent(inout) :: self type(rttov_conf), intent(in) :: conf - integer :: prof, ichan + integer, intent(in) :: prof_start - self % emissivity(:) % emis_in = -1.0_kind_real ! RMDI? - self % emissivity(:) % emis_out = -1.0_kind_real ! RMDI? - self % calcemis(:) = .false. + integer :: prof, ichan -!Emissivity and calcemis are only set for used channels. So if a profile is skipped then you must not set emis data for the channels that are skipped +!Emissivity and calcemis are only set for used channels. +!So if a profile is skipped then you must not set emis data for the channels that are skipped if ( conf % rttov_coef_array(1) % coef % id_sensor == sensor_id_mw) then do ichan = 1, nchan_sim, nchan_inst ! all channels initialised equally - prof = self % chanprof(ichan)%prof - if (self % profiles(prof) % skin % surftype == surftype_sea) then + prof = prof_start + self % chanprof(ichan)%prof - 1 + self % calcemis(ichan:ichan + nchan_inst - 1) = .false. + if (self % profiles(prof) % skin % surftype == surftype_sea) then self % emissivity(ichan:ichan + nchan_inst - 1) % emis_in = 0.0_kind_real self % calcemis(ichan:ichan + nchan_inst - 1) = .true. else !IF ATLAS - + !ELSE if (self % profiles(prof) % skin % surftype == surftype_land) then - + !IF FASTEM (not implemented at MetO) ELSE self % emissivity(ichan:ichan + nchan_inst - 1) % emis_in = 0.95_kind_real elseif (self % profiles(prof) % skin % surftype == surftype_seaice) then - + !IF FASTEM (not implemented at MetO) ELSE self % emissivity(ichan:ichan + nchan_inst - 1) % emis_in = 0.92_kind_real endif @@ -1367,188 +1591,163 @@ subroutine ufo_rttov_init_emissivity(self, conf) endif enddo endif - + + end subroutine ufo_rttov_init_emissivity subroutine set_defaults_rttov(self, default_opts_set) class(rttov_conf), intent(inout) :: self - character(8), intent(in) :: default_opts_set + character(10), intent(in) :: default_opts_set + + integer :: PS_Number + logical :: PS_configuration write(message,'(A, A)') 'Setting RTTOV default options to ', default_opts_set call fckit_log%info(message) - select case (trim(default_opts_set)) - case ('RTTOV') ! RTTOV defaults - needn't set this... - self % rttov_opts % rt_all % addrefrac = .false. !< Switch to enable atmospheric refraction - self % rttov_opts % rt_all % switchrad = .false. !< Switch for input units in AD/K models - self % rttov_opts % rt_all % use_q2m = .true. !< Switch to enable use of 2m q variable - self % rttov_opts % rt_all % do_lambertian = .false. !< Switch for setting Lambertian reflection (IR and MW) - self % rttov_opts % rt_all % lambertian_fixed_angle = .true. !< Switch for fixed/parameterised effective angle for Lambertian option - self % rttov_opts % rt_all % plane_parallel = .false. !< Switch to ignore atmospheric curvature - self % rttov_opts % rt_all % rad_down_lin_tau = .true. !< Linear-in-tau or layer-mean for downwelling radiances - self % rttov_opts % rt_all % dtau_test = .true. !< Switch to apply dtau test in transmit/integrate calculations - - self % rttov_opts % rt_ir % solar_sea_brdf_model = 1 !< Solar sea BRDF model (1-2) - self % rttov_opts % rt_ir % ir_sea_emis_model = 2 !< IR sea emissivity model (1-2) - self % rttov_opts % rt_ir % addsolar = .false. !< Switch to enable solar simulations - self % rttov_opts % rt_ir % rayleigh_single_scatt = .true. !< Switch to enable Rayleigh single-scattering for VIS/NIR channels - self % rttov_opts % rt_ir % do_nlte_correction = .false. !< Switch to enable NLTE bias correction - self % rttov_opts % rt_ir % addaerosl = .false. !< Switch to enable IR aerosol calculations - self % rttov_opts % rt_ir % user_aer_opt_param = .false. !< Switch to supply aerosol optical properties explicitly per channel - self % rttov_opts % rt_ir % addclouds = .false. !< Switch to enable IR cloudy calculations - self % rttov_opts % rt_ir % user_cld_opt_param = .false. !< Switch to supply cloud optical properties explicitly per channel - self % rttov_opts % rt_ir % grid_box_avg_cloud = .false. !< Switch to supply grid-box average cloud concentration or cloud - !! concentration in cloudy fraction of each layer - self % rttov_opts % rt_ir % cldstr_threshold = -1.0_kind_real !< Ignore cloud streams with weights lower than this - self % rttov_opts % rt_ir % cldstr_simple = .false. !< Switch for simplified cloud stream option - USE WITH CAUTION - self % rttov_opts % rt_ir % cldstr_low_cloud_top = 750._kind_real !< Upper pressure limit for cldstr_simple option (hPa) - self % rttov_opts % rt_ir % ir_scatt_model = ir_scatt_chou !< IR scattering model to use - self % rttov_opts % rt_ir % vis_scatt_model = vis_scatt_dom !< VIS/NIR scattering model to use - self % rttov_opts % rt_ir % dom_nstreams = 8 !< Number of DOM streams, must be even and not less than 2 - self % rttov_opts % rt_ir % dom_accuracy = 0._kind_real !< Convergence criterion for termination of DOM azimuthal loop - self % rttov_opts % rt_ir % dom_opdep_threshold = 0._kind_real !< DOM ignores levels below this optical depth: - !! 10. is reasonable, not applied if < = 0 - self % rttov_opts % rt_ir % ozone_data = .false. !< Switch to enable input of O3 profile - self % rttov_opts % rt_ir % co2_data = .false. !< Switch to enable input of CO2 profile - self % rttov_opts % rt_ir % n2o_data = .false. !< Switch to enable input of N2O profile - self % rttov_opts % rt_ir % co_data = .false. !< Switch to enable input of CO profile - self % rttov_opts % rt_ir % ch4_data = .false. !< Switch to enable input of CH4 profile - self % rttov_opts % rt_ir % so2_data = .false. !< Switch to enable input of SO2 profile - - self % rttov_opts % rt_ir % pc % addpc = .false. !< Switch to enable PC-RTTOV - self % rttov_opts % rt_ir % pc % ipcbnd = -1 !< PC spectral band - self % rttov_opts % rt_ir % pc % ipcreg = -1 !< PC predictor channel set - self % rttov_opts % rt_ir % pc % npcscores = -1 !< Number of PC scores to compute, if less than 1 npcscores is derived - !! from the size of the pccomp%pcscores array - self % rttov_opts % rt_ir % pc % addradrec = .false. !< Switch for calculation of reconstructed radiances - - !> MW-only radiative transfer options - self % rttov_opts % rt_mw % fastem_version = 6 !< FASTEM version (0-6); 0 => TESSEM2 - self % rttov_opts % rt_mw % supply_foam_fraction = .false. !< Supply a foam fraction to FASTEM - self % rttov_opts % rt_mw % clw_data = .false. !< Switch to enable input of cloud liquid water profile - self % rttov_opts % rt_mw % clw_scheme = 1 !< MW CLW scheme: 1 => Liebe, 2 => Rosenkranz, 3 => TKC - self % rttov_opts % rt_mw % clw_calc_on_coef_lev = .true. !< Apply MW CLW calculations on coef/user levels (true/false resp.) - self % rttov_opts % rt_mw % clw_cloud_top = 322 !< Lower pressure limit for MW CLW calculations (hPa) - self % rttov_opts % rt_mw % apply_band_correction = .true. !< Apply band-correction for Planck radiance and BT calculations - - self % rttov_opts % interpolation % addinterp = .false. !< Switch to enable RTTOV interpolator - self % rttov_opts % interpolation % interp_mode = interp_rochon !< Interpolation mode (1-5, see user guide) - self % rttov_opts % interpolation % lgradp = .false. !< Switch to make pressure an active variable in TL/AD/K models - self % rttov_opts % interpolation % spacetop = .true. !< Switch to assume space boundary at top-most input pressure level - self % rttov_opts % interpolation % reg_limit_extrap = .false. !< Switch to extrapolate input profiles using regression limits - - !> HTFRTC options structure - self % rttov_opts % htfrtc_opts % htfrtc = .false. !< Switch to use htfrtc - self % rttov_opts % htfrtc_opts % n_pc_in = -1 !< Number of principal components to be used - self % rttov_opts % htfrtc_opts % reconstruct = .false. !< Switch to select reconstructed radiances - self % rttov_opts % htfrtc_opts % simple_cloud = .false. !< Calculate simple cloud - self % rttov_opts % htfrtc_opts % overcast = .false. !< Calculate overcast cloud on all levels - - case ('OPS','PS45') - self % rttov_opts % config % apply_reg_limits = .true. ! set as true for all instruments - self % rttov_opts % config % fix_hgpl = .false. ! true @ PS44 - self % rttov_opts % config % verbose = .false. ! true if (ProcessMode > VerboseMode .OR. RTTOV_Verbosity > 0) - self % rttov_opts % config % do_checkinput = .false. ! we will use the more thorough and verbose - ! rttov_user_profile_checkinput instead to save ourselves an - ! RTTOV call where the profile is no good - - self % rttov_opts % rt_all % rad_down_lin_tau = .true. !FALSE @PS44 - self % rttov_opts % rt_all % dtau_test = .true. !FALSE @PS44 - - if(trim(default_opts_set) == 'OPS') then - self % rttov_opts % rt_all % addrefrac = .false. ! At PS44 .TRUE. @ PS45? - else - self % rttov_opts % rt_all % addrefrac = .true. ! At PS44 .TRUE. @ PS45? - endif - - self % rttov_opts % rt_all % switchrad = .true. - if(trim(default_opts_set) == 'OPS') then - self % rttov_opts % rt_all % use_q2m = .false. ! At PS44 .TRUE. @ PS45? - endif - self % rttov_opts % rt_all % do_lambertian = .false. ! - ! self % rttov_opts % rt_all % plane_parallel = .FALSE. - - self % rttov_opts % interpolation % addinterp = .true. ! Allow interpolation of input profile - self % rttov_opts % interpolation % interp_mode = 4 ! Set interpolation method (4 for all insts at PS44) - ! self % rttov_opts % interpolation % lgradp = .FALSE. - if(trim(default_opts_set) == 'OPS') then - self % rttov_opts % interpolation % spacetop = .false. ! At PS44 .TRUE. @ PS45? - self % rttov_opts % interpolation % reg_limit_extrap = .false. ! At PS44 .TRUE. @ PS45? - else - self % rttov_opts % interpolation % reg_limit_extrap = .true. ! At PS44 .TRUE. @ PS45? - endif - - ! self % rttov_opts % rt_ir % addsolar = .FALSE. - ! self % rttov_opts % rt_ir % do_nlte_correction = .FALSE. - self % rttov_opts % rt_ir % ir_sea_emis_model = 1 ! At PS44 2 @ PS45? + ! Get PS number if it exists + if(default_opts_set(1:4) == 'UKMO') then + PS_configuration = .true. + read(default_opts_set(8:9),*) PS_Number - ! self % rttov_opts % rt_ir % user_aer_opt_param = .FALSE. ! user specifies aerosol scattering optical parameters - ! self % rttov_opts % rt_ir % user_cld_opt_param = .FALSE. ! user specifies cloud scattering optical parameters - ! self % rttov_opts % rt_ir % ir_scatt_model = 2 ! Chou-scaling - self % rttov_opts % rt_ir % grid_box_avg_cloud = .true. ! Assume grid-box average for cloud concentrations - - self % rttov_opts % rt_ir % ozone_data = .true. ! There WILL be an ozone profile - self % rttov_opts % rt_ir % co2_data = .false. ! We do not have profiles - self % rttov_opts % rt_ir % n2o_data = .false. ! for any other constituents - self % rttov_opts % rt_ir % ch4_data = .false. - self % rttov_opts % rt_ir % co_data = .false. - self % rttov_opts % rt_ir % so2_data = .false. + write(message,'(A, i3)') 'Setting RTTOV default options for PS', PS_Number + call fckit_log%info(message) + else + PS_configuration = .false. + PS_Number = -1 + endif - ! RTTOV v12 Discrete ordinates method (DOM) not implemented for now +!Set RTTOV 12.3 defaults explicitly + self % rttov_opts % config % apply_reg_limits = .false. !< Switch to restrict input profiles to coef training limits + self % rttov_opts % config % verbose = .true. !< Switch for verbose output + self % rttov_opts % config % do_checkinput = .true. !< Switch to apply internal profile checking + self % rttov_opts % config % fix_hgpl = .false. !< Switch to apply fix to match 2m p with elevation in geometry calculations + + self % rttov_opts % rt_all % addrefrac = .false. !< Switch to enable atmospheric refraction + self % rttov_opts % rt_all % switchrad = .false. !< Switch for input units in AD/K models + self % rttov_opts % rt_all % use_q2m = .true. !< Switch to enable use of 2m q variable + self % rttov_opts % rt_all % do_lambertian = .false. !< Switch for setting Lambertian reflection (IR and MW) + self % rttov_opts % rt_all % lambertian_fixed_angle = .true. !< Switch for fixed/parameterised effective angle for Lambertian option + self % rttov_opts % rt_all % plane_parallel = .false. !< Switch to ignore atmospheric curvature + self % rttov_opts % rt_all % rad_down_lin_tau = .true. !< Linear-in-tau or layer-mean for downwelling radiances + self % rttov_opts % rt_all % dtau_test = .true. !< Switch to apply dtau test in transmit/integrate calculations + + self % rttov_opts % rt_ir % solar_sea_brdf_model = 1 !< Solar sea BRDF model (1-2) + self % rttov_opts % rt_ir % ir_sea_emis_model = 2 !< IR sea emissivity model (1-2) + self % rttov_opts % rt_ir % addsolar = .false. !< Switch to enable solar simulations + self % rttov_opts % rt_ir % rayleigh_single_scatt = .true. !< Switch to enable Rayleigh single-scattering for VIS/NIR channels + self % rttov_opts % rt_ir % do_nlte_correction = .false. !< Switch to enable NLTE bias correction + self % rttov_opts % rt_ir % addaerosl = .false. !< Switch to enable IR aerosol calculations + self % rttov_opts % rt_ir % user_aer_opt_param = .false. !< Switch to supply aerosol optical properties explicitly per channel + self % rttov_opts % rt_ir % addclouds = .false. !< Switch to enable IR cloudy calculations + self % rttov_opts % rt_ir % user_cld_opt_param = .false. !< Switch to supply cloud optical properties explicitly per channel + self % rttov_opts % rt_ir % grid_box_avg_cloud = .false. !< Switch to supply grid-box average cloud concentration or cloud + + self % rttov_opts % rt_ir % cldstr_threshold = -1.0_kind_real !< Ignore cloud streams with weights lower than this + self % rttov_opts % rt_ir % cldstr_simple = .false. !< Switch for simplified cloud stream option - USE WITH CAUTION + self % rttov_opts % rt_ir % cldstr_low_cloud_top = 750._kind_real !< Upper pressure limit for cldstr_simple option (hPa) + self % rttov_opts % rt_ir % ir_scatt_model = ir_scatt_chou !< IR scattering model to use + self % rttov_opts % rt_ir % vis_scatt_model = vis_scatt_dom !< VIS/NIR scattering model to use + self % rttov_opts % rt_ir % dom_nstreams = 8 !< Number of DOM streams, must be even and not less than 2 + self % rttov_opts % rt_ir % dom_accuracy = 0._kind_real !< Convergence criterion for termination of DOM azimuthal loop + self % rttov_opts % rt_ir % dom_opdep_threshold = 0._kind_real !< DOM ignores levels below this optical depth: + + self % rttov_opts % rt_ir % ozone_data = .false. !< Switch to enable input of O3 profile + self % rttov_opts % rt_ir % co2_data = .false. !< Switch to enable input of CO2 profile + self % rttov_opts % rt_ir % n2o_data = .false. !< Switch to enable input of N2O profile + self % rttov_opts % rt_ir % co_data = .false. !< Switch to enable input of CO profile + self % rttov_opts % rt_ir % ch4_data = .false. !< Switch to enable input of CH4 profile + self % rttov_opts % rt_ir % so2_data = .false. !< Switch to enable input of SO2 profile + + self % rttov_opts % rt_ir % pc % addpc = .false. !< Switch to enable PC-RTTOV + self % rttov_opts % rt_ir % pc % ipcbnd = -1 !< PC spectral band + self % rttov_opts % rt_ir % pc % ipcreg = -1 !< PC predictor channel set + self % rttov_opts % rt_ir % pc % npcscores = -1 !< Number of PC scores to compute + self % rttov_opts % rt_ir % pc % addradrec = .false. !< Switch for calculation of reconstructed radiances + + !> MW-only radiative transfer options + self % rttov_opts % rt_mw % fastem_version = 6 !< FASTEM version (0-6); 0 => TESSEM2 + self % rttov_opts % rt_mw % supply_foam_fraction = .false. !< Supply a foam fraction to FASTEM + self % rttov_opts % rt_mw % clw_data = .false. !< Switch to enable input of cloud liquid water profile + self % rttov_opts % rt_mw % clw_scheme = 1 !< MW CLW scheme: 1 => Liebe, 2 => Rosenkranz, 3 => TKC + self % rttov_opts % rt_mw % clw_calc_on_coef_lev = .true. !< Apply MW CLW calculations on coef/user levels (true/false resp.) + self % rttov_opts % rt_mw % clw_cloud_top = 322 !< Lower pressure limit for MW CLW calculations (hPa) + self % rttov_opts % rt_mw % apply_band_correction = .true. !< Apply band-correction for Planck radiance and BT calculations + + self % rttov_opts % interpolation % addinterp = .false. !< Switch to enable RTTOV interpolator + self % rttov_opts % interpolation % interp_mode = interp_rochon !< Interpolation mode 1 (valid options 1-5, see user guide) + self % rttov_opts % interpolation % lgradp = .false. !< Switch to make pressure an active variable in TL/AD/K models + self % rttov_opts % interpolation % spacetop = .true. !< Switch to assume space boundary at top-most input pressure level + self % rttov_opts % interpolation % reg_limit_extrap = .false. !< Switch to extrapolate input profiles using regression limits + + !> HTFRTC options structure + self % rttov_opts % htfrtc_opts % htfrtc = .false. !< Switch to use htfrtc + self % rttov_opts % htfrtc_opts % n_pc_in = -1 !< Number of principal components to be used + self % rttov_opts % htfrtc_opts % reconstruct = .false. !< Switch to select reconstructed radiances + self % rttov_opts % htfrtc_opts % simple_cloud = .false. !< Calculate simple cloud + self % rttov_opts % htfrtc_opts % overcast = .false. !< Calculate overcast cloud on all levels + + if (PS_configuration) then - self % rttov_opts % rt_mw % clw_calc_on_coef_lev = .true. ! .FALSE. @PS44 - self % rttov_opts % rt_mw % clw_data = .true. ! Set to true for allocation - if(trim(default_opts_set) == 'OPS') then - self % rttov_opts % rt_mw % fastem_version = 2 ! at PS44 6 at PS45? + ! Set RTTOV options that different from default and are true for all MetO configurations up to PS45 + if (cmp_strings(default_opts_set(1:4), 'UKMO')) then + self % rttov_opts % config % verbose = .false. ! true if (ProcessMode > VerboseMode .OR. RTTOV_Verbosity > 0) + self % rttov_opts % config % do_checkinput = .false. ! we will use the more thorough and verbose user_checkinput + + self % rttov_opts % rt_all % switchrad = .true. + self % rttov_opts % rt_all % use_q2m = .false. + + self % rttov_opts % rt_ir % grid_box_avg_cloud = .true. ! Assume grid-box average for cloud concentrations + self % rttov_opts % rt_ir % ozone_data = .true. ! Set to true for allocation purposes + + self % rttov_opts % rt_mw % clw_data = .true. ! Set to true for allocation purposes + + self % rttov_opts % interpolation % addinterp = .true. ! Allow interpolation of input profile + self % rttov_opts % interpolation % interp_mode = 4 ! Set interpolation method (4 for all insts at PS44) + endif + + !RTTOV options that are different from RTTOV defaults at and before PS44 + if (PS_Number <= 44) then + self % rttov_opts % rt_ir % ir_sea_emis_model = 1 ! Use SSIREM + + self % rttov_opts % rt_mw % fastem_version = 2 ! no support for Fastem-bug so use caution + + self % rttov_opts % interpolation % spacetop = .false. + endif + + !RTTOV options that are different from RTTOV and PS43 defaults at PS44 + if (PS_Number >= 44) then + self % rttov_opts % config % apply_reg_limits = .true. + self % rttov_opts % config % fix_hgpl = .true. ! This is an RTTOV 13 default + + self % rttov_opts % rt_all % dtau_test = .false. ! This is an RTTOV 13 default + self % rttov_opts % rt_all % rad_down_lin_tau = .false. ! This is the recommended setting + + self % rttov_opts % rt_mw % clw_calc_on_coef_lev = .false. ! This is an RTTOV 13 default + endif + + !RTTOV options that are different from RTTOV and PS44 defaults at PS45 + if (PS_Number == 45) then + self % rttov_opts % rt_all % addrefrac = .true. ! This is an RTTOV 13 default + + self % rttov_opts % rt_mw % clw_scheme = 2 ! This is an RTTOV 13 default + + self % rttov_opts % interpolation % reg_limit_extrap = .true. ! This is an RTTOV 13 default endif - !self % rttov_opts % rt_mw % supply_foam_fraction = RTTOV_Supply_Foam_Fraction - - ! IF (ANY (MwscattSwitch)) THEN - ! RTTOV12_opts_scatt % config = self % rttov_opts % config - ! RTTOV12_opts_scatt % use_q2m = self % rttov_opts % rt_all % use_q2m - ! RTTOV12_opts_scatt % fastem_version = self % rttov_opts % rt_mw % fastem_version - ! RTTOV12_opts_scatt % supply_foam_fraction = self % rttov_opts % rt_mw % supply_foam_fraction - ! RTTOV12_opts_scatt % interp_mode = self % rttov_opts % interpolation % interp_mode - ! RTTOV12_opts_scatt % reg_limit_extrap = self % rttov_opts % interpolation % reg_limit_extrap - ! RTTOV12_opts_scatt % rttov9_fastem_bug = self % rttov_opts % compatibility % rttov9_fastem_bug - ! END IF - - case ('test') - self % rttov_opts % rt_ir % addsolar = .false. ! Do not include solar radiation - self % rttov_opts % interpolation % addinterp = .true. ! Allow interpolation of input profile - self % rttov_opts % interpolation % interp_mode = 4 ! Set interpolation method - self % rttov_opts % interpolation % reg_limit_extrap = .true. ! reg_limit_extrap - self % rttov_opts % rt_all % addrefrac = .true. ! Include refraction in path calc - self % rttov_opts % rt_all % switchrad = .true. ! Include refraction in path calc - self % rttov_opts % rt_all % dtau_test = .false. - self % rttov_opts % rt_ir % addclouds = .false. ! Don't include cloud effects - self % rttov_opts % rt_ir % addaerosl = .false. ! Don't include aerosol effects - - self % rttov_opts % rt_ir % ozone_data = .false. ! Set the relevant flag to .TRUE. - self % rttov_opts % rt_ir % co2_data = .false. ! when supplying a profile of the - self % rttov_opts % rt_ir % n2o_data = .false. ! given trace gas (ensure the - self % rttov_opts % rt_ir % ch4_data = .false. ! coef file supports the gas) - self % rttov_opts % rt_ir % co_data = .false. ! - self % rttov_opts % rt_ir % so2_data = .false. ! - self % rttov_opts % rt_mw % clw_data = .false. ! - - self % rttov_opts % config % verbose = .true. ! Enable printing of warnings - self % rttov_opts % config % apply_reg_limits = .true. - self % rttov_opts % config % do_checkinput = .false. - self % rttov_opts % config % fix_hgpl = .false. - case('default') - ! RTTOV defaults - end select + endif end subroutine set_defaults_rttov - subroutine populate_hofxdiags(RTProf, chanprof, hofxdiags) + subroutine populate_hofxdiags(RTProf, chanprof, conf, hofxdiags) + use ufo_constants_mod, only : g_to_kg + type(ufo_rttov_io), intent(in) :: RTProf type(rttov_chanprof), intent(in) :: chanprof(:) + type(rttov_conf), intent(in) :: conf type(ufo_geovals), intent(inout) :: hofxdiags !non-h(x) diagnostics - integer :: jvar, chan, prof, ichan + integer :: jvar, chan, prof, ichan integer :: nlayers, nchanprof, nlevels, nprofiles real(kind_real), allocatable :: od_level(:), wfunc(:) @@ -1570,18 +1769,19 @@ subroutine populate_hofxdiags(RTProf, chanprof, hofxdiags) ! Diagnostics used for QC and bias correction !============================================ - if (trim(xstr_diags(jvar)) == "") then + if (cmp_strings(xstr_diags(jvar), "")) then ! forward h(x) diags select case(trim(ystr_diags(jvar))) ! variable: optical_thickness_of_atmosphere_layer_CH - ! variable: transmittances_of_atmosphere_layer_CH + ! variable: transmittances_of_atmosphere_layer_CH ! variable: weightingfunction_of_atmosphere_layer_CH case (var_opt_depth, var_lvl_transmit,var_lvl_weightfunc) nlayers = nlevels - 1 - hofxdiags%geovals(jvar)%nval = nlayers - allocate(hofxdiags%geovals(jvar)%vals(hofxdiags%geovals(jvar)%nval,nprofiles)) + hofxdiags%geovals(jvar)%nval = nlevels + if(.not. allocated(hofxdiags%geovals(jvar)%vals)) & + allocate(hofxdiags%geovals(jvar)%vals(hofxdiags%geovals(jvar)%nval,nprofiles)) hofxdiags%geovals(jvar)%vals = missing ! get channel/profile do ichan = 1, nchanprof @@ -1590,12 +1790,13 @@ subroutine populate_hofxdiags(RTProf, chanprof, hofxdiags) if(chan == ch_diags(jvar)) then ! if profile not skipped - if(ystr_diags(jvar) == var_opt_depth) then + if(cmp_strings(ystr_diags(jvar), var_opt_depth)) then od_level(:) = log(RTProf % transmission%tau_levels(:,chan)) !level->TOA transmittances -> od hofxdiags%geovals(jvar)%vals(:,prof) = od_level(1:nlevels-1) - od_level(2:nlevels) ! defined +ve - else if (ystr_diags(jvar) == var_lvl_transmit) then - hofxdiags%geovals(jvar)%vals(:,prof) = RTProf % transmission % tau_levels(1:nlevels-1,chan) - RTProf % transmission%tau_levels(2:,chan) - else if (ystr_diags(jvar) == var_lvl_weightfunc) then + else if (cmp_strings(ystr_diags(jvar), var_lvl_transmit)) then + hofxdiags%geovals(jvar)%vals(:,prof) = RTProf % transmission % tau_levels(1:nlevels-1,chan) - & + RTProf % transmission%tau_levels(2:,chan) + else if (cmp_strings(ystr_diags(jvar), var_lvl_weightfunc)) then od_level(:) = log(RTProf % transmission%tau_levels(:,chan)) !level->TOA transmittances -> od call rttov_calc_weighting_fn(rttov_errorstatus, RTProf % profiles(prof)%p, od_level(:), & hofxdiags%geovals(jvar)%vals(:,prof)) @@ -1608,10 +1809,12 @@ subroutine populate_hofxdiags(RTProf, chanprof, hofxdiags) ! variable: toa_outgoing_radiance_per_unit_wavenumber_CH [mW / (m^2 sr cm^-1)] (nval=1) ! variable: brightness_temperature_assuming_clear_sky_CH ! variable: pressure_level_at_peak_of_weightingfunction_CH - case (var_radiance, var_tb_clr, var_tb, var_pmaxlev_weightfunc) + ! variable: toa_total_transmittance_CH + case (var_radiance, var_tb_clr, var_tb, var_pmaxlev_weightfunc, var_total_transmit) ! always returned hofxdiags%geovals(jvar)%nval = 1 - allocate(hofxdiags%geovals(jvar)%vals(hofxdiags%geovals(jvar)%nval,nprofiles)) + if(.not. allocated(hofxdiags%geovals(jvar)%vals)) & + allocate(hofxdiags%geovals(jvar)%vals(hofxdiags%geovals(jvar)%nval,nprofiles)) hofxdiags%geovals(jvar)%vals = missing do ichan = 1, nchanprof @@ -1619,35 +1822,45 @@ subroutine populate_hofxdiags(RTProf, chanprof, hofxdiags) prof = chanprof(ichan)%prof if(chan == ch_diags(jvar)) then - if(ystr_diags(jvar) == var_radiance) then + if(cmp_strings(ystr_diags(jvar), var_radiance)) then hofxdiags%geovals(jvar)%vals(1,prof) = RTProf % radiance % total(ichan) - else if(ystr_diags(jvar) == var_tb_clr) then + else if(cmp_strings(ystr_diags(jvar), var_tb_clr)) then hofxdiags%geovals(jvar)%vals(1,prof) = RTProf % radiance % bt_clear(ichan) - else if(ystr_diags(jvar) == var_tb) then + else if(cmp_strings(ystr_diags(jvar), var_tb)) then hofxdiags%geovals(jvar)%vals(1,prof) = RTProf % radiance % bt(ichan) - else if(ystr_diags(jvar) == var_pmaxlev_weightfunc ) then + else if(cmp_strings(ystr_diags(jvar), var_pmaxlev_weightfunc)) then call rttov_calc_weighting_fn(rttov_errorstatus, RTProf % profiles(prof)%p, od_level(:), & Wfunc(:)) hofxdiags%geovals(jvar)%vals(1,prof) = maxloc(Wfunc(:), DIM=1) ! scalar not array(1) + else if(cmp_strings(ystr_diags(jvar), var_total_transmit)) then + hofxdiags%geovals(jvar)%vals(1,prof) = RTProf % transmission % tau_total(ichan) end if endif end do case default + ! not a supported obsdiag but we allocate and initialise here anyway for use later on + hofxdiags%geovals(jvar)%nval = 1 + allocate(hofxdiags%geovals(jvar)%vals(hofxdiags%geovals(jvar)%nval,nprofiles)) + hofxdiags%geovals(jvar)%vals = missing + write(message,*) 'ufo_radiancerttov_simobs: //& - & ObsDiagnostic is unsupported, ', & - & hofxdiags%variables(jvar) + & ObsDiagnostic is unsupported but allocating anyway, ', & + & hofxdiags%variables(jvar), shape(hofxdiags%geovals(jvar)%vals) call fckit_log%info(message) + end select - else if (trim(ystr_diags(jvar)) == var_tb) then + else if (cmp_strings(ystr_diags(jvar), var_tb)) then ! var_tb jacobians select case (trim(xstr_diags(jvar))) - case (var_ts,var_mixr,var_clw) + case (var_ts,var_mixr,var_q,var_clw,var_cli) + nlayers = nlevels - 1 - hofxdiags%geovals(jvar)%nval = nlayers - allocate(hofxdiags%geovals(jvar)%vals(hofxdiags%geovals(jvar)%nval,nprofiles)) + hofxdiags%geovals(jvar)%nval = nlevels + if(.not. allocated(hofxdiags%geovals(jvar)%vals)) & + allocate(hofxdiags%geovals(jvar)%vals(hofxdiags%geovals(jvar)%nval,nprofiles)) hofxdiags%geovals(jvar)%vals = missing do ichan = 1, nchanprof @@ -1655,22 +1868,28 @@ subroutine populate_hofxdiags(RTProf, chanprof, hofxdiags) prof = chanprof(ichan)%prof if(chan == ch_diags(jvar)) then - if(ystr_diags(jvar) == var_ts) then + if(xstr_diags(jvar) == var_ts) then hofxdiags%geovals(jvar)%vals(:,prof) = & RTProf % profiles_k(ichan) % t(:) - else if(ystr_diags(jvar) == var_mixr) then + else if(xstr_diags(jvar) == var_mixr) then + hofxdiags%geovals(jvar)%vals(:,prof) = & + RTProf % profiles_k(ichan) % q(:) * conf%scale_fac(gas_id_watervapour) / g_to_kg + else if(xstr_diags(jvar) == var_q) then hofxdiags%geovals(jvar)%vals(:,prof) = & - RTProf % profiles_k(ichan) % q(:) - else if(ystr_diags(jvar) == var_clw) then + RTProf % profiles_k(ichan) % q(:) * conf%scale_fac(gas_id_watervapour) + else if(xstr_diags(jvar) == var_clw) then hofxdiags%geovals(jvar)%vals(:,prof) = & - RTProf % profiles_k(ichan) % clw(:) + RTProf % profiles_k(ichan) % clw(:) + else if(xstr_diags(jvar) == var_cli) then + ! not in use yet endif endif enddo case (var_sfc_t2m, var_sfc_tskin, var_sfc_emiss, var_sfc_q2m, var_sfc_p2m, var_u, var_v) hofxdiags%geovals(jvar)%nval = 1 - allocate(hofxdiags%geovals(jvar)%vals(hofxdiags%geovals(jvar)%nval,nprofiles)) + if(.not. allocated(hofxdiags%geovals(jvar)%vals)) & + allocate(hofxdiags%geovals(jvar)%vals(hofxdiags%geovals(jvar)%nval,nprofiles)) hofxdiags%geovals(jvar)%vals = missing do ichan = 1, nchanprof @@ -1678,40 +1897,40 @@ subroutine populate_hofxdiags(RTProf, chanprof, hofxdiags) prof = chanprof(ichan)%prof if(chan == ch_diags(jvar)) then - if(ystr_diags(jvar) == var_sfc_tskin) then + if(xstr_diags(jvar) == var_sfc_tskin) then hofxdiags%geovals(jvar)%vals(1,prof) = & RTProf % profiles_k(ichan) % skin % t - else if (ystr_diags(jvar) == var_sfc_t2m) then + else if (xstr_diags(jvar) == var_sfc_t2m) then hofxdiags%geovals(jvar)%vals(1,prof) = & RTProf % profiles_k(ichan) % s2m % t - else if (ystr_diags(jvar) == var_sfc_p2m) then + else if (xstr_diags(jvar) == var_sfc_p2m) then hofxdiags%geovals(jvar)%vals(1,prof) = & RTProf % profiles_k(ichan) % s2m % p - else if (ystr_diags(jvar) == var_sfc_q2m) then + else if (xstr_diags(jvar) == var_sfc_q2m) then hofxdiags%geovals(jvar)%vals(1,prof) = & - RTProf % profiles_k(ichan) % s2m % q - else if (ystr_diags(jvar) == var_u) then + RTProf % profiles_k(ichan) % s2m % q * conf%scale_fac(gas_id_watervapour) + else if (xstr_diags(jvar) == var_u) then hofxdiags%geovals(jvar)%vals(1,prof) = & RTProf % profiles_k(ichan) % s2m % u - else if (ystr_diags(jvar) == var_v) then + else if (xstr_diags(jvar) == var_v) then hofxdiags%geovals(jvar)%vals(1,prof) = & RTProf % profiles_k(ichan) % s2m % v - else if (ystr_diags(jvar) == var_sfc_emiss) then + else if (xstr_diags(jvar) == var_sfc_emiss) then hofxdiags%geovals(jvar)%vals(1,prof) = & - RTProf % emissivity_k(ichan) % emis_in + RTProf % emissivity_k(ichan) % emis_in end if end if end do case default write(message,*) 'ufo_radiancerttov_simobs: //& - & ObsDiagnostic is unsupported, ', & + & Jacobian ObsDiagnostic is unsupported, ', & & hofxdiags%variables(jvar) call fckit_log%info(message) end select else write(message,*) 'ufo_radiancerttov_simobs: //& - & ObsDiagnostic is unsupported, ', & + & ObsDiagnostic is not recognised, ', & & hofxdiags%variables(jvar) call fckit_log%info(message) end if @@ -1730,18 +1949,22 @@ subroutine parse_hofxdiags(hofxdiags, jacobian_needed) character(10), parameter :: jacobianstr = "_jacobian_" integer :: str_pos(4) - character(len=MAXVARLEN) :: varstr + character(len=maxvarlen) :: varstr integer :: jvar character(len=max_string) :: err_msg jacobian_needed = .false. + if (allocated(ystr_diags)) deallocate (ystr_diags) + if (allocated(xstr_diags)) deallocate (xstr_diags) + if (allocated(ch_diags)) deallocate (ch_diags) + if(hofxdiags%nvar > 0) then - allocate (ystr_diags(hofxdiags%nvar), & - xstr_diags(hofxdiags%nvar), & - ch_diags(hofxdiags%nvar)) + if (.not. allocated(ystr_diags)) allocate (ystr_diags(hofxdiags%nvar)) + if (.not. allocated(xstr_diags)) allocate (xstr_diags(hofxdiags%nvar)) + if (.not. allocated(ch_diags)) allocate (ch_diags(hofxdiags%nvar)) - ch_diags = -9999 + ch_diags = -9999 do jvar = 1, hofxdiags%nvar varstr = hofxdiags%variables(jvar) @@ -1766,7 +1989,7 @@ subroutine parse_hofxdiags(hofxdiags, jacobian_needed) xstr_diags(jvar)(str_pos(4)+1:) = "" else !null !Diagnostic is a dependent variable (y) - + xstr_diags(jvar) = "" ystr_diags(jvar)(1:str_pos(3)-1) = varstr(1:str_pos(3)-1) ystr_diags(jvar)(str_pos(3):) = "" @@ -1777,208 +2000,4 @@ subroutine parse_hofxdiags(hofxdiags, jacobian_needed) end subroutine parse_hofxdiags - !------------------------------------------------------------------------------- - ! (C) Crown copyright Met Office. All rights reserved. - ! Refer to COPYRIGHT.txt of this distribution for details. - !------------------------------------------------------------------------------- - ! IF output_type=1 : Split total water content (qtotal) into - ! water vapor content (q) and - ! cloud liquid water content (ql) and - ! cloud ice water content (qi) - ! If output_type ne 1 : Compute derivatives: (q) =dq/dqtotal - ! (ql)=dql/dqtotal - ! (qi)=dqi/dqtotal - ! - ! WARNING: The derivatives are not valid if LtemperatureVar=.TRUE. since - ! qsaturated depends on temperature. - ! - ! The partitioning of the excess moisture between ice and clw uses a temperature - ! based parametrization based on aircraft data Ref: Jones DC Reading phdthesis - ! p126 - !------------------------------------------------------------------------------- - - subroutine Ops_SatRad_Qsplit ( & - output_type, & - p, & - t, & - qtotal, & - q, & - ql, & - qi, & - UseQtSplitRain) - - use ufo_utils_mod, only : Ops_Qsat - use ufo_constants_mod, only : zero, zerodegc, min_q - - implicit none - - ! Subroutine arguments: - integer, intent(in) :: output_type - real(kind_real), intent(in) :: p(:) - real(kind_real), intent(in) :: t(:) - real(kind_real), intent(in) :: qtotal(:) - real(kind_real), intent(out) :: q(size(qtotal)) ! humidity component q - real(kind_real), intent(out) :: ql(size(qtotal)) ! liquid component ql - real(kind_real), intent(out) :: qi(size(qtotal)) ! ice component qi - logical, intent(in) :: UseQtSplitRain - - ! Local declarations: - integer :: i - real(kind_real), parameter :: lower_rh = 0.95 - real(kind_real), parameter :: upper_rh = 1.05 - real(kind_real), parameter :: Split_Factor = 0.5 - real(kind_real), parameter :: MinTempQl = 233.15 ! temperature (K) below which all cloud is ice - real(kind_real) :: qsaturated(size(qtotal)) - real(kind_real) :: RH_qtotal(size(qtotal)) - real(kind_real) :: qnv(size(qtotal)) ! non vapour component - real(kind_real) :: qc(size(qtotal)) ! cloud component - real(kind_real) :: V1(size(qtotal)) - real(kind_real) :: V2(size(qtotal)) - real(kind_real) :: V1zero - real(kind_real) :: V2zero - real(kind_real) :: W(size(qtotal)) - real(kind_real) :: Y1,Y2,Y3,Y4 - real(kind_real) :: IntConst - real(kind_real) :: Aconst - real(kind_real) :: Bconst - real(kind_real) :: Cconst - real(kind_real) :: Dconst - real(kind_real) :: Denom - real(kind_real) :: SmallValue - real(kind_real) :: LF(size(qtotal)) ! fraction of ql to ql+qi - character(len=*), parameter :: RoutineName = "Ops_SatRad_Qsplit" - - real(kind_real) :: QsplitRainParamA !Parameters used to define proportion of - real(kind_real) :: QsplitRainParamB !qt that is partitioned into a rain compnenent - real(kind_real) :: QsplitRainParamC ! - - !Qsplit_MixPhaseParam = 1 !Jones' method as default - QsplitRainParamA = 0.15_kind_real - QsplitRainParamB = 0.09_kind_real - QsplitRainParamC = 50.0_kind_real - - ! Compute saturated water vapor profile for nlevels_q only - - call Ops_Qsat (qsaturated(:), & ! out - t(:), & ! in - p(:), & ! in - size(qtotal)) ! in - - SmallValue = 1.0_kind_real / 8.5_kind_real - Denom = SmallValue * (upper_rh - lower_rh) - - ! don't let rh exceed 2.0 to avoid cosh function blowing up - RH_qtotal(:) = min (qtotal(:) / qsaturated(:), 2.0_kind_real) - - V1(:) = (RH_qtotal(:) - lower_rh) / Denom - V2(:) = (RH_qtotal(:) - upper_rh) / Denom - - Y1 = 1.0_kind_real - Y2 = Split_Factor - Y3 = 0.0_kind_real - Y4 = Split_Factor - - Aconst = (Y2 - Y1) / 2.0_kind_real - Bconst = -(Y4 - Y3) / 2.0_kind_real - Cconst = (Y2 + Y1) / 2.0_kind_real - Dconst = -(Y4 + Y3) / 2.0_kind_real - - ! Compute fraction of ql to ql+qi based on temperature profile - - where (t(:) - ZeroDegC >= -0.01_kind_real) ! -0.01degc and above - - ! all ql - LF(:) = 1.0_kind_real - - end where - - where (t(:) <= MinTempql) - - ! all qi - LF(:) = 0.0_kind_real - - end where - - where (t(:) > MinTempql .and. t(:) - ZeroDegC < -0.01_kind_real) - - ! Jones' parametrization - LF(:) = sqrt (-1.0_kind_real * log (-0.025_kind_real * (t(:) - ZeroDegC)) / 70.0_kind_real) - - end where - - ! finally set LF to 0.0_kind_real for the rttov levels on which clw jacobians are not - ! calculated since nlevels_mwclw < nlevels_q - - V1zero = -1.0_kind_real * lower_rh / Denom - V2zero = -1.0_kind_real * upper_rh / Denom - IntConst = -(Aconst * Denom * log (cosh (V1zero)) + Bconst * Denom * log (cosh (V2zero))) - W(:) = Aconst * Denom * log (cosh (V1(:))) + Bconst * Denom * log (cosh (V2(:))) + & - (Cconst + Dconst) * RH_qtotal(:) + IntConst - - ! store the components of qtotal - ! ensuring that they are above lower limits - - if (UseQtsplitRain) then - - ! Split qtotal into q and qnv (non-vapour part - includes - ! ql, qi, qr) - - ! Split qtotal into q, ql ,qi - - do i = 1, size(qtotal) - - q(i) = max (W(i) * qsaturated(i), min_q) - qnv(i) = max (qtotal(i) - q(i), 0.0_kind_real) - - ! Split qnv into a cloud and precipitation part - - qc(i) = max (QsplitRainParamA * (QsplitRainParamB - (QsplitRainParamB / ((QsplitRainParamC * qnv(i)) + 1))), 0.0_kind_real) - - ! Finally split non-precip part into liquid and ice - - ql(i) = max (LF(i) * qc(i), 0.0_kind_real) - qi(i) = max ((1.0_kind_real - LF(i)) * (qc(i)), 0.0_kind_real) - - end do - - else - do i = 1, size(qtotal) - - q(i) = max (W(i) * qsaturated(i), min_q) - ql(i) = max (LF(i) * (qtotal(i) - q(i)), 0.0_kind_real) - qi(i) = max ((1.0_kind_real - LF(i)) * (qtotal(i) - q(i)), 0.0_kind_real) - - end do - - end if - - ! Values of q, ql and qi are overwritten if output_type /= 1 - ! and replaced with the derivatives - - if (output_type /= 1) then - - ! Compute derivates - ! q = dq/dqtotal, ql = dql/dqtotal, qi=dqi/dqtotal - - q(:) = Aconst * tanh (V1(:)) + Cconst + Bconst * tanh (V2(:)) + Dconst - - if (UseQtsplitRain) then - - ql(:) = LF(:) * QsplitRainParamA * QsplitRainParamB * QsplitRainParamC * (1.0_kind_real - q(:)) / & - ((QsplitRainParamC * qnv(:)) + 1.0_kind_real) ** 2 - qi(:) = (1.0_kind_real - LF(:)) * QsplitRainParamA * QsplitRainParamB * QsplitRainParamC * (1.0_kind_real - q(:)) / & - ((QsplitRainParamC * qnv(:)) + 1.0_kind_real) ** 2 - - else - - ql(:) = LF(:) * (1.0_kind_real - q(:)) - qi(:) = (1.0_kind_real - LF(:)) * (1.0_kind_real - q(:)) - - end if - - end if - - end subroutine Ops_SatRad_Qsplit - - end module ufo_radiancerttov_utils_mod diff --git a/src/ufo/rttovcpp/CMakeLists.txt b/src/ufo/rttovcpp/CMakeLists.txt new file mode 100644 index 000000000..7e9ba4021 --- /dev/null +++ b/src/ufo/rttovcpp/CMakeLists.txt @@ -0,0 +1,21 @@ +# (C) Copyright 2017-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( rttovcpp_files + ObsRadianceRTTOVCPP.h + ObsRadianceRTTOVCPP.cc + ObsRadianceRTTOVCPPTLAD.h + ObsRadianceRTTOVCPPTLAD.cc + rttovcpp_interface.h + rttovcpp_interface.cc +) + +PREPEND( _p_rttovcpp_files "rttovcpp" ${rttovcpp_files} ) + +set ( rttovcpp_src_files + ${_p_rttovcpp_files} + PARENT_SCOPE +) + diff --git a/src/ufo/rttovcpp/ObsRadianceRTTOVCPP.cc b/src/ufo/rttovcpp/ObsRadianceRTTOVCPP.cc new file mode 100644 index 000000000..b6a69247b --- /dev/null +++ b/src/ufo/rttovcpp/ObsRadianceRTTOVCPP.cc @@ -0,0 +1,116 @@ +/* + * (C) Copyright 2017-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include + +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/IntSetParser.h" +#include "oops/util/missingValues.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsBias.h" +#include "ufo/ObsDiagnostics.h" +#include "ufo/rttovcpp/ObsRadianceRTTOVCPP.h" +#include "ufo/rttovcpp/rttovcpp_interface.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static ObsOperatorMaker makerRTTOVCPP_("RTTOVCPP"); + +// ----------------------------------------------------------------------------- + +ObsRadianceRTTOVCPP::ObsRadianceRTTOVCPP(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : ObsOperatorBase(odb, config), odb_(odb), varin_() +{ + // Fields to be requested from getvalues and stored in geovals + // need to be consistent with those defined in ufo_variables_mod.F90 + //----------------------------------------------------------------------------- + const std::vector vv{ + "air_pressure", + "air_temperature", + "specific_humidity", + "surface_pressure", + "surface_temperature", // this is actually var_sfc_t2m + "specific_humidity_at_two_meters_above_surface", + "uwind_at_10m", + "vwind_at_10m", + "skin_temperature", + "seaice_fraction", + "landmask", + "surface_geopotential_height" + }; + + for (size_t jvar = 0; jvar < vv.size(); ++jvar) { + varin_.push_back(vv[jvar]); // set private data member varin_ + } + + // get channels from observations + const oops::Variables & observed = odb.obsvariables(); + channels_ = observed.channels(); // set private data member channels_ + + // get optical depth coef file name from yaml + std::string CoefPath = config.getString("CoefPath"); + std::string SensorID = config.getString("SensorID"); + CoefFileName = CoefPath + "rtcoef_" + SensorID + ".dat"; + oops::Log::info() << CoefFileName << std::endl; + + oops::Log::trace() << "ObsRadianceRTTOVCPP created." << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsRadianceRTTOVCPP::~ObsRadianceRTTOVCPP() { + oops::Log::trace() << "ObsRadianceRTTOVCPP destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsRadianceRTTOVCPP::simulateObs(const GeoVaLs & geovals, ioda::ObsVector & hofx, + ObsDiagnostics &) const { +// + std::vector skip_profile; + ufo::rttovcpp_interface(geovals, odb_, aRttov_, CoefFileName, channels_, + nlevels, skip_profile); + +// ------------------------------------------------------------------------ +// Obtain calculated brightness temperature for all profiles/channels +// ------------------------------------------------------------------------ + std::size_t nprofiles = geovals.nlocs(); + std::size_t nchannels = aRttov_.getNchannels(); + + ASSERT(geovals.nlocs() == hofx.nlocs()); + hofx.zero(); // this may not be necessary + + double missing = util::missingValue(missing); + + std::vector bt; + + for (size_t p = 0; p < nprofiles; p++) { + for (size_t c = 0; c < nchannels; c++) hofx[p*nchannels+c] = missing; + if (skip_profile[p]) continue; + bt = aRttov_.getBtRefl(p); + for (size_t c = 0; c < nchannels; c++) hofx[p*nchannels+c] = bt[c]; + } + + oops::Log::trace() << "ObsRadianceRTTOVCPP::simulateObs done." << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsRadianceRTTOVCPP::print(std::ostream & os) const { + os << "ObsRadianceRTTOVCPP::print not implemented"; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/rttovcpp/ObsRadianceRTTOVCPP.h b/src/ufo/rttovcpp/ObsRadianceRTTOVCPP.h new file mode 100644 index 000000000..66c2b5e2a --- /dev/null +++ b/src/ufo/rttovcpp/ObsRadianceRTTOVCPP.h @@ -0,0 +1,67 @@ +/* + * (C) Copyright 2017-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_RTTOVCPP_OBSRADIANCERTTOVCPP_H_ +#define UFO_RTTOVCPP_OBSRADIANCERTTOVCPP_H_ + +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/ObsOperatorBase.h" + +#include "rttov/wrapper/RttovSafe.h" + +namespace eckit { + class Configuration; + class LocalConfiguration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsDiagnostics; + +// ----------------------------------------------------------------------------- +/// RadianceRTTOVCPP observation operator class +class ObsRadianceRTTOVCPP : public ObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsRadianceRTTOVCPP";} + + ObsRadianceRTTOVCPP(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsRadianceRTTOVCPP(); + +// Obs Operator + void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; + +// Other: declare variable function with return type of oops:Variables + const oops::Variables & requiredVars() const override {return varin_;} + + private: + void print(std::ostream &) const override; + const ioda::ObsSpace& odb_; + oops::Variables varin_; + std::string CoefFileName; + std::vector channels_; + mutable std::size_t nlevels; // need this in order to allocate dx + +// Declare a RttovSafe object for one single sensor + mutable rttov::RttovSafe aRttov_ = rttov::RttovSafe(); +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_RTTOVCPP_OBSRADIANCERTTOVCPP_H_ diff --git a/src/ufo/rttovcpp/ObsRadianceRTTOVCPPTLAD.cc b/src/ufo/rttovcpp/ObsRadianceRTTOVCPPTLAD.cc new file mode 100644 index 000000000..775798cf4 --- /dev/null +++ b/src/ufo/rttovcpp/ObsRadianceRTTOVCPPTLAD.cc @@ -0,0 +1,202 @@ +/* + * (C) Copyright 2017-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include + +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/IntSetParser.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsBias.h" +#include "ufo/ObsDiagnostics.h" +#include "ufo/rttovcpp/ObsRadianceRTTOVCPPTLAD.h" +#include "ufo/rttovcpp/rttovcpp_interface.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static LinearObsOperatorMaker makerRTTOVCPPTL_("RTTOVCPP"); +// ----------------------------------------------------------------------------- + +ObsRadianceRTTOVCPPTLAD::ObsRadianceRTTOVCPPTLAD(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : LinearObsOperatorBase(odb), varin_() +{ + // Increment fields to be requested from getvalues and stored in geovals + const std::vector vv{ + "air_temperature", + "specific_humidity" + }; + + for (size_t jvar = 0; jvar < vv.size(); ++jvar) { + varin_.push_back(vv[jvar]); // set private data member varin_ + } + + // get channels from observations + const oops::Variables & observed = odb.obsvariables(); + channels_ = observed.channels(); // set private data member channels_ + + // get optical depth coef file name from yaml + std::string CoefPath = config.getString("CoefPath"); + std::string SensorID = config.getString("SensorID"); + CoefFileName = CoefPath + "rtcoef_" + SensorID + ".dat"; + + oops::Log::trace() << "ObsRadianceRTTOVCPPTLAD created." << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsRadianceRTTOVCPPTLAD::~ObsRadianceRTTOVCPPTLAD() { + oops::Log::trace() << "ObsRadianceRTTOVCPPTLAD destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsRadianceRTTOVCPPTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, + ObsDiagnostics &) { +// + ufo::rttovcpp_interface(geovals, obsspace(), aRttov_, CoefFileName, channels_, + nlevels, skip_profile); + + oops::Log::trace() << "ObsRadianceRTTOVCPPTLAD::setTrajectory done" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsRadianceRTTOVCPPTLAD::simulateObsTL(const GeoVaLs & dx, ioda::ObsVector & dy) const { +// + std::size_t nprofiles = dy.nlocs(); + std::size_t nchannels = aRttov_.getNchannels(); + + std::vector tmpvar2d(nprofiles, 0.0); // one single level field + std::vector> dT; // [nlevels][nprofiles] + std::vector> dQ; // [nlevels][nprofiles] + + // Retrieve temperature increment in K + for (std::size_t i = 0; i < nlevels; ++i) { + dx.get(tmpvar2d, "air_temperature", i+1); // get one level T + dT.push_back(tmpvar2d); // push one level T into 3D T + } + + // Retrieve specific humidity increment in kg/kg + for (std::size_t i = 0; i < nlevels; ++i) { + dx.get(tmpvar2d, "specific_humidity", i+1); // get one level Q + dQ.push_back(tmpvar2d); // push one level Q into 3D Q + } + +//------------------------------------------- + ASSERT(dx.nlocs() == dy.nlocs()); + ASSERT(nchannels == dy.nvars()); + dy.zero(); + + std::vector var_k(nlevels, 0.0); + + for (size_t p = 0; p < nprofiles; p++) { + if (skip_profile[p]) continue; + for (size_t c = 0; c < nchannels; c++) { + var_k = aRttov_.getTK(p, c); // T Jacobian for a single profile/channel + for (size_t l = 0; l < nlevels; l++) + dy[p*nchannels+c] += var_k[l]*dT[l][p]; + + var_k = aRttov_.getItemK(rttov::Q, p, c); // Q Jacobian + for (size_t l = 0; l < nlevels; l++) + dy[p*nchannels+c] += var_k[l]*dQ[l][p]; + } + } + + oops::Log::trace() << "ObsRadianceRTTOVCPPTLAD::simulateObsTL done" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsRadianceRTTOVCPPTLAD::simulateObsAD(GeoVaLs & dx, const ioda::ObsVector & dy) const { +// + std::size_t nprofiles = dy.nlocs(); + std::size_t nchannels = aRttov_.getNchannels(); + + if ( dx.nlevs("air_temperature") == 0 ) { // if dx is not allocated + dx.allocate(nlevels, varin_); + dx.zero(); + } + + std::vector tmpvar2d(nprofiles, 0.0); // one single level field + std::vector> dT; // [nlevels][nprofiles] + std::vector> dQ; // [nlevels][nprofiles] + + // Retrieve temperature increment in K + for (std::size_t i = 0; i < nlevels; ++i) { + dx.get(tmpvar2d, "air_temperature", i+1); // get one level T + dT.push_back(tmpvar2d); // push one level T into 3D T + } + + // Retrieve specific humidity increment in kg/kg + for (std::size_t i = 0; i < nlevels; ++i) { + dx.get(tmpvar2d, "specific_humidity", i+1); // get one level Q + dQ.push_back(tmpvar2d); // push one level Q into 3D Q + } + +//------------------------------------------- + ASSERT(dx.nlocs() == dy.nlocs()); + + double missing = util::missingValue(missing); + + std::vector var_k(nlevels, 0.0); + + for (size_t p = 0; p < nprofiles; p++) { + if (skip_profile[p]) continue; + for (size_t c = 0; c < nchannels; c++) { + var_k = aRttov_.getTK(p, c); // T Jacobian, nlevels + for (size_t l = 0; l < nlevels; ++l) { + if (dy[p*nchannels+c] != missing) { + dT[l][p] += dy[p*nchannels+c] * var_k[l]; + } + } + + var_k = aRttov_.getItemK(rttov::Q, p, c); // Q Jacobian, nlevels + for (size_t l = 0; l < nlevels; l++) { + if (dy[p*nchannels+c] != missing) { + dQ[l][p] += dy[p*nchannels+c] * var_k[l]; + } + } + } + } + + // Put temperature increment in kg/kg + for (std::size_t l = 0; l < nlevels; ++l) { + for (size_t p = 0; p < nprofiles; p++) { + tmpvar2d[p] = dT[l][p]; + } + dx.put(tmpvar2d, "air_temperature", l+1); // put one level T + } + + // Put specific humidity increment in kg/kg + for (std::size_t l = 0; l < nlevels; ++l) { + for (size_t p = 0; p < nprofiles; p++) { + tmpvar2d[p] = dQ[l][p]; + } + dx.put(tmpvar2d, "specific_humidity", l+1); // put one level Q + } + + oops::Log::trace() << "ObsRadianceRTTOVCPPTLAD::simulateObsAD done" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsRadianceRTTOVCPPTLAD::print(std::ostream & os) const { + os << "ObsRadianceRTTOVCPPTLAD::print not implemented" << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/rttovcpp/ObsRadianceRTTOVCPPTLAD.h b/src/ufo/rttovcpp/ObsRadianceRTTOVCPPTLAD.h new file mode 100644 index 000000000..19cd5bd4d --- /dev/null +++ b/src/ufo/rttovcpp/ObsRadianceRTTOVCPPTLAD.h @@ -0,0 +1,72 @@ +/* + * (C) Copyright 2017-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_RTTOVCPP_OBSRADIANCERTTOVCPPTLAD_H_ +#define UFO_RTTOVCPP_OBSRADIANCERTTOVCPPTLAD_H_ + +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/LinearObsOperatorBase.h" + +#include "rttov/wrapper/RttovSafe.h" + +namespace eckit { + class Configuration; + class LocalConfiguration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsBias; + class ObsDiagnostics; + +// ----------------------------------------------------------------------------- +/// RadianceRTTOV TL/AD observation operator class +class ObsRadianceRTTOVCPPTLAD : public LinearObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsRadianceRTTOVCPPTLAD";} + + ObsRadianceRTTOVCPPTLAD(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsRadianceRTTOVCPPTLAD(); + + // Calculate Jacobian H(x_g) of obs operator + void setTrajectory(const GeoVaLs &, const ObsBias &, ObsDiagnostics &) override; + // Calculate dy = H dx + void simulateObsTL(const GeoVaLs &, ioda::ObsVector &) const override; + // Calculate H^T dy + void simulateObsAD(GeoVaLs &, const ioda::ObsVector &) const override; + +// Other: declare variable function with return type of oops:Variables + const oops::Variables & requiredVars() const override {return varin_;} + + private: + void print(std::ostream &) const override; + oops::Variables varin_; + std::string CoefFileName; + std::vector channels_; + mutable std::size_t nlevels; // need this in order to allocate dx + std::vector skip_profile; + +// Declare a RttovSafe object for one single sensor + mutable rttov::RttovSafe aRttov_ = rttov::RttovSafe(); +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_RTTOVCPP_OBSRADIANCERTTOVCPPTLAD_H_ diff --git a/src/ufo/rttovcpp/rttovcpp_interface.cc b/src/ufo/rttovcpp/rttovcpp_interface.cc new file mode 100644 index 000000000..ab18bb453 --- /dev/null +++ b/src/ufo/rttovcpp/rttovcpp_interface.cc @@ -0,0 +1,296 @@ +/* + * (C) Copyright 2017-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/rttovcpp/rttovcpp_interface.h" + +#include +#include +#include + +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/IntSetParser.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsBias.h" +#include "ufo/ObsDiagnostics.h" + +#include "rttov/wrapper/Profile.h" +#include "rttov/wrapper/RttovSafe.h" + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsDiagnostics; +} + +namespace ufo { + +// ----------------------------------------------------------------------------- +/*! \brief Set one single RttovSafe object for a single sensor +* +* \details **rttovcpp_interface()** loads rttov coef files, set profiles and +* surface emissivity/reflectance, call Jacobian function, and perform some quality +* control. It is called by both simulateObs() and setTrajectory(). +* +* \param[in] geovals reference to the input full model state at observation locations +* \param[in] odb_ reference to the input observational information +* \param[in] CoefFileName rttov coef file to be loaded +* \param[in] channels_ indexes for a subset of channels for a single sensor +* \param[out] nlevels number of model vertical levels +* \param[out] aRttov_ reference to the output rttov object +* \param[out] skip_profile logical to determine if a profile is used or not +* +* \author Zhiquan (Jake) Liu (NCAR/MMM), initial version for clear-sky DA +* +* \date Feb. 2021, initial version +* +*/ +void rttovcpp_interface(const GeoVaLs & geovals, const ioda::ObsSpace & odb_, + rttov::RttovSafe & aRttov_, const std::string CoefFileName, + const std::vector channels_, std::size_t & nlevels, + std::vector & skip_profile) { + // 1. Set options for a RttovSafe instance: + //----------------------------------------------- + // 1.1 general setting for all sensors: clear-sky + + aRttov_.setFileCoef(CoefFileName); + aRttov_.options.setAddInterp(true); // input P differ from coef file levels + aRttov_.options.setVerboseWrapper(true); // more output info + aRttov_.options.setCO2Data(false); + aRttov_.options.setStoreRad(true); + aRttov_.options.setDoCheckinput(false); // turn this off, or many failing profiles? + aRttov_.options.setSwitchrad(true); // use radiance_k%bt(:)=1 input perturbation + + // 1.2 for Microwave Sensors + aRttov_.options.setFastemVersion(6); // emissivity over sea + aRttov_.options.setSupplyFoamFraction(false); + aRttov_.options.setApplyBandCorrection(true); + + // 1.3 Load coef for subset channels of an instrument + //------------------------------------------------- + try { + aRttov_.loadInst(channels_); + } + catch (std::exception& e) { + oops::Log::error() << "Error loading instrument " << e.what() << std::endl; + } + + oops::Log::info() << "ObsRadianceRTTOVCPP channels: " << channels_ << std::endl; + + // 2. Allocate profiles + //--------------------------------------------------------------------------------- + nlevels = geovals.nlevs("air_temperature"); // set private data member + std::size_t nprofiles = odb_.nlocs(); + std::size_t nchannels = aRttov_.getNchannels(); + + std::vector profiles; // RTTOV Profile object + for (std::size_t p = 0; p < nprofiles; p++) { + rttov::Profile aProfile(nlevels); + profiles.push_back(aProfile); + } + + // global scope for it accessable by reflectance* cos(sunzen) + std::vector sunzen(nprofiles, 0.0); + + // 3. Populate the profiles object + //--------------------------------------------------------------------------------- + try { + std::vector tmpvar1d(nlevels, 0.0); // one single vertical profile + std::vector tmpvar2d(nprofiles, 0.0); // one single level field + + // 3.1 Common 3D fields needed + //---------------------------------------------------- + // 3.1.1 Retrieve pressure in hPa + std::vector> tmpvar3d; // [nlevels][nprofiles] + for (std::size_t i = 0; i < nlevels; ++i) { + geovals.get(tmpvar2d, "air_pressure", i+1); // get one level P + tmpvar3d.push_back(tmpvar2d); // push one level P into 3D P + } + for (std::size_t i = 0; i < nprofiles; ++i) { + for (std::size_t k = 0; k < nlevels; ++k) { + // get one vertical profile, rttov level index is from top to bottom + tmpvar1d[k] = tmpvar3d[k][i]*0.01; + } + profiles[i].setP(tmpvar1d); + } + // release memory of tmpvar3d variable + std::vector>().swap(tmpvar3d); + + // 3.1.2 Retrieve temperature in K + std::vector> tmpvar3d_T; // [nlevels][nprofiles] + for (std::size_t i = 0; i < nlevels; ++i) { + geovals.get(tmpvar2d, "air_temperature", i+1); // get one level T + tmpvar3d_T.push_back(tmpvar2d); // push one level T into 3D T + } + for (std::size_t i = 0; i < nprofiles; ++i) { + for (std::size_t k = 0; k < nlevels; ++k) { + tmpvar1d[k] = tmpvar3d_T[k][i]; + } + profiles[i].setT(tmpvar1d); + } + std::vector>().swap(tmpvar3d_T); + + // 3.1.3 Retrieve specific humidity in kg/kg + std::vector> tmpvar3d_Q; // [nlevels][nprofiles] + for (std::size_t i = 0; i < nlevels; ++i) { + geovals.get(tmpvar2d, "specific_humidity", i+1); + tmpvar3d_Q.push_back(tmpvar2d); + } + for (std::size_t i = 0; i < nprofiles; ++i) { + profiles[i].setGasUnits(rttov::kg_per_kg); + for (std::size_t k = 0; k < nlevels; ++k) { + tmpvar1d[k] = tmpvar3d_Q[k][i]; + } + profiles[i].setQ(tmpvar1d); + } + std::vector>().swap(tmpvar3d_Q); + + // 3.2 2D surface fields at obs locations + //------------------------------------------- + std::vector ps(nprofiles, 0.0); + std::vector t2m(nprofiles, 0.0); + std::vector q2m(nprofiles, 0.0); + std::vector u10(nprofiles, 0.0); + std::vector v10(nprofiles, 0.0); + std::vector tskin(nprofiles, 0.0); + std::vector landmask(nprofiles); // 1: land, 0:ocean + std::vector seaice_frac(nprofiles, 0.0); + + // Retrieve surface variables + geovals.get(ps, "surface_pressure"); // in Pa, get one level Ps + geovals.get(t2m, "surface_temperature"); // Kelvin + geovals.get(q2m, "specific_humidity_at_two_meters_above_surface"); // kg/kg + geovals.get(u10, "uwind_at_10m"); + geovals.get(v10, "vwind_at_10m"); + geovals.get(tskin, "skin_temperature"); // Kelvin + geovals.get(landmask, "landmask"); // 1: land, 0:ocean + geovals.get(seaice_frac, "seaice_fraction"); + + // 3.3 Obs metadata + //----------------------------------------------- + std::vector satzen(nprofiles, 0.0); // always needed + std::vector satazi(nprofiles, 0.0); // not always needed + std::vector sunazi(nprofiles, 0.0); // not always needed + std::vector lat(nprofiles, 0.0); + std::vector lon(nprofiles, 0.0); + std::vector elev(nprofiles, 0.0); + std::vector times(nprofiles); + + odb_.get_db("MetaData", "sensor_zenith_angle", satzen); // in degree + odb_.get_db("MetaData", "sensor_azimuth_angle", satazi); // in degree + odb_.get_db("MetaData", "solar_zenith_angle", sunzen); // in degree + odb_.get_db("MetaData", "solar_azimuth_angle", sunazi); // in degree + odb_.get_db("MetaData", "latitude", lat); // -90~90 in degree + odb_.get_db("MetaData", "longitude", lon); // 0~360 in degree + odb_.get_db("MetaData", "height_above_mean_sea_level", elev); // in m + odb_.get_db("MetaData", "datetime", times); + + // 4. Call rttov set functions + //--------------------------------------------------------------------------------- + util::DateTime time1; + int year, month, day, hour, minute, second; + int surftype; + + for (std::size_t i = 0; i < nprofiles; i++) { + profiles[i].setGasUnits(rttov::kg_per_kg); + + // convert mpas landmask/xice to rttov surface type + // may need to make this more generic for different models + if ( landmask[i] == 0 ) surftype=1; // sea + if ( landmask[i] == 1 ) surftype=0; // land + if ( seaice_frac[i] >= 0.5 ) surftype=2; // sea-ice + profiles[i].setSurfGeom(lat[i], lon[i], 0.001*elev[i]); + + time1 = times[i]; + time1.toYYYYMMDDhhmmss(year, month, day, hour, minute, second); + profiles[i].setDateTimes(year, month, day, hour, minute, second); + + // 0:land, 1:sea, 2:sea-ice, (sea, fresh water) temporary + profiles[i].setSurfType(surftype, 0); + + // Ps (hPa), t2m (k), q2m (kg/kg), u10/v10 (m/s), wind fetch + profiles[i].setS2m(ps[i]*0.01, t2m[i], q2m[i], u10[i], v10[i], 100000.); + + // tskin (k), salinity (35), snow_fraction, foam_fraction, fastem_coef_1-5, specularity + // over sea/land + profiles[i].setSkin(tskin[i], 35., 0., 0., 3.0, 5.0, 15.0, 0.1, 0.3, 0.); + if ( surftype == 2 ) // over seaice, newice(no snow) + profiles[i].setSkin(tskin[i], 35., 0., 0., 2.9, 3.4, 27.0, 0.0, 0.0, 0.); + + profiles[i].setAngles(satzen[i], satazi[i], sunzen[i], sunazi[i]); + } + } // end try + catch (std::exception& e) { + oops::Log::error() << "Error defining the profile data " << e.what() << std::endl; + } + + // 4.1 Associate the profiles with each RttovSafe instance: the profiles undergo + // some checks so use a try block to catch any errors that are thrown up + try { + aRttov_.setTheProfiles(profiles); + } + catch (exception& e) { + oops::Log::error() << "Error setting the profiles " << e.what() << std::endl; + } + + // 5. Set the surface emissivity/reflectance arrays + // and associate with the Rttov objects + //-------------------------------------------------- + double surfemisrefl[2][nprofiles][nchannels]; + + aRttov_.setSurfEmisRefl(reinterpret_cast(surfemisrefl)); + +// Surface emissivity/reflectance arrays must be initialised *before every call to RTTOV* +// Negative values will cause RTTOV to supply emissivity/BRDF values (i.e. equivalent to +// calcemis/calcrefl TRUE - see RTTOV user guide) + for (int i = 0; i < nprofiles; i++) { + for (int j = 0; j < 2; j++) { + for (int c = 0; c < nchannels; c++) surfemisrefl[j][i][c] = -1.; + } + } + +// 6. Call the RTTOV K model for one instrument for all profiles: +// no arguments are supplied so all 'loaded' channels are simulated +//---------------------------------------------------------------------- + try { + aRttov_.runK(); + } + catch (std::exception& e) { + oops::Log::error() << "Error running RTTOV K model " << e.what() << std::endl; + } + +// 7. Check if Jacobian has any NaN and set to skip bad profiles +//---------------------------------------------------------------------- + std::vector var_k(nlevels, 0.0); + + for (size_t p = 0; p < nprofiles; p++) skip_profile.push_back(false); + + for (size_t p = 0; p < nprofiles; p++) { + for (size_t c = 0; c < nchannels; c++) { + var_k = aRttov_.getTK(p, c); // T Jacobian for a single profile/channel + for (size_t l = 0; l < nlevels; ++l) if (std::isnan(var_k[l])) {skip_profile[p] = true;} + + var_k = aRttov_.getItemK(rttov::Q, p, c); // Q Jacobian for a single profile/channel + for (size_t l = 0; l < nlevels; ++l) if (std::isnan(var_k[l])) {skip_profile[p] = true;} + } + } + + oops::Log::trace() << "rttovcpp_interface done" << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/rttovcpp/rttovcpp_interface.h b/src/ufo/rttovcpp/rttovcpp_interface.h new file mode 100644 index 000000000..619bb3eec --- /dev/null +++ b/src/ufo/rttovcpp/rttovcpp_interface.h @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2017-2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_RTTOVCPP_RTTOVCPP_INTERFACE_H_ +#define UFO_RTTOVCPP_RTTOVCPP_INTERFACE_H_ + +#include +#include + +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/IntSetParser.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsBias.h" +#include "ufo/ObsDiagnostics.h" + +#include "rttov/wrapper/Profile.h" +#include "rttov/wrapper/RttovSafe.h" + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsDiagnostics; +} + +namespace ufo { + +void rttovcpp_interface(const GeoVaLs &, const ioda::ObsSpace & odb_, + rttov::RttovSafe & aRttov_, const std::string CoefFileName, + const std::vector channels_, std::size_t & nlevels, + std::vector & skip_profile); + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_RTTOVCPP_RTTOVCPP_INTERFACE_H_ diff --git a/src/ufo/sattcwv/CMakeLists.txt b/src/ufo/sattcwv/CMakeLists.txt new file mode 100644 index 000000000..523e86ab4 --- /dev/null +++ b/src/ufo/sattcwv/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# (C) Crown Copyright 2021 Met Office +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( sattcwv_files + SatTCWV.h + SatTCWV.cc + SatTCWV.interface.h + SatTCWV.interface.F90 + ufo_SatTCWV_mod.F90 + SatTCWVTLAD.h + SatTCWVTLAD.cc + SatTCWVTLAD.interface.h + SatTCWVTLAD.interface.F90 + ufo_SatTCWV_tlad_mod.F90 +) +PREPEND( _p_sattcwv_files "sattcwv" ${sattcwv_files} ) + +set ( sattcwv_src_files + ${_p_sattcwv_files} + PARENT_SCOPE +) diff --git a/src/ufo/sattcwv/SatTCWV.cc b/src/ufo/sattcwv/SatTCWV.cc new file mode 100644 index 000000000..b0d4aab9b --- /dev/null +++ b/src/ufo/sattcwv/SatTCWV.cc @@ -0,0 +1,67 @@ +/* + * + * Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/sattcwv/SatTCWV.h" + +#include +#include +#include + +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsDiagnostics.h" + +namespace ufo { + +// ---------------------------------------------------------------------------- +static ObsOperatorMaker makerSatTCWV_("SatTCWV"); +// ----------------------------------------------------------------------------- + +SatTCWV::SatTCWV(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : ObsOperatorBase(odb, config), keyOperSatTCWV_(0), odb_(odb), varin_() +{ + const std::vector vv{"air_pressure_levels", "specific_humidity", + "surface_pressure"}; + varin_.reset(new oops::Variables(vv)); + + const eckit::LocalConfiguration obsOptions(config, "obs options"); + const eckit::Configuration *configc = &obsOptions; + ufo_sattcwv_setup_f90(keyOperSatTCWV_, &configc); + + oops::Log::trace() << "SatTCWV created." << std::endl; +} + +// ----------------------------------------------------------------------------- + +SatTCWV::~SatTCWV() { + ufo_sattcwv_delete_f90(keyOperSatTCWV_); + oops::Log::trace() << "SatTCWV destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void SatTCWV::simulateObs(const GeoVaLs & gom, ioda::ObsVector & ovec, + ObsDiagnostics &) const { + ufo_sattcwv_simobs_f90(keyOperSatTCWV_, gom.toFortran(), odb_, + ovec.size(), ovec.toFortran()); +} + +// ----------------------------------------------------------------------------- + +void SatTCWV::print(std::ostream & os) const { + os << "SatTCWV::print not implemented"; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/sattcwv/SatTCWV.h b/src/ufo/sattcwv/SatTCWV.h new file mode 100644 index 000000000..c268f3005 --- /dev/null +++ b/src/ufo/sattcwv/SatTCWV.h @@ -0,0 +1,65 @@ +/* + * + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_SATTCWV_SATTCWV_H_ +#define UFO_SATTCWV_SATTCWV_H_ + +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/ObsOperatorBase.h" +#include "ufo/sattcwv/SatTCWV.interface.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsDiagnostics; + +// ----------------------------------------------------------------------------- + +/// SatTCWV observation operator +class SatTCWV : public ObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::SatTCWV";} + + SatTCWV(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~SatTCWV(); + +// Obs Operator + void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; + +// Other + const oops::Variables & requiredVars() const override {return *varin_;} + + int & toFortran() {return keyOperSatTCWV_;} + const int & toFortran() const {return keyOperSatTCWV_;} + + private: + void print(std::ostream &) const override; + F90hop keyOperSatTCWV_; + const ioda::ObsSpace& odb_; + std::unique_ptr varin_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_SATTCWV_SATTCWV_H_ diff --git a/src/ufo/sattcwv/SatTCWV.interface.F90 b/src/ufo/sattcwv/SatTCWV.interface.F90 new file mode 100644 index 000000000..308fe48c4 --- /dev/null +++ b/src/ufo/sattcwv/SatTCWV.interface.F90 @@ -0,0 +1,84 @@ +! +! (C) Copyright 2021 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to handle SatTCWV operator + +module ufo_sattcwv_mod_c + + use fckit_configuration_module, only: fckit_configuration + use iso_c_binding + + use ufo_sattcwv_mod + implicit none + private + +#define LISTED_TYPE ufo_sattcwv + + !> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + + !> Global registry + type(registry_t) :: ufo_sattcwv_registry + + ! ------------------------------------------------------------------------------ +contains + ! ------------------------------------------------------------------------------ + !> Linked list implementation +#include "oops/util/linkedList_c.f" + +! ------------------------------------------------------------------------------ + +subroutine ufo_sattcwv_setup_c(c_key_self, c_conf) bind(c,name='ufo_sattcwv_setup_f90') +use, intrinsic :: iso_c_binding, only: c_ptr +implicit none +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), intent(in) :: c_conf + +type(ufo_sattcwv), pointer :: self +type(fckit_configuration) :: f_conf + +call ufo_sattcwv_registry%setup(c_key_self, self) +f_conf = fckit_configuration(c_conf) + +call self%setup(f_conf) + +end subroutine ufo_sattcwv_setup_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_sattcwv_delete_c(c_key_self) bind(c,name='ufo_sattcwv_delete_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self + +type(ufo_sattcwv), pointer :: self + +call ufo_sattcwv_registry%delete(c_key_self,self) + +end subroutine ufo_sattcwv_delete_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_sattcwv_simobs_c(c_key_self, c_key_geovals, c_obsspace, c_nobs, c_hofx) & + bind(c,name='ufo_sattcwv_simobs_f90') +use, intrinsic :: iso_c_binding, only: c_ptr +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nobs +real(c_double), intent(inout) :: c_hofx(c_nobs) + +type(ufo_sattcwv), pointer :: self + +character(len=*), parameter :: myname_="ufo_sattcwv_simobs_c" +call ufo_sattcwv_registry%get(c_key_self, self) +call self%opr_simobs(c_key_geovals, c_obsspace, c_hofx) + +end subroutine ufo_sattcwv_simobs_c + +! ------------------------------------------------------------------------------ + +end module ufo_sattcwv_mod_c diff --git a/src/ufo/sattcwv/SatTCWV.interface.h b/src/ufo/sattcwv/SatTCWV.interface.h new file mode 100644 index 000000000..342321a79 --- /dev/null +++ b/src/ufo/sattcwv/SatTCWV.interface.h @@ -0,0 +1,36 @@ +/* + * + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_SATTCWV_SATTCWV_INTERFACE_H_ +#define UFO_SATTCWV_SATTCWV_INTERFACE_H_ + +#include "ioda/ObsSpace.h" +#include "ufo/Fortran.h" + +namespace ufo { + +/// Interface to Fortran UFO routines +/*! + * The core of the UFO is coded in Fortran. + * Here we define the interfaces to the Fortran code. + */ + +extern "C" { +// ----------------------------------------------------------------------------- +// SatTCWV Observation Operator - (Met Office) +// ----------------------------------------------------------------------------- + void ufo_sattcwv_setup_f90(F90hop &, const eckit::Configuration * const *); + void ufo_sattcwv_delete_f90(F90hop &); + void ufo_sattcwv_simobs_f90(const F90hop &, const F90goms &, const ioda::ObsSpace &, + const int &, double &); +// ----------------------------------------------------------------------------- + +} // extern C + +} // namespace ufo +#endif // UFO_SATTCWV_SATTCWV_INTERFACE_H_ diff --git a/src/ufo/sattcwv/SatTCWVTLAD.cc b/src/ufo/sattcwv/SatTCWVTLAD.cc new file mode 100644 index 000000000..9a4d0489b --- /dev/null +++ b/src/ufo/sattcwv/SatTCWVTLAD.cc @@ -0,0 +1,83 @@ +/* + * (C) Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/sattcwv/SatTCWVTLAD.h" + +#include +#include +#include + +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" + +#include "oops/base/Variables.h" +#include "oops/util/Logger.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsDiagnostics.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static LinearObsOperatorMaker makerSatTCWVTL_("SatTCWV"); +// ----------------------------------------------------------------------------- + +SatTCWVTLAD::SatTCWVTLAD(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : LinearObsOperatorBase(odb), keyOperSatTCWV_(0), varin_() +{ + const std::vector vv{"air_pressure_levels", "specific_humidity", + "surface_pressure"}; + varin_.reset(new oops::Variables(vv)); + + const eckit::LocalConfiguration obsOptions(config, "obs options"); + const eckit::Configuration * configc = &obsOptions; + ufo_sattcwv_tlad_setup_f90(keyOperSatTCWV_, &configc); + + oops::Log::trace() << "SatTCWVTLAD created" << std::endl; +} + +// ----------------------------------------------------------------------------- + +SatTCWVTLAD::~SatTCWVTLAD() { + ufo_sattcwv_tlad_delete_f90(keyOperSatTCWV_); + oops::Log::trace() << "SatTCWVTLAD destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void SatTCWVTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, + ObsDiagnostics &) { + ufo_sattcwv_tlad_settraj_f90(keyOperSatTCWV_, geovals.toFortran(), + obsspace()); +} + +// ----------------------------------------------------------------------------- + +void SatTCWVTLAD::simulateObsTL( + const GeoVaLs & geovals, ioda::ObsVector & ovec) const { + ufo_sattcwv_simobs_tl_f90(keyOperSatTCWV_, geovals.toFortran(), + obsspace(), ovec.size(), ovec.toFortran()); +} + +// ----------------------------------------------------------------------------- + +void SatTCWVTLAD::simulateObsAD( + GeoVaLs & geovals, const ioda::ObsVector & ovec) const { + ufo_sattcwv_simobs_ad_f90(keyOperSatTCWV_, geovals.toFortran(), + obsspace(), ovec.size(), ovec.toFortran()); +} + +// ----------------------------------------------------------------------------- + +void SatTCWVTLAD::print(std::ostream & os) const { + os << "SatTCWVTLAD::print not implemented" << std::endl; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/sattcwv/SatTCWVTLAD.h b/src/ufo/sattcwv/SatTCWVTLAD.h new file mode 100644 index 000000000..c07e22766 --- /dev/null +++ b/src/ufo/sattcwv/SatTCWVTLAD.h @@ -0,0 +1,65 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_SATTCWV_SATTCWVTLAD_H_ +#define UFO_SATTCWV_SATTCWVTLAD_H_ + +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/LinearObsOperatorBase.h" +#include "ufo/sattcwv/SatTCWVTLAD.interface.h" + +// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsBias; + class ObsDiagnostics; + +// ----------------------------------------------------------------------------- +/// Precipitable water observation operator +class SatTCWVTLAD : public LinearObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::SatTCWVTLAD";} + + SatTCWVTLAD(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~SatTCWVTLAD(); + + // Obs Operators + void setTrajectory(const GeoVaLs &, const ObsBias &, ObsDiagnostics &) override; + void simulateObsTL(const GeoVaLs &, ioda::ObsVector &) const override; + void simulateObsAD(GeoVaLs &, const ioda::ObsVector &) const override; + + // Other + const oops::Variables & requiredVars() const override {return *varin_;} + + int & toFortran() {return keyOperSatTCWV_;} + const int & toFortran() const {return keyOperSatTCWV_;} + + private: + void print(std::ostream &) const override; + F90hop keyOperSatTCWV_; + std::unique_ptr varin_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo +#endif // UFO_SATTCWV_SATTCWVTLAD_H_ diff --git a/src/ufo/sattcwv/SatTCWVTLAD.interface.F90 b/src/ufo/sattcwv/SatTCWVTLAD.interface.F90 new file mode 100644 index 000000000..f6132458c --- /dev/null +++ b/src/ufo/sattcwv/SatTCWVTLAD.interface.F90 @@ -0,0 +1,120 @@ +! (C) Copyright 2021 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module to handle precipitable water observations + +module ufo_sattcwv_tlad_mod_c + + use fckit_configuration_module, only: fckit_configuration + use ufo_sattcwv_tlad_mod + implicit none + private + +#define LISTED_TYPE ufo_sattcwv_tlad + + !> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + + !> Global registry + type(registry_t) :: ufo_sattcwv_tlad_registry + + ! ------------------------------------------------------------------------------ +contains + ! ------------------------------------------------------------------------------ + !> Linked list implementation +#include "oops/util/linkedList_c.f" + +! ------------------------------------------------------------------------------ + +subroutine ufo_sattcwv_tlad_setup_c(c_key_self, c_conf) bind(c,name='ufo_sattcwv_tlad_setup_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), intent(in) :: c_conf + +type(ufo_sattcwv_tlad), pointer :: self +type(fckit_configuration) :: f_conf + +call ufo_sattcwv_tlad_registry%setup(c_key_self, self) +f_conf = fckit_configuration(c_conf) + +call self%setup(f_conf) + +end subroutine ufo_sattcwv_tlad_setup_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_sattcwv_tlad_delete_c(c_key_self) bind(c,name='ufo_sattcwv_tlad_delete_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self + +type(ufo_sattcwv_tlad), pointer :: self + +call ufo_sattcwv_tlad_registry%get(c_key_self, self) +call self%opr_delete() +call ufo_sattcwv_tlad_registry%remove(c_key_self) + +end subroutine ufo_sattcwv_tlad_delete_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_sattcwv_tlad_settraj_c(c_key_self, c_key_geovals, c_obsspace) bind(c,name='ufo_sattcwv_tlad_settraj_f90') + +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace + +type(ufo_sattcwv_tlad), pointer :: self + +character(len=*), parameter :: myname_="ufo_sattcwv_tlad_settraj_c" + +call ufo_sattcwv_tlad_registry%get(c_key_self, self) +call self%opr_settraj(c_key_geovals, c_obsspace) + +end subroutine ufo_sattcwv_tlad_settraj_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_sattcwv_simobs_tl_c(c_key_self, c_key_geovals, c_obsspace, c_nobs, c_hofx) bind(c,name='ufo_sattcwv_simobs_tl_f90') + +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nobs +real(c_double), intent(inout) :: c_hofx(c_nobs) + +type(ufo_sattcwv_tlad), pointer :: self + +character(len=*), parameter :: myname_="ufo_sattcwv_simobs_tl_c" + +call ufo_sattcwv_tlad_registry%get(c_key_self, self) +call self%opr_simobs_tl(c_key_geovals, c_obsspace, c_hofx) + +end subroutine ufo_sattcwv_simobs_tl_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_sattcwv_simobs_ad_c(c_key_self, c_key_geovals, c_obsspace, c_nobs, c_hofx) bind(c,name='ufo_sattcwv_simobs_ad_f90') + +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nobs +real(c_double), intent(in) :: c_hofx(c_nobs) + +type(ufo_sattcwv_tlad), pointer :: self + +character(len=*), parameter :: myname_="ufo_sattcwv_simobs_ad_c" + +call ufo_sattcwv_tlad_registry%get(c_key_self, self) +call self%opr_simobs_ad(c_key_geovals, c_obsspace, c_hofx) + +end subroutine ufo_sattcwv_simobs_ad_c + +! ------------------------------------------------------------------------------ + +end module ufo_sattcwv_tlad_mod_c diff --git a/src/ufo/sattcwv/SatTCWVTLAD.interface.h b/src/ufo/sattcwv/SatTCWVTLAD.interface.h new file mode 100644 index 000000000..1c9985b6e --- /dev/null +++ b/src/ufo/sattcwv/SatTCWVTLAD.interface.h @@ -0,0 +1,39 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_SATTCWV_SATTCWVTLAD_INTERFACE_H_ +#define UFO_SATTCWV_SATTCWVTLAD_INTERFACE_H_ + +#include "ioda/ObsSpace.h" +#include "ufo/Fortran.h" + +namespace ufo { + +/// Interface to Fortran UFO routines +/*! + * The core of the UFO is coded in Fortran. + * Here we define the interfaces to the Fortran code. + */ + +extern "C" { +// ----------------------------------------------------------------------------- +// Precipitable water tl/ad observation operators +// ----------------------------------------------------------------------------- + void ufo_sattcwv_tlad_setup_f90(F90hop &, const eckit::Configuration * const *); + void ufo_sattcwv_tlad_delete_f90(F90hop &); + void ufo_sattcwv_tlad_settraj_f90(const F90hop &, const F90goms &, + const ioda::ObsSpace &); + void ufo_sattcwv_simobs_tl_f90( + const F90hop &, const F90goms &, const ioda::ObsSpace &, const int &, double &); + void ufo_sattcwv_simobs_ad_f90( + const F90hop &, const F90goms &, const ioda::ObsSpace &, const int &, const double &); +// ----------------------------------------------------------------------------- + +} // extern C + +} // namespace ufo +#endif // UFO_SATTCWV_SATTCWVTLAD_INTERFACE_H_ diff --git a/src/ufo/sattcwv/ufo_SatTCWV_mod.F90 b/src/ufo/sattcwv/ufo_SatTCWV_mod.F90 new file mode 100644 index 000000000..dbc339a92 --- /dev/null +++ b/src/ufo/sattcwv/ufo_SatTCWV_mod.F90 @@ -0,0 +1,214 @@ +! +! (C) Crown Copyright 2021 Met Office +! +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module for satellite precipitable water observation operator + +module ufo_sattcwv_mod + + use iso_c_binding + use kinds + use oops_variables_mod + use ufo_vars_mod + use ufo_geovals_mod + use ufo_geovals_mod_c, only: ufo_geovals_registry + use ufo_basis_mod, only: ufo_basis + use obsspace_mod + use fckit_log_module, only: fckit_log + +! Generic routines from elsewhere in jedi + use missing_values_mod + use ufo_constants_mod, only: one, zero, half, grav ! Gravitational field strength + + implicit none + public :: ufo_sattcwv + private + + !> Fortran derived type for sattcwv trajectory + type, extends(ufo_basis) :: ufo_sattcwv + contains + procedure :: setup => ufo_sattcwv_setup + procedure :: simobs => ufo_sattcwv_simobs + final :: destructor + end type ufo_sattcwv + +contains + +! ------------------------------------------------------------------------------ +subroutine ufo_sattcwv_setup(self, f_conf) + + use fckit_configuration_module, only: fckit_configuration + implicit none + class(ufo_sattcwv), intent(inout) :: self + type(fckit_configuration), intent(in) :: f_conf + +end subroutine ufo_sattcwv_setup + +! ------------------------------------------------------------------------------ +subroutine destructor(self) + implicit none + type(ufo_sattcwv), intent(inout) :: self +end subroutine destructor +! ------------------------------------------------------------------------------ +! percipitable water observation operator +subroutine ufo_sattcwv_simobs(self, geovals, hofx, obss) + use kinds + use ufo_geovals_mod, only: ufo_geovals, ufo_geoval, ufo_geovals_get_var + use iso_c_binding + use obsspace_mod + + implicit none + + class(ufo_sattcwv), intent(in) :: self ! The object in which this operator is + type(ufo_geovals), intent(in) :: geovals ! Model vals at Obs locs + real(kind_real), intent(inout) :: hofx(:) ! Simulated Obs + type(c_ptr), value, intent(in) :: obss ! The obs and metadata + + ! Local variables + ! + type(ufo_geoval), pointer :: prs ! Model background values of air pressure + type(ufo_geoval), pointer :: psfc ! Model background values of surface pressure + type(ufo_geoval), pointer :: q ! Model background values of specific humidity + logical :: ascend ! Flag on direction of model levels + integer :: iobs ! Counter + integer :: nlocs ! number of observations + integer :: ilev1 ! starting level for loop + integer :: ilev2 ! ending level for loop + integer :: inc ! increment to level loop + integer :: ibot ! index of second lowest level + integer :: isfc ! index of lowest level + integer, parameter :: max_string = 800 + character(max_string) :: err_msg ! Error message for output + character(max_string) :: message ! General message for output + character(len=*), parameter :: myname_ = "ufo_sattcwv_simobs" + + ! check if nlocs is consistent in geovals & hofx + if (geovals%nlocs /= size(hofx)) then + write(err_msg,*) myname_, ' error: nlocs ',geovals%nlocs,' inconsistent with HofX size',size(hofx) + call abor1_ftn(err_msg) + endif + + ! get geovals of atmospheric pressure in (Pa) and q (kg/kg) + ! + call ufo_geovals_get_var(geovals, var_ps, psfc) + call ufo_geovals_get_var(geovals, var_prsi, prs) + call ufo_geovals_get_var(geovals, var_q, q) +! The model data must be on a staggered grid, with nlevp = nlevq+1 + IF (prs % nval /= q % nval + 1) THEN + write(err_msg,*) myname_ // ':' // & + ' Data must be on a staggered grid nlevp, nlevq = ',prs%nval,q%nval + call fckit_log % warning(err_msg) + write(err_msg,*) myname_ // ':' // ' error: number of levels inconsistent!' + call abor1_ftn(err_msg) + END IF +! +! Determine if models levels are ascending or descending and set up indices for level loop +! Note assumes model levels stay same for all obs +! + ascend = .false. + if((prs%vals(1,1)-prs%vals(2,1)) > zero )ascend = .true. + if (ascend)then ! Model level starts above surface + ilev1 = 1 + ilev2 = q%nval + inc = 1 + ibot = 2 + isfc = 1 + else ! Model level starts at top of atmosphere + ilev1 = q%nval + ilev2 = 1 + inc = -1 + ibot = q%nval + isfc = q%nval + endif + + ! get number of observations + nlocs = obsspace_get_nlocs(obss) + ! + obs_loop: do iobs = 1, nlocs + ! + call SatTCWV_ForwardModel(prs%nval, & ! Number of pressure levels + ilev1, & ! Starting pressure layer + ilev2, & ! Ending pressure layer + inc, & ! Layer increment + ibot, & ! Lowest layer inc surface + isfc, & ! Level next to surface + psfc % vals(1,iobs), & ! Surface pressure (Pa) + prs % vals(:,iobs), & ! Pressure levels (Pa) + q % vals(:,iobs), & ! Mean layer specific humidity (kg/kg) + hofx(iobs)) ! Simulated precipitable water (kg/sq.m) +! + end do obs_loop +! + write(err_msg,*) "TRACE: ufo_sattcwv_simobs: completed" + call fckit_log%info(err_msg) +! +end subroutine ufo_sattcwv_simobs +! ------------------------------------------------------------------------------ +SUBROUTINE SatTCWV_ForwardModel(nlevP, & + ilev1, & + ilev2, & + inc, & + ibot, & + isfc, & + psfc, & + prs, & + q, & + Model_SatTCWV) +!------------------------------------------------------------------------------- +!> Compute Total column water vapour amount from geovals water vapour profile. +!! +!! \details Heritage: Var_SatTCWVOperator.f90 +!! +!! \author Met Office +!! +!! \date 05/01/2021: Created +!! +INTEGER, INTENT(IN) :: nlevp ! Number of pressure levels +INTEGER, INTENT(IN) :: ilev1 ! Starting layer +INTEGER, INTENT(IN) :: ilev2 ! Ending layer +INTEGER, INTENT(IN) :: inc ! loop increment +INTEGER, INTENT(IN) :: ibot ! lowest layer including surface +INTEGER, INTENT(IN) :: isfc ! lowest level next to surface +REAL(kind_real), INTENT(IN) :: psfc ! surface pressure (Pa) +REAL(kind_real), INTENT(IN) :: prs(1:nlevP) ! Model background pressure (Pa) at levels +REAL(kind_real), INTENT(IN) :: q(1:nlevP-1) ! Model background specific humidity (kg/kg) +REAL(kind_real), INTENT(INOUT) :: Model_SatTCWV ! Model forecast of the observations (kg/sq.m) +! +! Local parameters +! +integer, parameter :: max_string = 800 ! Length of strings +character(len=*), parameter :: myname_ = "SatTCWV_ForwardModel" +! +! Local variables +! +character(max_string) :: err_msg ! Error message to be output +character(max_string) :: message ! General message for output +REAL(kind_real) :: PDiff ! Pressure diff across layer +REAL(kind_real) :: GK ! 1/gravity +INTEGER :: i ! level counter +!------------------------------------------------------------------------------- +!1. Initialise variables and check model levels +!------------------------------------------------------------------------------- +PDiff = zero +GK = one / grav +Model_SatTCWV = zero +! +!------------------------------------------------------------------------------- +! Calculate model equivalent of satellite total column water vapour. +!------------------------------------------------------------------------------- +! +! Now integrate through atmosphere layers +DO i = ilev1, ilev2, inc + PDiff = prs(i) - prs(i+1) ! prs is pressure on level i +! Include surface layer assuming surface q is same as q 10m but could use q2m in future + IF (i == isfc)PDiff = psfc - prs(ibot) +! + Model_SatTCWV = Model_SatTCWV + GK * q(i) * PDiff ! Accumulate layer water vapour conc +END DO +! +END SUBROUTINE SatTCWV_forwardmodel + +end module ufo_sattcwv_mod diff --git a/src/ufo/sattcwv/ufo_SatTCWV_tlad_mod.F90 b/src/ufo/sattcwv/ufo_SatTCWV_tlad_mod.F90 new file mode 100644 index 000000000..8073b0deb --- /dev/null +++ b/src/ufo/sattcwv/ufo_SatTCWV_tlad_mod.F90 @@ -0,0 +1,381 @@ +!------------------------------------------------------------------------------- +! (C) Crown Copyright 2021 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- +!> Fortran module for precipitable water tangent linear and adjoint + +module ufo_sattcwv_tlad_mod +use iso_c_binding + +use kinds +use oops_variables_mod +use ufo_vars_mod +use ufo_geovals_mod +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_basis_tlad_mod, only: ufo_basis_tlad +use obsspace_mod +use fckit_log_module, only : fckit_log + +! Generic routines from elsewhere in jedi +use missing_values_mod +use ufo_constants_mod, only: one, zero, half, grav ! Gravitational field strength + +integer, parameter :: max_string=800 + +!> Fortran derived type for precipitable water trajectory +type, extends(ufo_basis_tlad) :: ufo_sattcwv_tlad + private + integer :: nlevp, nlevq, nlocs + real(kind_real), allocatable :: K(:,:) + + contains + procedure :: setup => ufo_sattcwv_setup + procedure :: delete => ufo_sattcwv_tlad_delete + procedure :: settraj => ufo_sattcwv_tlad_settraj + procedure :: simobs_tl => ufo_sattcwv_simobs_tl + procedure :: simobs_ad => ufo_sattcwv_simobs_ad +end type ufo_sattcwv_tlad + +contains + +! ------------------------------------------------------------------------------ +! Get the optional settings for the forward model, and save them in the object +! so that they can be used in the code. +! ------------------------------------------------------------------------------ +subroutine ufo_sattcwv_setup(self, f_conf) + +use fckit_configuration_module, only: fckit_configuration +implicit none +class(ufo_sattcwv_tlad), intent(inout) :: self +type(fckit_configuration), intent(in) :: f_conf + +end subroutine ufo_sattcwv_setup + +!------------------------------------------------------------------------- +! Tidy up the variables that are used for passing information +!------------------------------------------------------------------------- +subroutine ufo_sattcwv_tlad_delete(self) + + implicit none + class(ufo_sattcwv_tlad), intent(inout) :: self + character(len=*), parameter :: myname_="ufo_sattcwv_tlad_delete" + + self%nlocs = 0 + self%nlevP = 0 + self%nlevq = 0 + if (allocated(self%K)) deallocate(self%K) + self%ltraj = .false. + +end subroutine ufo_sattcwv_tlad_delete + +! ------------------------------------------------------------------------------ +! Calculate the K-matrix (Jacobian) for the observation. It is necessary to run +! this routine before calling the TL or AD routines. +! ------------------------------------------------------------------------------ +subroutine ufo_sattcwv_tlad_settraj(self, geovals, obss) +! + implicit none +! Subroutine arguments + class(ufo_sattcwv_tlad), intent(inout) :: self ! The object that we use to save data in + type(ufo_geovals), intent(in) :: geovals ! The input geovals + type(c_ptr), value, intent(in) :: obss ! The input observations + +! Local parameters + character(len=*), parameter :: myname_="ufo_sattcwv_tlad_settraj" + +! Local variables + type(ufo_geoval), pointer :: prs ! The model geovals - atmospheric pressure (Pa) + type(ufo_geoval), pointer :: psfc ! The model geovals - surface pressure (Pa) + type(ufo_geoval), pointer :: q ! Model background values of specific humidity (kg/kg) + real(kind_real), allocatable :: dtcwv_dq(:) ! The deriv of precip water vs layer q conc (kg/sq.m/kg/kg) + logical :: ascend ! Flag on direction of model levels + integer :: iobs ! Counter + integer :: nlocs ! number of observations + integer :: ilev1 ! starting level for loop + integer :: ilev2 ! ending level for loop + integer :: inc ! increment to level loop + integer :: ibot ! index of second lowest level + integer :: isfc ! index of lowest level + integer, parameter :: max_string = 800 + character(max_string) :: message ! General message for output + character(max_string) :: err_msg ! Error Messages to be output to the user + + write(err_msg,*) "TRACE: ufo_sattcwv_tlad_settraj: begin" + call fckit_log%info(err_msg) + +! Make sure that any previous values of geovals don't get carried over + call self%delete() + +! get model state variables from geovals + call ufo_geovals_get_var(geovals, var_prsi, prs) ! pressure (Pa) + call ufo_geovals_get_var(geovals, var_ps, psfc) ! Surface pressure (Pa) + call ufo_geovals_get_var(geovals, var_q, q) ! Specific humidity (kg/kg) + +! Keep copy of dimensions for TL & AD + self % nlevP = prs % nval + self % nlevq = q % nval + self % nlocs = obsspace_get_nlocs(obss) +! +! The model data must be on a staggered grid, with nlevp = nlevq+1 + IF (prs % nval /= q % nval + 1) THEN + write(err_msg,*) myname_ // ':' // & + ' Data must be on a staggered grid nlevp, nlevq = ',prs%nval,q%nval + call fckit_log % warning(err_msg) + write(err_msg,*) myname_ // ':' // ' error: number of levels inconsistent!' + call abor1_ftn(err_msg) + END IF +! +! Determine if models levels are ascending or descending and set up indices for level loop +! Note assumes model levels stay same for all obs +! + ascend = .false. + if((prs%vals(1,1)-prs%vals(2,1)) > zero )ascend = .true. + if (ascend)then ! Model level starts above surface + ilev1 = 1 + ilev2 = q%nval + inc = 1 + ibot = 2 + isfc = 1 + else ! Model level starts at top of atmosphere + ilev1 = q%nval + ilev2 = 1 + inc = -1 + ibot = q%nval + isfc = q%nval + endif +! + ALLOCATE(self % K(1:self%nlocs, 1:self%nlevq)) + ALLOCATE(dtcwv_dq(1:self%nlevq)) +! +! For each observation, calculate the K-matrix + obs_loop: do iobs = 1, self % nlocs + CALL sattcwv_GetK( self % nlevP, & ! Number of pressure levels + ilev1, & ! Starting pressure layer + ilev2, & ! Ending pressure layer + inc, & ! Layer increment + ibot, & ! Lowest layer inc surface + isfc, & ! Lowest level next to surface + psfc % vals(1,iobs), & ! Surface pressure (Pa) + prs % vals(:,iobs), & ! Pressure levels (Pa) + dtcwv_dq) ! deriv of precip water vs layer q concentration +! +! Build K-matrix (Jacobian of precip water with respect to layer q) + self%K(iobs,1:self%nlevq) = dtcwv_dq(1:self%nlevq) + ! + end do obs_loop + +! Note that this routine has been run. + self%ltraj = .true. + deallocate(dtcwv_dq) + +end subroutine ufo_sattcwv_tlad_settraj + +! ------------------------------------------------------------------------------ +! Given an increment to the model state, calculate an increment to the +! observation +! ------------------------------------------------------------------------------ +subroutine ufo_sattcwv_simobs_tl(self, geovals, hofx, obss) +! + implicit none +! +! Subroutine arguments + class(ufo_sattcwv_tlad), intent(in) :: self ! Object which is being used to transfer information + type(ufo_geovals), intent(in) :: geovals ! Model perturbations + real(kind_real), intent(inout) :: hofx(:) ! Increment to the observations + type(c_ptr), value, intent(in) :: obss ! Input - the observations + +! Local parameters + character(len=*), parameter :: myname_="ufo_sattcwv_simobs_tl" + +! Local variables + integer :: iobs ! Loop variable, observation number + integer :: ilev ! loop variable, level number + integer :: nlev ! number of levels + integer :: nlocs ! Number of observations + character(max_string) :: err_msg ! Message to be output + character(max_string) :: message ! General message for output + type(ufo_geoval), pointer :: q_d ! Increment to the specific humidity + real(kind_real), allocatable :: x_d(:) ! Increment to the state vector + + write(err_msg,*) "TRACE: ufo_sattcwv_simobs_tl: begin" + call fckit_log%info(err_msg) + +! Check if trajectory was set + if (.not. self%ltraj) then + write(err_msg,*) myname_, ' trajectory wasnt set!' + call abor1_ftn(err_msg) + endif + +! Check if nlocs is consistent in geovals & hofx + if (geovals%nlocs /= size(hofx)) then + write(err_msg,*) myname_, ' error: nlocs ',& + geovals%nlocs,' inconsistent with HofX size',size(hofx) + call abor1_ftn(err_msg) + endif + +! Get perturbed variables from geovals q (kg/kg) +! + call ufo_geovals_get_var(geovals, var_q, q_d) ! specific humidity + + nlev = self % nlevq ! number of layers + nlocs = self % nlocs ! number of observations + hofx = zero +! Allocate state vector increment + allocate(x_d(1:nlev)) + x_d = zero +! +! Loop through the obs, calculating the increment to the observation hofx +! + obs_loop: do iobs = 1, nlocs + lev_loop: do ilev = 1 , nlev + x_d(ilev) = q_d % vals(ilev,iobs) + hofx(iobs) = hofx(iobs) + (self % K(iobs,ilev) * x_d(ilev) ) + end do lev_loop + end do obs_loop +! + deallocate(x_d) +! + write(err_msg,*) "TRACE: ufo_sattcwv_simobs_tl: complete" + call fckit_log%info(err_msg) +! + return +end subroutine ufo_sattcwv_simobs_tl + +! ------------------------------------------------------------------------------ +! Given an increment to the observation, find the equivalent increment to the +! model state +! ------------------------------------------------------------------------------ +subroutine ufo_sattcwv_simobs_ad(self, geovals, hofx, obss) +! + use typesizes, only: wp => EightByteReal +! + implicit none +! +! Subroutine arguments + class(ufo_sattcwv_tlad), intent(in) :: self ! Object which is being used to transfer information + type(ufo_geovals), intent(inout) :: geovals ! Calculated perturbations to model state + real(kind_real), intent(in) :: hofx(:) ! Increment to the observations + type(c_ptr), value, intent(in) :: obss ! Input - the observations + +! Local parameters + character(len=*), parameter :: myname_="ufo_sattcwv_simobs_ad" + +! Local variables + real(c_double) :: missing ! Missing data values + type(ufo_geoval), pointer :: q_d ! Pointer to the specific humidity perturbations + integer :: iobs ! Loop variable, observation number + integer :: nlocs ! Number of observations + integer :: ilev ! Loop variable, level number + integer :: nlev ! level number + real(kind_real), allocatable :: x_d(:) ! Perturbation to the state vector + character(max_string) :: err_msg ! Message to be output + + write(err_msg,*) "TRACE: ufo_sattcwv_simobs_ad: begin" + call fckit_log%info(err_msg) + +! Check if trajectory was set + if (.not. self%ltraj) then + write(err_msg,*) myname_, ' trajectory wasnt set!' + call abor1_ftn(err_msg) + endif + +! Check if nlocs is consistent in geovals & hofx + if (geovals%nlocs /= size(hofx)) then + write(err_msg,*) myname_, ' error: nlocs inconsistent!' + call abor1_ftn(err_msg) + endif + +! setting up array for perturbed variables in geovals + call ufo_geovals_get_var(geovals, var_q, q_d) ! layer specific humidity +! + nlev = self % nlevq ! Number of layers + nlocs = self % nlocs ! Number of observations + +! Allocate the output for the specific humidity perturbations + if (.not. allocated(q_d%vals)) then + q_d % nlocs = self % nlocs + q_d % nval = self % nlevq + allocate(q_d % vals(q_d % nval, q_d % nlocs)) + q_d % vals = zero + endif +! + missing = missing_value(missing) +! Allocate state vector x_d + allocate(x_d(1:nlev)) +! +! Loop through the obs, calculating the increment to the model state layer q +! + obs_loop: do iobs = 1, nlocs + if (hofx(iobs) /= missing) then + level_loop: do ilev = 1 , nlev + x_d(ilev) = hofx(iobs) * self % K(iobs,ilev) + q_d% vals(ilev,iobs) = x_d(ilev) + end do level_loop + end if + end do obs_loop +! + deallocate(x_d) +! + write(err_msg,*) "TRACE: ufo_sattcwv_simobs_ad: complete" + call fckit_log%info(err_msg) +! + return +end subroutine ufo_sattcwv_simobs_ad + +!------------------------------------------------------------ +! Calculate the partial derivatives dprecipwater/dq +!------------------------------------------------------------ +SUBROUTINE sattcwv_GetK( nlevP, & + ilev1, & + ilev2, & + inc, & + ibot, & + isfc, & + psfc, & + prs, & + dtcwv_dq) +! +IMPLICIT NONE + +INTEGER, INTENT(IN) :: nlevP ! The number of model pressure levels +INTEGER, INTENT(IN) :: ilev1 ! Starting layer +INTEGER, INTENT(IN) :: ilev2 ! Ending layer +INTEGER, INTENT(IN) :: inc ! loop increment +INTEGER, INTENT(IN) :: ibot ! lowest layer including surface +INTEGER, INTENT(IN) :: isfc ! lowest level next to surface +REAL(kind_real), INTENT(IN) :: psfc ! surface pressure (Pa) +REAL(kind_real), INTENT(IN) :: prs(1:nlevP) ! Model background pressure (Pa) at levels +REAL(kind_real), INTENT(INOUT) :: dtcwv_dq(1:nlevP-1)! Partial deriv of HofX for precip water wrt layer water vapour +! +! Local constants +! +character(max_string) :: err_msg ! Error message to be output +character(max_string) :: message ! General message for output +character(len=*), parameter :: myname_ = "ufo_sattcwv_tlad" +REAL(kind_real) :: PDiff ! Pressure diff across layer +REAL(kind_real) :: GK ! 1/gravity +INTEGER :: i ! level counter +!------------------------------------------------------------------------------- +!1. Initialise variables +!------------------------------------------------------------------------------- +PDiff = zero +GK = one / grav +dtcwv_dq(:) = zero +! +!------------------------------------------------------------------------------- +! Calculate derivative dtcwv wrt dq at all levels for K matrix +!------------------------------------------------------------------------------- +! +DO i = ilev1, ilev2, inc + PDiff = prs(i) - prs(i+1) ! prs is pressure on level i +! Include surface layer assuming surface q is same as q 10m but could use q2m in future + IF (i == isfc)PDiff = psfc - prs(ibot) + dtcwv_dq(i) = GK * PDiff ! compute dtcwv/dq on each layer +END DO +! +END SUBROUTINE sattcwv_GetK + +end module ufo_sattcwv_tlad_mod + diff --git a/src/ufo/scatwind/CMakeLists.txt b/src/ufo/scatwind/CMakeLists.txt new file mode 100644 index 000000000..38a8ed25e --- /dev/null +++ b/src/ufo/scatwind/CMakeLists.txt @@ -0,0 +1,9 @@ +########################## +add_subdirectory( NeutralMetOffice ) + +PREPEND( _p_metoffice_files "scatwind/NeutralMetOffice" ${neutralmetoffice_src_files} ) + +set ( scatwind_src_files + ${_p_metoffice_files} + PARENT_SCOPE +) diff --git a/src/ufo/scatwind/NeutralMetOffice/CMakeLists.txt b/src/ufo/scatwind/NeutralMetOffice/CMakeLists.txt new file mode 100644 index 000000000..d85fdf100 --- /dev/null +++ b/src/ufo/scatwind/NeutralMetOffice/CMakeLists.txt @@ -0,0 +1,13 @@ +# (C) British Crown Copyright 2020 Met Office +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( neutralmetoffice_src_files + ObsScatwindNeutralMetOffice.h + ObsScatwindNeutralMetOffice.cc + ObsScatwindNeutralMetOffice.interface.h + ObsScatwindNeutralMetOffice.interface.F90 + ufo_scatwind_neutralmetoffice_mod.F90 +PARENT_SCOPE +) diff --git a/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.cc b/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.cc new file mode 100644 index 000000000..cb7168874 --- /dev/null +++ b/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.cc @@ -0,0 +1,65 @@ +/* + * (C) British Crown Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.h" + +#include + +#include "oops/util/Logger.h" + +#include "ioda/ObsVector.h" + +#include "ufo/GeoVaLs.h" +#include "ufo/ObsDiagnostics.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +static ObsOperatorMaker + makerScatwindNeutralMetOffice_("ScatwindNeutralMetOffice"); +// ----------------------------------------------------------------------------- + +ObsScatwindNeutralMetOffice::ObsScatwindNeutralMetOffice(const ioda::ObsSpace & odb, + const eckit::Configuration & config) + : ObsOperatorBase(odb, config), keyOperScatwindNeutralMetOffice_(0), + odb_(odb), varin_() +{ + ufo_scatwind_neutralmetoffice_setup_f90(keyOperScatwindNeutralMetOffice_, + config, odb.obsvariables(), varin_); + + oops::Log::trace() << "ObsScatwindNeutralMetOffice created." << std::endl; +} + +// ----------------------------------------------------------------------------- + +ObsScatwindNeutralMetOffice::~ObsScatwindNeutralMetOffice() { + ufo_scatwind_neutralmetoffice_delete_f90(keyOperScatwindNeutralMetOffice_); + oops::Log::trace() << "ObsScatwindNeutralMetOffice destructed" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsScatwindNeutralMetOffice::simulateObs(const GeoVaLs & gom, ioda::ObsVector & ovec, + ObsDiagnostics &) const { + oops::Log::trace() << "ObsScatwindNeutralMetOffice::simulateObs entered" << std::endl; + + ufo_scatwind_neutralmetoffice_simobs_f90(keyOperScatwindNeutralMetOffice_, + gom.toFortran(), odb_, + ovec.nvars(), ovec.nlocs(), ovec.toFortran()); + + oops::Log::trace() << "ObsScatwindNeutralMetOffice::simulateObs exit" << std::endl; +} + +// ----------------------------------------------------------------------------- + +void ObsScatwindNeutralMetOffice::print(std::ostream & os) const { + os << "ObsScatwindNeutralMetOffice::print not implemented"; +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.h b/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.h new file mode 100644 index 000000000..a35b3587b --- /dev/null +++ b/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.h @@ -0,0 +1,63 @@ +/* + * (C) British Crown Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_SCATWIND_NEUTRALMETOFFICE_OBSSCATWINDNEUTRALMETOFFICE_H_ +#define UFO_SCATWIND_NEUTRALMETOFFICE_OBSSCATWINDNEUTRALMETOFFICE_H_ + +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "ufo/ObsOperatorBase.h" +#include "ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.interface.h" + +namespace eckit { + class Configuration; +} + +namespace ioda { + class ObsSpace; + class ObsVector; +} + +namespace ufo { + class GeoVaLs; + class ObsDiagnostics; + +// ----------------------------------------------------------------------------- + +/// ScatwindNeutralMetOffice observation operator +class ObsScatwindNeutralMetOffice : public ObsOperatorBase, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::ObsScatwindNeutralMetOffice";} + + ObsScatwindNeutralMetOffice(const ioda::ObsSpace &, const eckit::Configuration &); + virtual ~ObsScatwindNeutralMetOffice(); + +// Obs Operator + void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; + +// Other + const oops::Variables & requiredVars() const override {return varin_;} + + int & toFortran() {return keyOperScatwindNeutralMetOffice_;} + const int & toFortran() const {return keyOperScatwindNeutralMetOffice_;} + + private: + void print(std::ostream &) const override; + F90hop keyOperScatwindNeutralMetOffice_; + const ioda::ObsSpace& odb_; + oops::Variables varin_; +}; + +// ----------------------------------------------------------------------------- + +} // namespace ufo + +#endif // UFO_SCATWIND_NEUTRALMETOFFICE_OBSSCATWINDNEUTRALMETOFFICE_H_ diff --git a/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.interface.F90 b/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.interface.F90 new file mode 100644 index 000000000..5dfe96313 --- /dev/null +++ b/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.interface.F90 @@ -0,0 +1,97 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +!> Fortran module to handle scatwind observations - Met Office neutral operator + +module ufo_scatwind_neutralmetoffice_mod_c + + use fckit_configuration_module, only: fckit_configuration + use iso_c_binding + use ufo_scatwind_neutralmetoffice_mod + use ufo_geovals_mod, only: ufo_geovals + use ufo_geovals_mod_c, only: ufo_geovals_registry + + implicit none + private + +#define LISTED_TYPE ufo_scatwind_neutralmetoffice + + !> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + + !> Global registry + type(registry_t) :: ufo_scatwind_neutralmetoffice_registry + + ! ------------------------------------------------------------------------------ +contains + ! ------------------------------------------------------------------------------ + !> Linked list implementation +#include "oops/util/linkedList_c.f" + +! ------------------------------------------------------------------------------ + +subroutine ufo_scatwind_neutralmetoffice_setup_c(c_key_self, c_conf, c_obsvars, & + c_geovars) bind(c,name='ufo_scatwind_neutralmetoffice_setup_f90') +use oops_variables_mod +implicit none +integer(c_int), intent(inout) :: c_key_self +type(c_ptr), intent(in), value :: c_conf +type(c_ptr), intent(in), value :: c_obsvars !< variables to be simulated +type(c_ptr), intent(in), value :: c_geovars !< variables requested from the model + +type(ufo_scatwind_NeutralMetOffice), pointer :: self +type(fckit_configuration) :: f_conf + +call ufo_scatwind_neutralmetoffice_registry%setup(c_key_self, self) +f_conf = fckit_configuration(c_conf) + +self%obsvars = oops_variables(c_obsvars) +self%geovars = oops_variables(c_geovars) + +call self%setup(f_conf) + +end subroutine ufo_scatwind_neutralmetoffice_setup_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_scatwind_neutralmetoffice_delete_c(c_key_self) bind(c,name='ufo_scatwind_neutralmetoffice_delete_f90') +implicit none +integer(c_int), intent(inout) :: c_key_self + +type(ufo_scatwind_NeutralMetOffice), pointer :: self + +call ufo_scatwind_NeutralMetOffice_registry%delete(c_key_self,self) + +end subroutine ufo_scatwind_neutralmetoffice_delete_c + +! ------------------------------------------------------------------------------ + +subroutine ufo_scatwind_neutralmetoffice_simobs_c(c_key_self, c_key_geovals, & + c_obsspace, c_nvars, c_nlocs, & + c_hofx) bind(c,name='ufo_scatwind_neutralmetoffice_simobs_f90') + +implicit none +integer(c_int), intent(in) :: c_key_self +integer(c_int), intent(in) :: c_key_geovals +type(c_ptr), value, intent(in) :: c_obsspace +integer(c_int), intent(in) :: c_nvars, c_nlocs +real(c_double), intent(inout) :: c_hofx(c_nvars, c_nlocs) + +type(ufo_scatwind_NeutralMetOffice), pointer :: self +type(ufo_geovals), pointer :: geovals +character(len=*), parameter :: myname_="ufo_scatwind_neutralmetoffice_simobs_c" + +call ufo_scatwind_NeutralMetOffice_registry%get(c_key_self, self) +call ufo_geovals_registry%get(c_key_geovals, geovals) + +call self%simobs(geovals, c_obsspace, c_nvars, c_nlocs, c_hofx) + +end subroutine ufo_scatwind_neutralmetoffice_simobs_c + +! ------------------------------------------------------------------------------ + +end module ufo_scatwind_neutralmetoffice_mod_c diff --git a/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.interface.h b/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.interface.h new file mode 100644 index 000000000..89037214d --- /dev/null +++ b/src/ufo/scatwind/NeutralMetOffice/ObsScatwindNeutralMetOffice.interface.h @@ -0,0 +1,39 @@ +/* + * (C) British Crown Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_SCATWIND_NEUTRALMETOFFICE_OBSSCATWINDNEUTRALMETOFFICE_INTERFACE_H_ +#define UFO_SCATWIND_NEUTRALMETOFFICE_OBSSCATWINDNEUTRALMETOFFICE_INTERFACE_H_ + +#include "ioda/ObsSpace.h" +#include "oops/base/Variables.h" +#include "ufo/Fortran.h" + +namespace ufo { + +/// Interface to Fortran UFO routines +/*! + * The core of the UFO is coded in Fortran. + * Here we define the interfaces to the Fortran code. + */ + +extern "C" { +// ----------------------------------------------------------------------------- +// Scatwind neutral wind observation operator - (Met Office) +// ----------------------------------------------------------------------------- + void ufo_scatwind_neutralmetoffice_setup_f90(F90hop &, const eckit::Configuration &, + const oops::Variables &, oops::Variables &); + void ufo_scatwind_neutralmetoffice_delete_f90(F90hop &); + void ufo_scatwind_neutralmetoffice_simobs_f90(const F90hop &, const F90goms &, + const ioda::ObsSpace &, + const int &, const int &, + double &); +// ----------------------------------------------------------------------------- + +} // extern C + +} // namespace ufo +#endif // UFO_SCATWIND_NEUTRALMETOFFICE_OBSSCATWINDNEUTRALMETOFFICE_INTERFACE_H_ diff --git a/src/ufo/scatwind/NeutralMetOffice/ufo_scatwind_neutralmetoffice_mod.F90 b/src/ufo/scatwind/NeutralMetOffice/ufo_scatwind_neutralmetoffice_mod.F90 new file mode 100644 index 000000000..7bdbf5e10 --- /dev/null +++ b/src/ufo/scatwind/NeutralMetOffice/ufo_scatwind_neutralmetoffice_mod.F90 @@ -0,0 +1,458 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +!> \brief Fortran module for Met Office scatwind neutral wind forward operator +!! +!! \details This code introduces the Met Office observation operator for scatterometer +!! wind data. We assimilate the data as a "neutral" 10m wind, i.e. where the effects of +!! atmospheric stability are neglected. For each observation we calculate the momentum +!! roughness length using the Charnock relation. We then calculate the Monin-Obukhov +!! stability function for momentum, integrated to the model's lowest wind level. +!! The calculations are dependant upon on whether we have stable or unstable conditions +!! according to the Obukhov Length. The neutral 10m wind components are then calculated +!! from the lowest model level winds. +!! +!! \author J.Cotton (Met Office) +!! +!! \date 22/12/2020: Created +!! +module ufo_scatwind_neutralmetoffice_mod + +use iso_c_binding +use kinds +use ufo_vars_mod +use ufo_geovals_mod +use ufo_geovals_mod_c, only: ufo_geovals_registry +use ufo_basis_mod, only: ufo_basis +use obsspace_mod +use oops_variables_mod +use missing_values_mod +use fckit_log_module, only : fckit_log + +implicit none +private + + !> Fortran derived type for neutral wind +type, public :: ufo_scatwind_neutralmetoffice + type(oops_variables), public :: geovars + type(oops_variables), public :: obsvars + contains + procedure :: setup => ufo_scatwind_neutralmetoffice_setup + procedure :: simobs => ufo_scatwind_neutralmetoffice_simobs +end type ufo_scatwind_neutralmetoffice + +character(len=maxvarlen), dimension(7), parameter :: geovars_default = (/ & + var_u, & + var_v, & + var_zi, & + var_sfc_ifrac, & + var_sfc_geomz, & + var_sea_fric_vel, & + var_obk_length /) + +! ------------------------------------------------------------------------------ +contains +! ------------------------------------------------------------------------------ + +subroutine ufo_scatwind_neutralmetoffice_setup(self, f_conf) + use fckit_configuration_module, only: fckit_configuration + implicit none + class(ufo_scatwind_neutralmetoffice), intent(inout) :: self + type(fckit_configuration), intent(in) :: f_conf + + call self%geovars%push_back(geovars_default) + +end subroutine ufo_scatwind_neutralmetoffice_setup + +! ------------------------------------------------------------------------------ +!> Neutral wind forward operator for the Met Office system +!! +!! \author Met Office +!! +!! \date 22/12/2020: Created +!! +! ------------------------------------------------------------------------------ +subroutine ufo_scatwind_neutralmetoffice_simobs(self, geovals, obss, nvars, & + nlocs, hofx) + implicit none + + ! Arguments to this routine + class(ufo_scatwind_NeutralMetOffice), intent(in) :: self !< The object in which this operator is contained + integer, intent(in) :: nvars, nlocs !< The number of variables and locations + type(ufo_geovals), intent(in) :: geovals !< The model values, interpolated to the obsevation locations + real(c_double), intent(inout) :: hofx(nvars, nlocs) !< The output model equivalent of the observations + type(c_ptr), value, intent(in) :: obss !< The observations, and meta-data for those observations + + character(len=*), parameter :: myname_ = "ufo_scatwind_neutralmetoffice_simobs" + integer, parameter :: max_string = 800 + + character(max_string) :: err_msg ! Error message for output + character(max_string) :: message ! General message for output + integer :: nobs ! Number of observations + integer :: iobs ! Loop variable, observation number + type(ufo_geoval), pointer :: cx_u ! Model column of eastward wind + type(ufo_geoval), pointer :: cx_v ! Model column of northward wind + type(ufo_geoval), pointer :: cx_za ! Model heights of wind levels + type(ufo_geoval), pointer :: cx_friction_vel ! Model friction velocity + type(ufo_geoval), pointer :: cx_obukhov_length ! Model obukhov length + type(ufo_geoval), pointer :: cx_orog ! Model orography + type(ufo_geoval), pointer :: cx_seaice ! Model sea ice + real(kind_real), allocatable :: CDR10(:) ! 10m interpolation coefficients + + write(err_msg,*) "TRACE: ufo_scatwind_neutralmetoffice_simobs: begin" + call fckit_log%info(err_msg) + +! check if nlocs is consistent in geovals & hofx + if (geovals%nlocs /= size(hofx(1,:))) then + write(err_msg,*) myname_, ' error: nlocs inconsistent!' + call abor1_ftn(err_msg) + endif + + ! check that hofx is the correct size for simulated variables + if (size(hofx(:,1)) /= 2) then + write(err_msg,*) myname_, ' error: hofx should have 2 variables - eastward_wind and northward_wind' + call abor1_ftn(err_msg) + endif + + write(message, *) myname_, ' Running Met Office neutral wind operator' + call fckit_log%info(message) + +! get variables from geovals + call ufo_geovals_get_var(geovals, var_u, cx_u) ! Eastward wind + call ufo_geovals_get_var(geovals, var_v, cx_v) ! Northward wind + call ufo_geovals_get_var(geovals, var_zi, cx_za) ! Geopotential height of wind levels + call ufo_geovals_get_var(geovals, var_sfc_ifrac, cx_seaice) ! Sea ice + call ufo_geovals_get_var(geovals, var_sfc_geomz, cx_orog) ! Orography + call ufo_geovals_get_var(geovals, var_sea_fric_vel, cx_friction_vel) ! Friction velocity + call ufo_geovals_get_var(geovals, var_obk_length, cx_obukhov_length) ! Obukhov length + + ! Allocate arrays for interpolation weights and initialise to missing data + allocate(CDR10(nlocs)) + CDR10(:) = missing_value(CDR10(1)) + + write(err_msg,*) "TRACE: ufo_scatwind_neutralmetoffice_simobs: begin observation loop, nobs = ", nlocs + call fckit_log%info(err_msg) + + obs_loop: do iobs = 1, nlocs + call ops_scatwind_forwardmodel(cx_za % vals(:, iobs), & + cx_u % vals(:, iobs), & + cx_v % vals(:, iobs), & + cx_friction_vel % vals(1,iobs), & + cx_obukhov_length % vals(1,iobs), & + cx_seaice % vals(1,iobs), & + cx_orog % vals(1,iobs), & + hofx(:,iobs), & + CDR10(iobs)) + end do obs_loop + + deallocate(CDR10) + + write(err_msg,*) "TRACE: ufo_scatwind_neutralmetoffice_simobs: completed" + call fckit_log%info(err_msg) + +end subroutine ufo_scatwind_neutralmetoffice_simobs + +! ------------------------------------------------------------------------------ +!> \brief Scatterometer forward model +!! +!! \details The main steps are as follows: +!! * For each observation we calculate the momentum roughness length using the +!! Charnock relation (Charnock, 1955) modified to include low-wind conditions +!! (Smith, 1988): +!! \f[ +!! z_{0m(sea)} = \frac{0.11\nu}{1.0 \times 10^{-5} + u_*} + +!! \alpha_{ch} \frac{u_*^2}{g} +!! \f] +!! where +!! \f$\nu = 14 \times 10^{-6}\f$ ms-1 is the dynamic viscosity of air, +!! \f$u_{*}\f$ is the friction velocity, +!! \f$\alpha_{ch} =0.018\f$ is the charnock parameter, and +!! \f$g\f$ is the acceleration due to gravity. +!! * Call a subroutine to calculate the Monin-Obukhov stability +!! function for momentum integrated to the lowest model level, +!! \f$\Phi_{m}\f$. +!! The calculations are dependant upon on whether we have stable or unstable +!! conditions according to the Obukhov Length, \f$L\f$. +!! * The neutral 10m wind components, \f$v_{10n}\f$ are then calculated from +!! the lowest model level winds, \f$v_1\f$, as +!! \f[ +!! \textbf{v}_{10n}=\frac{\ln\left(\left(10+z_{0m}\right)/z_{0m}\right)} +!! {\Phi_m\left(L,z_1+z_{0m},z_{0m}\right)} +!! \textbf{v}_1 +!! \f] +!! +!! References: +!! +!! Charnock, H. (1955). Wind stress on a water surface. Quart. J. Royal +!! Meteorol. Soc., 81, 639-640. +!! Smith, R. N. B. (1988). Coefficient for sea surface wind stress, heat flux +!! and wind profiles as a function of wind speed and temperature. +!! J. Geophys. Res., 93, 15467-15472. +!! +!! \author Met Office +!! +!! \date 22/12/2020: Created +!! +! ------------------------------------------------------------------------------ +subroutine ops_scatwind_forwardmodel(za, & + u, & + v, & + ustr, & + oblen, & + seaice, & + orog, & + ycalc, & + cdr10) + +use ufo_constants_mod, only: & + grav ! Gravitational field strength + +real(kind_real), intent(in) :: za(:) !< heights of rho levs +real(kind_real), intent(in) :: u(:) !< Model eastward wind profile +real(kind_real), intent(in) :: v(:) !< Model northward wind profile +real(kind_real), intent(in) :: ustr !< Model friction velocity +real(kind_real), intent(in) :: oblen !< Model obukhov length +real(kind_real), intent(in) :: seaice !< Model sea ice fraction +real(kind_real), intent(in) :: orog !< Model orography +real(kind_real), intent(inout) :: ycalc(:) !< Model equivalent of the obs +real(kind_real), intent(inout) :: cdr10 !< 10m interpolation coefficients +! +! Local parameters +! +integer, parameter :: max_string = 800 ! Length of strings +real, parameter :: scatt_height = 10.0 ! height of observation +real, parameter :: charnock = 0.018 ! Charnock parameter +character(len=*), parameter :: myname_ = "Ops_Scatwind_ForwardModel" +character(max_string) :: message ! General message for output +! +! Local variables +! +real :: u1 ! eastward wind on lowest model level +real :: v1 ! northward wind on lowest model level +real :: oblen_1 ! ObLen checked for very small numbers +real :: recip_l_mo ! Reciprocal of ObLen +real :: z1_uv ! Height of lowest wind (rho) level +real :: z0m ! Roughness length for momentum +real :: phi_m ! Monin-Obukhov stability function + ! integrated to lowest level +real :: phi_m_10 ! Monin-Obukhov stability function + ! integrated to 10m +real :: phi_mn_10 ! Neutral form of stability + ! function integrated to 10m +character(max_string) :: err_msg ! Error message to be output + +if (u(1) == missing_value(u(1))) then ! u wind missing + write(message, *) myname_, "Missing value u1" + call abor1_ftn(message) +end if + +if (v(1) == missing_value(v(1))) then ! v wind missing + write(message, *) myname_, "Missing value v1" + call abor1_ftn(message) +end if + +if (za(1) == missing_value(za(1))) then ! height missing + write(message, *) myname_, "Missing value z1_uv" + call abor1_ftn(message) +end if + +if (oblen == missing_value(oblen)) then ! obukhov length missing + write(message, *) myname_, "Missing value obukhov length" + call abor1_ftn(message) +end if + +if (ustr == missing_value(ustr)) then ! friction vel missing + write(message, *) myname_, "Missing value friction velocity" + call abor1_ftn(message) +end if + +if (orog == missing_value(orog)) then ! orogoraphy missing + write(message, *) myname_, "Missing value orography" + call fckit_log % warning(message) +end if + +if (seaice == missing_value(seaice)) then ! sea ice missing + write(message, *) myname_, "Missing value sea ice" + call fckit_log % warning(message) +end if + +! Get u,v wind components on lowest model level +u1 = u(1) +v1 = v(1) + +! Height (m) of lowest wind (rho) level +z1_uv = za(1) + +! Obukhov length (m) and its reciprocal +oblen_1 = sign( max(1.0E-6, abs(oblen)),oblen) +recip_l_mo = 1.0 / oblen_1 + +if (orog == 0.0 .and. seaice == 0.0 .and. z1_uv > 0.0) then ! over sea only + + ! Calculate roughness height for momentum + ! Consistent with UMDP24 eqn 125 + z0m = 1.54E-6 / (1.0E-5 + ustr) + (charnock / grav) * ustr * ustr + + ! check z0m > 0 before proceeding + if (z0m <= 0) then + write(message, *) myname_, "Invalid roughness height" + call abor1_ftn(message) + end if + + ! Calculate Monin-Obukhov stability function for momentum + ! integrated to the model's lowest wind level. + call ops_scatwind_phi_m_sea (recip_l_mo, & ! in + z1_uv, & ! in + z0m, & ! in + phi_m) ! out + + ! Calculate Monin-Obukhov stability function for momentum + ! integrated to 10m (in case this is different to level 1) + call Ops_Scatwind_phi_m_sea (recip_l_mo, & ! in + scatt_height, & ! in + z0m, & ! in + phi_m_10) ! out + + ! Calculate model 10m neutral wind components + phi_mn_10 = log ((scatt_height + z0m) / z0m) + + if (phi_m > 0.0) then + ! avoid zero divide + ycalc(1) = (phi_mn_10 / phi_m) * u1 + ycalc(2) = (phi_mn_10 / phi_m) * v1 + end if + + ! Store 10m interpolation coefficients to go from 10m real + ! wind to 10m neutral wind (consistent with VAR using fixed 10m) + ! rather than lowest model level + if (phi_m_10 > 0.0) then + ! avoid zero divide + if (cdr10 == missing_value(cdr10)) then + ! only store coefficients the 1st time this routine is called + cdr10 = (phi_mn_10 / phi_m_10) + end if + + end if + +else + ! ob over land/seaice + ycalc(1) = missing_value(ycalc(1)) + ycalc(2) = missing_value(ycalc(2)) + +end if + +end subroutine ops_scatwind_forwardmodel + +! ------------------------------------------------------------------------------ +!> \brief Calculate the integrated froms of the Monin-Obukhov stability functions +!! for surface exchanges. +!! +!! \details The main steps are as follows: +!! * In neutral conditions we have the logarithmic profile: +!! \f[ +!! \Phi_{mn} = \ln\left(\frac{z_1 + z_{0m}}{z_{0m}}\right) +!! \f] +!! * In stable conditions the stability functions of Beljaars and Holtslag (1991) +!! are used: +!! \f[ +!! \Phi_{m} = \Phi_{mn} + +!! a\left(\zeta_1 - \zeta_{0m}\right) + +!! b\left( +!! \left(\zeta_1 - \frac{c}{d}\right) +!! \exp\left(-d\zeta_1\right) - +!! \left(\zeta_{0m} - \frac{c}{d}\right) +!! \exp\left(-d\zeta_{0m}\right) +!! \right) +!! \f] +!! with +!! \f$\zeta_1=(z_1+z_{0m})/L\f$, +!! \f$\zeta_{0m}=z_{0m}/L\f$, and +!! a=1, b=2/3, c=5, d=0.35. +!! +!! * In unstable conditions the Dyer and Hicks forms (Dyer, 1974) are used: +!! \f[ +!! \Phi_{m} = \Phi_{mn} - +!! 2\ln\left(\frac{1+X_1}{1+X_0}\right) - +!! \ln\left(\frac{1+X_1^2}{1+X_0^2}\right) + +!! 2\left(\tan^{-1}(X_1) - \tan^{-1}(X_0)\right) +!! \f] +!! with +!! \f$X_1=(1-16\zeta_1)^{1/4}\f$ and +!! \f$X_0=(1-16\zeta_{0m})^{1/4}\f$ and +!! +!! References: +!! +!! Beljaars, A. C. M. and Holtslag, A. A. M. (1991). Flux parameterisation +!! over land surfaces for atmospheric models. J. Appl. Meteor., 30, +!! 327-341. +!! +!! Dyer, A. J., 1974: A review of flux-profile relationships. Bound. Layer +!! Meteor., 7, 363-372. +!! +!! \author Met Office +!! +!! \date 22/12/2020: Created +!! +! ------------------------------------------------------------------------------ + +subroutine ops_scatwind_phi_m_sea (recip_l_mo, & + z_uv, & + z0m, & + phi_m) + +implicit none +! Subroutine arguments: +real, intent(in) :: recip_l_mo !< Reciprocal of Monin-Obukhov length (m^-1). +real, intent(in) :: z_uv !< Height of wind level above roughness height(m). +real, intent(in) :: z0m !< Roughness length for momentum (m). +real, intent(out) :: phi_m !< Stability function for momentum. +! Local declarations: +character(len=*), parameter :: RoutineName = 'ops_scatwind_phi_m_sea' +real, parameter :: a = 1.0 +real, parameter :: b = 2.0 / 3.0 +real, parameter :: c = 5.0 +real, parameter :: d = 0.35 +real, parameter :: c_over_d = c / d +real :: phi_mn ! Neutral value of stability function for momentum +real :: zeta_uv +real :: zeta_0m +real :: x_uv_sq +real :: x_0m_sq +real :: x_uv +real :: x_0m + +! Calculate neutral value of PHI_M +phi_mn = log ((z_uv + z0m)/z0m) + +! Calculate stability parameters +zeta_uv = (z_uv + z0m) * recip_l_mo +zeta_0m = z0m * recip_l_mo + +if (recip_l_mo >= 0.0) then + ! Calculate PHI_M for neutral and stable conditions. + ! Formulation of Beljaars and Holtslag (1991). + phi_m = phi_mn + & + a * (zeta_uv - zeta_0m) + & + b * ((zeta_uv - c_over_d) * exp (-d * zeta_uv) - & + (zeta_0m - c_over_d) * exp (-d * zeta_0m)) + +else + ! Calculate PHI_M for unstable conditions. + x_uv_sq = sqrt (1.0 - 16.0 * zeta_uv) + x_0m_sq = sqrt (1.0 - 16.0 * zeta_0m) + x_uv = sqrt (x_uv_sq) + x_0m = sqrt (x_0m_sq) + + phi_m = phi_mn - 2.0 * log ((1.0 + x_uv) / (1.0 + x_0m)) - & + log ((1.0 + x_uv_sq) / (1.0 + x_0m_sq)) + & + 2.0 * (atan (x_uv) - atan (x_0m)) + +end if + +end subroutine ops_scatwind_phi_m_sea + +end module ufo_scatwind_neutralmetoffice_mod diff --git a/src/ufo/sfcpcorrected/ufo_sfcpcorrected_mod.F90 b/src/ufo/sfcpcorrected/ufo_sfcpcorrected_mod.F90 index a7fd3d560..af1c75ffb 100644 --- a/src/ufo/sfcpcorrected/ufo_sfcpcorrected_mod.F90 +++ b/src/ufo/sfcpcorrected/ufo_sfcpcorrected_mod.F90 @@ -1,4 +1,4 @@ -! (C) Copyright 2017-2018 UCAR +! (C) Copyright 2017-2020 UCAR ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. @@ -13,6 +13,7 @@ module ufo_sfcpcorrected_mod use iso_c_binding use kinds use ufo_constants_mod, only : grav, rd, Lclr, t2tv + use gnssro_mod_transform, only : geop2geometric implicit none private @@ -29,24 +30,43 @@ module ufo_sfcpcorrected_mod procedure :: simobs => ufo_sfcpcorrected_simobs end type ufo_sfcpcorrected - character(len=MAXVARLEN), dimension(5), parameter :: geovars_default = (/ var_ps, var_geomz, var_sfc_geomz, var_tv, var_prs /) + character(len=MAXVARLEN), dimension(5) :: geovars_list = (/ var_ps, var_geomz, var_sfc_geomz, var_tv, var_prs /) contains ! ------------------------------------------------------------------------------ subroutine ufo_sfcpcorrected_setup(self, f_conf) use fckit_configuration_module, only: fckit_configuration +use fckit_log_module, only : fckit_log implicit none class(ufo_sfcpcorrected), intent(inout) :: self type(fckit_configuration), intent(in) :: f_conf -character(len=:), allocatable :: str +character(len=:), allocatable :: str_psfc_scheme, str_var_sfc_geomz, str_var_geomz +character(max_string) :: debug_msg + +!> In the case where a user wants to specify the geoVaLs variable name of model +!> height of vertical levels and/or the surface height. Example: MPAS is height +!> but FV-3 uses geopotential_height. + +if (f_conf%has("geovar_geomz")) then + call f_conf%get_or_die("geovar_geomz", str_var_geomz) + write(debug_msg,*) "ufo_sfcpcorrected_mod.F90: overriding default var_geomz with ", trim(str_var_geomz) + call fckit_log%debug(debug_msg) + geovars_list(2) = trim(str_var_geomz) +end if +if (f_conf%has("geovar_sfc_geomz")) then + call f_conf%get_or_die("geovar_sfc_geomz", str_var_sfc_geomz) + write(debug_msg,*) "ufo_sfcpcorrected_mod.F90: overriding default var_sfc_geomz with ", trim(str_var_sfc_geomz) + call fckit_log%debug(debug_msg) + geovars_list(3) = trim(str_var_sfc_geomz) +end if -call self%geovars%push_back(geovars_default) +call self%geovars%push_back(geovars_list) self%da_psfc_scheme = "UKMO" if (f_conf%has("da_psfc_scheme")) then - call f_conf%get_or_die("da_psfc_scheme",str) - self%da_psfc_scheme = str + call f_conf%get_or_die("da_psfc_scheme",str_psfc_scheme) + self%da_psfc_scheme = str_psfc_scheme end if end subroutine ufo_sfcpcorrected_setup @@ -67,7 +87,7 @@ subroutine ufo_sfcpcorrected_simobs(self, geovals, obss, nvars, nlocs, hofx) ! Local variables real(c_double) :: missing real(kind_real) :: H2000 = 2000.0 -integer :: nobs, iobs, ivar +integer :: nobs, iobs, ivar, k, kbot, idx_geop real(kind_real), allocatable :: cor_psfc(:) type(ufo_geoval), pointer :: model_ps, model_p, model_sfc_geomz, model_tv, model_geomz character(len=*), parameter :: myname_="ufo_sfcpcorrected_simobs" @@ -75,8 +95,9 @@ subroutine ufo_sfcpcorrected_simobs(self, geovals, obss, nvars, nlocs, hofx) real(kind_real) :: wf integer :: wi logical :: variable_present -real(kind_real), dimension(:), allocatable :: obs_height, obs_t, obs_q, obs_psfc +real(kind_real), dimension(:), allocatable :: obs_height, obs_t, obs_q, obs_psfc, obs_lat real(kind_real), dimension(:), allocatable :: model_tvs, model_zs, model_level1, model_p_2000, model_tv_2000, model_psfc +real(kind_real) :: model_znew missing = missing_value(missing) nobs = obsspace_get_nlocs(obss) @@ -89,6 +110,7 @@ subroutine ufo_sfcpcorrected_simobs(self, geovals, obss, nvars, nlocs, hofx) ! cor_psfc: observed surface pressure at model surface height, corresponding to P_o2m in da_intpsfc_prs* subroutines allocate(cor_psfc(nobs)) +cor_psfc = missing ! get obs variables allocate(obs_height(nobs)) @@ -96,26 +118,101 @@ subroutine ufo_sfcpcorrected_simobs(self, geovals, obss, nvars, nlocs, hofx) call obsspace_get_db(obss, "MetaData", "station_elevation",obs_height) call obsspace_get_db(obss, "ObsValue", "surface_pressure", obs_psfc) -! get model variables -call ufo_geovals_get_var(geovals, var_ps, model_ps) -call ufo_geovals_get_var(geovals, var_geomz, model_geomz) -call ufo_geovals_get_var(geovals, var_sfc_geomz, model_sfc_geomz) -call ufo_geovals_get_var(geovals, var_tv, model_tv) -call ufo_geovals_get_var(geovals, var_prs, model_p) - -if (model_geomz%vals(1,1) .gt. model_geomz%vals(model_geomz%nval,1) ) then - write(err_msg,'(a)') ' ufo_sfcpcorrected:'//new_line('a')// & - ' Model vertical height profile is from top to bottom' - call fckit_log%info(err_msg) -end if +! get model variables; geovars_list = (/ var_ps, var_geomz, var_sfc_geomz, var_tv, var_prs /) +write(err_msg,'(a)') ' ufo_sfcpcorrected:'//new_line('a')// & + ' retrieving GeoVaLs with these names: '//trim(geovars_list(1))// & + ', '//trim(geovars_list(2))//', '//trim(geovars_list(3))// & + ', '//trim(geovars_list(4))//', '//trim(geovars_list(5)) +call fckit_log%debug(err_msg) +call ufo_geovals_get_var(geovals, trim(geovars_list(1)), model_ps) +call ufo_geovals_get_var(geovals, trim(geovars_list(2)), model_geomz) +call ufo_geovals_get_var(geovals, trim(geovars_list(3)), model_sfc_geomz) +call ufo_geovals_get_var(geovals, trim(geovars_list(4)), model_tv) +call ufo_geovals_get_var(geovals, trim(geovars_list(5)), model_p) + +! discover if the model vertical profiles are ordered top-bottom or not +kbot = 1 +do iobs = 1, nlocs + if (obs_psfc(iobs).ne.missing) then + if (model_geomz%vals(1,iobs) .gt. model_geomz%vals(model_geomz%nval,iobs)) then + write(err_msg,'(a)') ' ufo_sfcpcorrected:'//new_line('a')// & + ' Model vertical height profile is from top to bottom' + call fckit_log%debug(err_msg) + kbot = model_geomz%nval + endif + exit + endif +enddo allocate(model_zs(nobs)) allocate(model_level1(nobs)) allocate(model_psfc(nobs)) +! If needed, we can convert geopotential heights to geometric altitude +! for the full model vertical column using gnssro_mod_transform. We need +! to get the latitude of observation to do this. +idx_geop = -1 +idx_geop = index(trim(geovars_list(2)),'geopotential') +if (idx_geop.gt.0) then + write(err_msg,'(a)') ' ufo_sfcpcorrected:'//new_line('a')// & + ' converting '//trim(geovars_list(2))// & + ' variable to z-geometric' + call fckit_log%debug(err_msg) + if (.not. allocated(obs_lat)) then + variable_present = obsspace_has(obss, "MetaData", "latitude") + if (variable_present) then + call fckit_log%debug(' allocating obs_lat array') + allocate(obs_lat(nobs)) + call obsspace_get_db(obss, "MetaData", "latitude", obs_lat) + else + call abor1_ftn('Variable latitude@MetaData does not exist, aborting') + endif + endif + do iobs = 1, nlocs + if (obs_psfc(iobs).ne.missing) then + do k = 1, model_geomz%nval + call geop2geometric(latitude=obs_lat(iobs), & + geopotentialH=model_geomz%vals(k,iobs), & + geometricZ=model_znew) + model_geomz%vals(k,iobs) = model_znew + enddo + endif + enddo +endif + +! Now do the same if needed for surface geopotential height. +idx_geop = -1 +idx_geop = index(trim(geovars_list(3)),'geopotential') +if (idx_geop.gt.0) then + write(err_msg,'(a)') ' ufo_sfcpcorrected:'//new_line('a')// & + ' converting '//trim(geovars_list(3))// & + ' variable to z-sfc-geometric' + call fckit_log%debug(err_msg) + if (.not. allocated(obs_lat)) then + variable_present = obsspace_has(obss, "MetaData", "latitude") + if (variable_present) then + call fckit_log%debug(' allocating obs_lat array') + allocate(obs_lat(nobs)) + call obsspace_get_db(obss, "MetaData", "latitude", obs_lat) + else + call abor1_ftn('Variable latitude@MetaData does not exist, aborting') + endif + endif + do iobs = 1, nlocs + if (obs_psfc(iobs).ne.missing) then + call geop2geometric(latitude=obs_lat(iobs), & + geopotentialH=model_sfc_geomz%vals(1,iobs), & + geometricZ=model_znew) + model_sfc_geomz%vals(1,iobs) = model_znew + endif + enddo +endif + +if (allocated(obs_lat)) deallocate(obs_lat) + model_zs = model_sfc_geomz%vals(1,:) -model_level1 = model_geomz%vals(model_geomz%nval,:) !reverse model_psfc = model_ps%vals(1,:) +model_level1 = model_geomz%vals(kbot,:) ! do terrain height correction, two optional schemes select case (trim(self%da_psfc_scheme)) @@ -135,7 +232,7 @@ subroutine ufo_sfcpcorrected_simobs(self, geovals, obss, nvars, nlocs, hofx) ! get extra model values allocate(model_tvs(nobs)) - model_tvs = model_tv%vals(model_tv%nval,:) + Lclr * ( model_level1 - model_zs ) !Lclr = 0.0065 K/m + model_tvs = model_tv%vals(kbot,:) + Lclr * ( model_level1 - model_zs ) !Lclr = 0.0065 K/m ! correction call da_intpsfc_prs(nobs, missing, cor_psfc, obs_height, obs_psfc, model_zs, model_tvs, obs_t, obs_q) @@ -163,7 +260,7 @@ subroutine ufo_sfcpcorrected_simobs(self, geovals, obss, nvars, nlocs, hofx) case default write(err_msg,*) "ufo_sfcpcorrected_mod.F90: da_psfc_scheme must be WRFDA or UKMO" - call fckit_log%info(err_msg) + call fckit_log%debug(err_msg) call abor1_ftn(err_msg) end select diff --git a/src/ufo/timeoper/ObsTimeOper.cc b/src/ufo/timeoper/ObsTimeOper.cc index ca1e12c82..980f98c02 100644 --- a/src/ufo/timeoper/ObsTimeOper.cc +++ b/src/ufo/timeoper/ObsTimeOper.cc @@ -61,64 +61,17 @@ ObsTimeOper::~ObsTimeOper() { // ----------------------------------------------------------------------------- -std::unique_ptr ObsTimeOper::locations(const util::DateTime & t1, - const util::DateTime & t2) const { +std::unique_ptr ObsTimeOper::locations() const { oops::Log::trace() << "entered ObsOperatorTime::locations" << std::endl; - util::DateTime t0, t3, stateTime; - util::Duration initial_dt, windowSub; + std::unique_ptr locs = actualoperator_->locations(); + // concatenate locations with itself (double the size) + *locs += *locs; - util::DateTime windowBegin(odb_.windowStart()); - util::DateTime windowEnd(odb_.windowEnd()); - - initial_dt = t2 - t1; - - std::unique_ptr locs; - std::unique_ptr locs2; - if ((t1 == windowBegin) && (t2 == windowEnd)) { - oops::Log::debug() << "locs: full window to concatenate" << std::endl; - locs = std::unique_ptr(new Locations(odb_, t1, t2)); - *locs += *locs; - } else { -// define t0, t3, stateTime - if ((t1 == windowBegin) || (t2 == windowEnd)) - windowSub = initial_dt * 2; - else - windowSub = initial_dt; - - t0 = t1 - (windowSub/2); - t3 = t2 + (windowSub/2); - stateTime = t1 + initial_dt/2; - - if (t1 == windowBegin) { - t0 = windowBegin; - stateTime = windowBegin; - } - if (t2 == windowEnd) { - t3 = windowEnd; - stateTime = windowEnd; - } - - if ((t1 == windowBegin) && (t2 != windowEnd)) { - oops::Log::debug() << "locs: locsObsAfterState only" << std::endl; - locs = std::unique_ptr(new Locations(odb_, stateTime, t3)); - } else if ((t1 != windowBegin) && (t2 == windowEnd)) { - oops::Log::debug() << " locs: locsObsBeforeState only " << std::endl; - locs = std::unique_ptr(new Locations(odb_, stateTime, stateTime)); - // the above locs is mainly empty except for self%max_indx = obsspace_get_gnlocs(obss) - locs2 = std::unique_ptr(new Locations(odb_, t0, stateTime)); - *locs += *locs2; - } else { - oops::Log::debug() << "locs: internal window concatenate" << std::endl; - locs = std::unique_ptr(new Locations(odb_, stateTime, t3)); - locs2 = std::unique_ptr(new Locations(odb_, t0, stateTime)); - *locs += *locs2; - } - } - // create concatenation of Locations class return locs; } +// ----------------------------------------------------------------------------- void ObsTimeOper::simulateObs(const GeoVaLs & gv, ioda::ObsVector & ovec, ObsDiagnostics & ydiags) const { @@ -126,7 +79,9 @@ void ObsTimeOper::simulateObs(const GeoVaLs & gv, ioda::ObsVector & ovec, oops::Log::trace() << gv << std::endl; - GeoVaLs gv1(odb_.comm()), gv2(odb_.comm()); + GeoVaLs gv1(odb_.distribution(), gv.getVars()); + GeoVaLs gv2(odb_.distribution(), gv.getVars()); + gv.split(gv1, gv2); oops::Log::trace() << gv1 << std::endl; diff --git a/src/ufo/timeoper/ObsTimeOper.h b/src/ufo/timeoper/ObsTimeOper.h index 164017c37..1f7c08de8 100644 --- a/src/ufo/timeoper/ObsTimeOper.h +++ b/src/ufo/timeoper/ObsTimeOper.h @@ -14,7 +14,6 @@ #include #include "oops/base/Variables.h" -#include "oops/util/DateTime.h" #include "oops/util/Duration.h" #include "oops/util/ObjectCounter.h" @@ -51,8 +50,7 @@ class ObsTimeOper : public ObsOperatorBase, // Obs Operator void simulateObs(const GeoVaLs &, ioda::ObsVector &, ObsDiagnostics &) const override; - std::unique_ptr locations(const util::DateTime &, - const util::DateTime &) const override; + std::unique_ptr locations() const override; // Other const oops::Variables & requiredVars() const override {return actualoperator_->requiredVars();} diff --git a/src/ufo/timeoper/ObsTimeOperTLAD.cc b/src/ufo/timeoper/ObsTimeOperTLAD.cc index 563c3e5ea..70accf786 100644 --- a/src/ufo/timeoper/ObsTimeOperTLAD.cc +++ b/src/ufo/timeoper/ObsTimeOperTLAD.cc @@ -32,9 +32,10 @@ static LinearObsOperatorMaker makerTimeOperTL_("TimeOperLinInte ObsTimeOperTLAD::ObsTimeOperTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : actualoperator_(LinearObsOperatorFactory::create( + : LinearObsOperatorBase(odb), + actualoperator_(LinearObsOperatorFactory::create( odb, eckit::LocalConfiguration(config, "obs operator"))), - odb_(odb), timeWeights_(timeWeightCreate(odb, config)) + timeWeights_(timeWeightCreate(odb, config)) { oops::Log::trace() << "ObsTimeOperTLAD created" << std::endl; } @@ -55,7 +56,8 @@ void ObsTimeOperTLAD::setTrajectory(const GeoVaLs & geovals, oops::Log::debug() << "ObsTimeOperTLAD::setTrajectory input geovals " << geovals << std::endl; - GeoVaLs gv1(odb_.comm()), gv2(odb_.comm()); + GeoVaLs gv1(obsspace().distribution(), geovals.getVars()); + GeoVaLs gv2(obsspace().distribution(), geovals.getVars()); geovals.split(gv1, gv2); oops::Log::debug() << "ObsTimeOperTLAD::setTrajectory split geovals gv1 " @@ -86,7 +88,8 @@ void ObsTimeOperTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & o oops::Log::debug() << "ObsTimeOperTLAD::setTrajectory input geovals " << geovals << std::endl; - GeoVaLs gv1(odb_.comm()), gv2(odb_.comm()); + GeoVaLs gv1(obsspace().distribution(), geovals.getVars()); + GeoVaLs gv2(obsspace().distribution(), geovals.getVars()); geovals.split(gv1, gv2); oops::Log::debug() << "ObsTimeOperTLAD::simulateObsTL split geovals gv1 " @@ -115,7 +118,8 @@ void ObsTimeOperTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & o oops::Log::debug() << "ObsTimeOperTLAD::simulateObsAD input geovals " << geovals << std::endl; - GeoVaLs gv1(odb_.comm()), gv2(odb_.comm()); + GeoVaLs gv1(obsspace().distribution(), geovals.getVars()); + GeoVaLs gv2(obsspace().distribution(), geovals.getVars()); geovals.split(gv1, gv2); oops::Log::debug() << "ObsTimeOperTLAD::simulateObsAD split geovals gv1 " diff --git a/src/ufo/timeoper/ObsTimeOperTLAD.h b/src/ufo/timeoper/ObsTimeOperTLAD.h index b20156688..e09b1b3cf 100644 --- a/src/ufo/timeoper/ObsTimeOperTLAD.h +++ b/src/ufo/timeoper/ObsTimeOperTLAD.h @@ -56,7 +56,6 @@ class ObsTimeOperTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; std::unique_ptr actualoperator_; - const ioda::ObsSpace& odb_; std::vector> timeWeights_; }; diff --git a/src/ufo/timeoper/ObsTimeOperUtil.cc b/src/ufo/timeoper/ObsTimeOperUtil.cc index 1cabcdfeb..55b3da8bc 100644 --- a/src/ufo/timeoper/ObsTimeOperUtil.cc +++ b/src/ufo/timeoper/ObsTimeOperUtil.cc @@ -27,7 +27,7 @@ std::vector> timeWeightCreate(const ioda::ObsSpace & odb_, util::DateTime windowBegin(odb_.windowStart()); util::Duration windowSub; windowSub = util::Duration(config.getString("windowSub")); - int windowSubSec = windowSub.toSeconds(); + int64_t windowSubSec = windowSub.toSeconds(); std::size_t nlocs = odb_.nlocs(); @@ -42,13 +42,13 @@ std::vector> timeWeightCreate(const ioda::ObsSpace & odb_, for (std::size_t i = 0; i < nlocs; ++i) { util::Duration timeFromStart = dateTimeIn[i] - windowBegin; - int timeFromStartSec = timeFromStart.toSeconds(); - int StateTimeFromStartSec = + int64_t timeFromStartSec = timeFromStart.toSeconds(); + int64_t StateTimeFromStartSec = (timeFromStartSec / windowSubSec) * windowSubSec; if ((timeFromStartSec - StateTimeFromStartSec) == 0) { - TimeWeightObsAfterState[i] = 1.0; + TimeWeightObsAfterState[i] = 1.0f; } else { - TimeWeightObsAfterState[i] = 1.0 - static_cast(timeFromStartSec - + TimeWeightObsAfterState[i] = 1.0f - static_cast(timeFromStartSec - StateTimeFromStartSec)/ static_cast(windowSubSec); } @@ -57,7 +57,7 @@ std::vector> timeWeightCreate(const ioda::ObsSpace & odb_, << " StateTimeFromStartSec = " << StateTimeFromStartSec << std::endl; } - for (int i=0; i < TimeWeightObsAfterState.size(); ++i) { + for (std::size_t i=0; i < TimeWeightObsAfterState.size(); ++i) { oops::Log::debug() << "timeweights [" << i << "] = " << TimeWeightObsAfterState[i] << std::endl; } @@ -65,7 +65,7 @@ std::vector> timeWeightCreate(const ioda::ObsSpace & odb_, std::vector TimeWeightObsBeforeState(nlocs, 0.0); transform(TimeWeightObsAfterState.cbegin(), TimeWeightObsAfterState.cend(), TimeWeightObsBeforeState.begin(), - [] (float element) {return 1.0 - element;}); + [] (float element) {return 1.0f - element;}); std::vector> timeWeights; timeWeights.push_back(TimeWeightObsAfterState); diff --git a/src/ufo/ufo_constants_mod.F90 b/src/ufo/ufo_constants_mod.F90 index c2485541c..1b7f472da 100644 --- a/src/ufo/ufo_constants_mod.F90 +++ b/src/ufo/ufo_constants_mod.F90 @@ -3,7 +3,6 @@ module ufo_constants_mod !========================================================================== use kinds -use iso_c_binding implicit none real(kind_real), parameter, public :: grav = 9.80665e+0_kind_real @@ -64,9 +63,21 @@ module ufo_constants_mod real(kind_real), parameter, public :: g_to_kg = 0.001_kind_real real(kind_real), parameter, public :: m_to_km = 0.001_kind_real real(kind_real), parameter, public :: Pa_to_hPa = 0.01_kind_real -real(kind_real), parameter, public :: min_q = 3e-6 -real(kind_real), parameter, public :: ZeroDegC = 273.15 -real(kind_real), parameter, public :: epsilon = 0.622 ! DARFIX probably! + +real(kind_real), parameter, public :: min_q = 3.0e-6_kind_real +real(kind_real), parameter, public :: ZeroDegC = 273.15_kind_real +real(kind_real), parameter, public :: epsilon = 0.62198_kind_real ! molecular mass ratio of water (18.01528) to dry air (28.9645) + +real(kind_real), parameter, public :: q_mixratio_to_ppmv = 1.60771704e+6_kind_real +real(kind_real), parameter, public :: o3_mixratio_to_ppmv = 6.03504e+5_kind_real +real(kind_real), parameter, public :: co2_mixratio_to_ppmv = 6.58114e+5_kind_real +real(kind_real), parameter, public :: co_mixratio_to_ppmv = 1.0340699e+6_kind_real +real(kind_real), parameter, public :: n2o_mixratio_to_ppmv = 6.58090e+5_kind_real +real(kind_real), parameter, public :: ch4_mixratio_to_ppmv = 1.80548e+6_kind_real +real(kind_real), parameter, public :: so2_mixratio_to_ppmv = 4.52118e+5_kind_real ! determined using same method as used in RTTOV but this + ! constant was never explicitly calculated + ! mso2 = 32.065 + 2 * 15.9994 = 64.0638 + end module ufo_constants_mod diff --git a/src/ufo/ufo_geovals_mod.F90 b/src/ufo/ufo_geovals_mod.F90 index d47e43d6b..e524d809f 100644 --- a/src/ufo/ufo_geovals_mod.F90 +++ b/src/ufo/ufo_geovals_mod.F90 @@ -6,7 +6,6 @@ ! module ufo_geovals_mod -use fckit_configuration_module, only: fckit_configuration use iso_c_binding use ufo_vars_mod use kinds @@ -20,9 +19,10 @@ module ufo_geovals_mod integer, parameter :: max_string=800 public :: ufo_geovals, ufo_geoval -public :: ufo_geovals_get_var, ufo_geovals_put_var +public :: ufo_geovals_get_var public :: ufo_geovals_default_constr, ufo_geovals_setup, ufo_geovals_delete, ufo_geovals_print -public :: ufo_geovals_zero, ufo_geovals_random, ufo_geovals_dotprod, ufo_geovals_scalmult +public :: ufo_geovals_zero, ufo_geovals_random, ufo_geovals_scalmult +public :: ufo_geovals_allocate public :: ufo_geovals_profmult public :: ufo_geovals_reorderzdir public :: ufo_geovals_assign, ufo_geovals_add, ufo_geovals_diff, ufo_geovals_abs @@ -84,7 +84,6 @@ subroutine ufo_geovals_setup(self, vars, nlocs) integer, intent(in) :: nlocs integer :: ivar -type(fckit_configuration) :: f_vars call ufo_geovals_delete(self) self%nlocs = nlocs @@ -101,6 +100,49 @@ subroutine ufo_geovals_setup(self, vars, nlocs) end subroutine ufo_geovals_setup +! ------------------------------------------------------------------------------ +!> Allocates GeoVaLs for \p vars variables with \p nlevels number of levels. +!> If the GeoVaLs for this variable were allocated before with different size, +!> aborts. +subroutine ufo_geovals_allocate(self, vars, nlevels) +use oops_variables_mod +implicit none +type(ufo_geovals), intent(inout) :: self +type(oops_variables), intent(in) :: vars +integer, intent(in) :: nlevels + +integer :: ivar, ivar_gvals +character(max_string) :: err_msg + +do ivar = 1, vars%nvars() + ! find index of variable to be allocated + ivar_gvals = ufo_vars_getindex(self%variables, vars%variable(ivar)) + ! abort if we are trying to allocate geovals for nonexistent variable + if (ivar_gvals < 0) then + write(err_msg,*) "ufo_geovals_allocate: ", trim(vars%variable(ivar)), " doesn't exist in geovals" + call abor1_ftn(err_msg) + endif + ! abort if we are trying to allocate geovals again, and with a different size + if (allocated(self%geovals(ivar_gvals)%vals) .and. (self%geovals(ivar_gvals)%nval /= nlevels)) then + write(err_msg,*) "ufo_geovals_allocate: attempting to allocate already allocated geovals for ", & + trim(vars%variable(ivar)), ". Previously allocated as ", self%geovals(ivar_gvals)%nval, & + " levels; now trying to allocate as ", nlevels, " levels." + call abor1_ftn(err_msg) + ! only allocate if not already allocated + elseif (.not. allocated(self%geovals(ivar_gvals)%vals)) then + self%geovals(ivar_gvals)%nval = nlevels + allocate(self%geovals(ivar_gvals)%vals(nlevels, self%nlocs)) + endif +enddo + +! check if all variables are now allocated, and set self%linit accordingly +self%linit = .true. +do ivar = 1, self%nvar + if (.not. allocated(self%geovals(ivar)%vals)) self%linit = .false. +enddo + +end subroutine ufo_geovals_allocate + ! ------------------------------------------------------------------------------ subroutine ufo_geovals_delete(self) @@ -114,7 +156,7 @@ subroutine ufo_geovals_delete(self) if (allocated(self%geovals(ivar)%vals)) deallocate(self%geovals(ivar)%vals) enddo deallocate(self%geovals) - endif +endif if (allocated(self%variables)) deallocate(self%variables) self%nvar = 0 self%nlocs = 0 @@ -157,21 +199,6 @@ end subroutine ufo_geovals_get_var ! ------------------------------------------------------------------------------ -subroutine ufo_geovals_put_var(self, varname, geoval,k) -type(ufo_geovals),intent(inout) :: self -character(len=*), intent(in) :: varname -type(ufo_geoval), intent(in) :: geoval -integer, intent(in) :: k - -integer :: ivar - -ivar = ufo_vars_getindex(self%variables, varname) -self%geovals(ivar)%vals(k,:)=geoval%vals(k,:) - -end subroutine ufo_geovals_put_var - -! ------------------------------------------------------------------------------ - subroutine ufo_geovals_zero(self) implicit none type(ufo_geovals), intent(inout) :: self @@ -348,9 +375,7 @@ subroutine ufo_geovals_reorderzdir(self, varname, zdir) ! Get vertical coordinate variable call ufo_geovals_get_var(self, varname, geoval) -if (associated(geoval)) then - print *, 'ufo_geovals_reorderzdir: geoval vertical coordinate variable ', trim(varname), geoval%nval, geoval%nlocs -else +if (.not. associated(geoval)) then write(err_msg, *) 'ufo_geovals_reorderzdir: geoval vertical coordinate variable ', trim(varname), ' doesnt exist' endif @@ -358,11 +383,9 @@ subroutine ufo_geovals_reorderzdir(self, varname, zdir) if ((zdir == "bottom2top" .and. geoval%vals(1,1) < geoval%vals(geoval%nval,1)) .or. & (zdir == "top2bottom" .and. geoval%vals(1,1) > geoval%vals(geoval%nval,1))) then do_flip = .true. - print *, 'ufo_geovals_reorderzdir: do_flip ', do_flip else if (zdir /= "bottom2top" .or. zdir /= "top2bottom") then write(err_msg, *) 'ufo_geovals_reorderzdir: z-coordinate direction ', trim(zdir), ' not defined' else - print *, 'no need to reorder variables in vertical direction (zdir) do_flip ', do_flip return endif @@ -596,21 +619,23 @@ end subroutine ufo_geovals_copy_one !! subroutine ufo_geovals_analytic_init(self, locs, ic) -use ufo_locs_mod, only : ufo_locs use dcmip_initial_conditions_test_1_2_3, only : test1_advection_deformation, & test1_advection_hadley, test3_gravity_wave use dcmip_initial_conditions_test_4, only : test4_baroclinic_wave +use ufo_locations_mod +use ufo_utils_mod, only: cmp_strings implicit none type(ufo_geovals), intent(inout) :: self -type(ufo_locs), intent(in) :: locs +type(ufo_locations), intent(in) :: locs character(*), intent(in) :: ic real(kind_real) :: pi = acos(-1.0_kind_real) real(kind_real) :: deg_to_rad,rlat, rlon real(kind_real) :: p0, kz, u0, v0, w0, t0, phis0, ps0, rho0, hum0 real(kind_real) :: q1, q2, q3, q4 -integer :: ivar, iloc, ival +real(kind_real), allocatable, dimension(:) :: lons, lats +integer :: nlocs, ivar, iloc, ival if (.not. self%linit) then call abor1_ftn("ufo_geovals_analytic_init: geovals not defined") @@ -618,19 +643,25 @@ subroutine ufo_geovals_analytic_init(self, locs, ic) ! The last variable should be the ln pressure coordinate. That's ! where we get the height information for the analytic init -if (trim(self%variables(self%nvar)) /= trim(var_prs)) then +if (self%variables(self%nvar) /= var_prs .and. & + self%variables(self%nvar) /= var_prsi) then call abor1_ftn("ufo_geovals_analytic_init: pressure coordinate not defined") endif deg_to_rad = pi/180.0_kind_real +nlocs = locs%nlocs() +allocate(lons(nlocs), lats(nlocs)) +call locs%get_lons(lons) +call locs%get_lats(lats) + do ivar = 1, self%nvar-1 do iloc = 1, self%geovals(ivar)%nlocs ! convert lat and lon to radians - rlat = deg_to_rad * locs%lat(iloc) - rlon = deg_to_rad*modulo(locs%lon(iloc)+180.0_kind_real,360.0_kind_real) - pi + rlat = deg_to_rad * lats(iloc) + rlon = deg_to_rad*modulo(lons(iloc)+180.0_kind_real,360.0_kind_real) - pi do ival = 1, self%geovals(ivar)%nval @@ -641,6 +672,10 @@ subroutine ufo_geovals_analytic_init(self, locs, ic) init_option: select case (trim(ic)) + case ("invent_state") + + t0 = cos(deg_to_rad * lons(iloc) ) * cos(rlat) + case ("dcmip-test-1-1") call test1_advection_deformation(rlon,rlat,p0,kz,0,u0,v0,w0,& @@ -667,8 +702,8 @@ subroutine ufo_geovals_analytic_init(self, locs, ic) end select init_option - ! currently only temperture is implemented - if (trim(self%variables(ivar)) == trim(var_tv)) then + ! currently only temperature is implemented + if (cmp_strings(self%variables(ivar), var_tv)) then ! Warning: we may need a conversion from temperature to ! virtual temperture here self%geovals(ivar)%vals(ival,iloc) = t0 @@ -678,6 +713,8 @@ subroutine ufo_geovals_analytic_init(self, locs, ic) enddo enddo +deallocate(lons, lats) + end subroutine ufo_geovals_analytic_init ! ------------------------------------------------------------------------------ @@ -748,44 +785,6 @@ end subroutine ufo_geovals_normalize ! ------------------------------------------------------------------------------ -subroutine ufo_geovals_dotprod(self, other, gprod, f_comm) -implicit none -real(kind_real), intent(inout) :: gprod -type(ufo_geovals), intent(in) :: self, other -integer :: ivar, iobs, ival, nval -real(kind_real) :: prod - -type(fckit_mpi_comm), intent(in) :: f_comm - -if (.not. self%linit) then - call abor1_ftn("ufo_geovals_dotprod: geovals not allocated") -endif - -if (.not. other%linit) then - call abor1_ftn("ufo_geovals_dotprod: geovals not allocated") -endif - -! just something to put in (dot product of the 1st var and 1st element in the profile -prod=0.0 -do ivar = 1, self%nvar - nval = self%geovals(ivar)%nval - do ival = 1, nval - do iobs = 1, self%nlocs - if ((self%geovals(ivar)%vals(ival,iobs) .ne. self%missing_value) .and. & - (other%geovals(ivar)%vals(ival,iobs) .ne. self%missing_value)) then - prod = prod + self%geovals(ivar)%vals(ival,iobs) * & - other%geovals(ivar)%vals(ival,iobs) - endif - enddo - enddo -enddo - -!Get global dot product -call f_comm%allreduce(prod,gprod,fckit_mpi_sum()) - -end subroutine ufo_geovals_dotprod - -!------------------------------------------------------------------------------- subroutine ufo_geovals_reset_sec_arg(self, other, nlocs) implicit none type(ufo_geovals), intent(in) :: self diff --git a/src/ufo/ufo_locations_mod.F90 b/src/ufo/ufo_locations_mod.F90 new file mode 100644 index 000000000..8dddff4eb --- /dev/null +++ b/src/ufo/ufo_locations_mod.F90 @@ -0,0 +1,107 @@ +! +! (C) Copyright 2020 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran interface to ufo::Locations + +module ufo_locations_mod + +use iso_c_binding +implicit none + +public :: ufo_locations + +type ufo_locations +private + type(c_ptr) :: ptr +contains + procedure, public :: nlocs + procedure, public :: get_lons + procedure, public :: get_lats + procedure, public :: get_timemask +end type + +interface ufo_locations + module procedure ctor_from_ptr +end interface + +private + +#include "ufo/locations_interface.f" + +contains + +!------------------------------------------------------------------------------- +!> Saves pointer to locations +function ctor_from_ptr(ptr) result(this) + type(ufo_locations) :: this + type(c_ptr), intent(in) :: ptr + + this%ptr = ptr +end function ctor_from_ptr + +!------------------------------------------------------------------------------- +!> Return the number of observational locations in this Locations object +integer function nlocs(this) + implicit none + class(ufo_locations), intent(in) :: this + + ! Implicit conversion from c_size_t to integer which is safe in this case + nlocs = c_locations_get_nlocs(this%ptr) +end function nlocs + +!------------------------------------------------------------------------------- + +!> Get longitudes from the Locations object +subroutine get_lons(this, lons) + implicit none + class(ufo_locations), intent(in) :: this + real(c_double), intent(inout) :: lons(:) + + integer(c_size_t) :: length + + length = size(lons) + call c_locations_get_lons(this%ptr, length, lons) + +end subroutine get_lons + +!------------------------------------------------------------------------------- + +!> Get latitudes from the Locations object +subroutine get_lats(this, lats) + implicit none + class(ufo_locations), intent(in) :: this + real(c_double), intent(inout) :: lats(:) + + integer(c_size_t) :: length + + length = size(lats) + call c_locations_get_lats(this%ptr, length, lats) + +end subroutine get_lats + +!------------------------------------------------------------------------------- + +!> Get time mask (obs that are between t1 & t2) from the Locations object +subroutine get_timemask(this, t1, t2, mask) + use datetime_mod + implicit none + class(ufo_locations), intent(in) :: this + type(datetime), intent(in) :: t1, t2 + logical(c_bool), intent(inout) :: mask(:) + + integer(c_size_t) :: length + type(c_ptr) :: c_t1, c_t2 + + length = size(mask) + call f_c_datetime(t1, c_t1) + call f_c_datetime(t2, c_t2) + call c_locations_get_timemask(this%ptr, c_t1, c_t2, length, mask) + +end subroutine get_timemask + +!------------------------------------------------------------------------------- + +end module ufo_locations_mod diff --git a/src/ufo/ufo_locs_mod.F90 b/src/ufo/ufo_locs_mod.F90 deleted file mode 100644 index 3a182b85d..000000000 --- a/src/ufo/ufo_locs_mod.F90 +++ /dev/null @@ -1,314 +0,0 @@ -! -! (C) Copyright 2017 UCAR -! -! This software is licensed under the terms of the Apache Licence Version 2.0 -! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - -!> Fortran module handling observation locations - -module ufo_locs_mod - -use datetime_mod -use iso_c_binding -use kinds -use fckit_log_module, only : fckit_log -use obsspace_mod - -implicit none -private -public :: ufo_locs, ufo_locs_create, ufo_locs_copy, ufo_locs_setup, ufo_locs_delete -public :: ufo_locs_init, ufo_locs_concatenate, ufo_locs_time_mask - -! -------------------------------------------------------------------------------------------------- - -!> Fortran derived type to hold observation locations -type :: ufo_locs - integer :: nlocs - integer :: max_indx - real(kind_real), allocatable, dimension(:) :: lat !< latitude - real(kind_real), allocatable, dimension(:) :: lon !< longitude - type(datetime), allocatable, dimension(:) :: time !< obs-time - integer, allocatable, dimension(:) :: indx !< indices of locations in the full [geovals] array -end type ufo_locs - -! -------------------------------------------------------------------------------------------------- -contains -! -------------------------------------------------------------------------------------------------- - -subroutine ufo_locs_create(self, obss, nlocs, lats, lons) -implicit none -type(ufo_locs), intent(inout) :: self -type(c_ptr), value, intent(in) :: obss -integer, intent(in) :: nlocs -real(kind_real), intent(in) :: lats(nlocs) -real(kind_real), intent(in) :: lons(nlocs) - -integer :: n -character(len=20), allocatable :: fstring(:) -type(datetime), dimension(:), allocatable :: date_time -self%nlocs = nlocs -self%max_indx = nlocs -allocate(self%lat(nlocs), self%lon(nlocs), self%time(nlocs)) -allocate(self%indx(nlocs)) -self%lat(:) = lats(:) -self%lon(:) = lons(:) - -allocate(fstring(nlocs)) - -if (obsspace_has(obss,"MetaData", "datetime")) then - allocate(date_time(nlocs)) - call obsspace_get_db(obss, "MetaData", "datetime", date_time) - do n = 1, self%nlocs - call datetime_to_string(date_time(n), fstring(n)) - enddo - deallocate(date_time) -else - fstring(:) = "9999-09-09T09:09:09Z" -endif - -do n = 1, self%nlocs - call datetime_create(fstring(n), self%time(n)) -enddo -do n = 1, self%nlocs - self%indx(n) = n -enddo - -end subroutine ufo_locs_create - -! -------------------------------------------------------------------------------------------------- - -subroutine ufo_locs_setup(self, nlocs) -implicit none -type(ufo_locs), intent(inout) :: self -integer, intent(in) :: nlocs - -character(len=20) :: fstring -integer :: n - -call ufo_locs_delete(self) - -self%max_indx = nlocs -self%nlocs = nlocs -allocate(self%lat(nlocs), self%lon(nlocs), self%time(nlocs), self%indx(nlocs)) -self%lat(:) = 0.0 -self%lon(:) = 0.0 -fstring="9999-09-09T09:09:09Z" -do n = 1, self%nlocs - call datetime_create(fstring, self%time(n)) -enddo -self%indx(:) = 0 - -end subroutine ufo_locs_setup - -! -------------------------------------------------------------------------------------------------- - -subroutine ufo_locs_copy(self, other) - -implicit none -type(ufo_locs), intent(inout) :: self -type(ufo_locs), intent(in) :: other - -self%nlocs = other%nlocs -self%max_indx = other%max_indx - -allocate(self%lat (self%nlocs)) -allocate(self%lon (self%nlocs)) -allocate(self%time(self%nlocs)) -allocate(self%indx(self%nlocs)) - -self%lat = other%lat -self%lon = other%lon -self%time = other%time -self%indx = other%indx - -end subroutine ufo_locs_copy - -! -------------------------------------------------------------------------------------------------- - -subroutine ufo_locs_delete(self) -implicit none -type(ufo_locs), intent(inout) :: self - -if (allocated(self%lat)) deallocate(self%lat) -if (allocated(self%lon)) deallocate(self%lon) -if (allocated(self%time)) deallocate(self%time) -if (allocated(self%indx)) deallocate(self%indx) -self%nlocs = 0 -self%max_indx = -1 ! not set - -end subroutine ufo_locs_delete - -! -------------------------------------------------------------------------------------------------- - -subroutine ufo_locs_concatenate(self, other) -implicit none -type(ufo_locs), intent(inout) :: self -type(ufo_locs), intent(in) :: other - -type(ufo_locs) :: temp_self, temp_other -character(255) :: message -integer :: n - -if ((self%max_indx < 0) .OR. (other%max_indx < 0)) then - write(message,'(A, A, I6, A, I6)') & - 'ufo_locs_concatenate: either self or other needs to be constructed valid indices ', & - ' self%max_indx =', self%max_indx, ' other%max_indx = ', other%max_indx - call fckit_log%info(message) - stop -end if - -! make a temporary copy of self -temp_self%nlocs = self%nlocs -temp_self%max_indx = self%max_indx -allocate(temp_self%lat(self%nlocs), temp_self%lon(self%nlocs), & - temp_self%time(self%nlocs), temp_self%indx(self%nlocs)) - -temp_self%lat(:) = self%lat(:) -temp_self%lon(:) = self%lon(:) -temp_self%indx(:) = self%indx(:) -do n = 1, self%nlocs - temp_self%time(n) = self%time(n) -end do - -! make a temporary copy of other -temp_other%nlocs = other%nlocs -temp_other%max_indx = other%max_indx -allocate(temp_other%lat(other%nlocs), temp_other%lon(other%nlocs), & - temp_other%time(other%nlocs), temp_other%indx(other%nlocs)) - -temp_other%lat(:) = other%lat(:) -temp_other%lon(:) = other%lon(:) -temp_other%indx(:) = other%indx(:) -do n = 1, other%nlocs - temp_other%time(n) = other%time(n) -end do - -! deallocate self -call ufo_locs_delete(self) - -! reallocate self with combined concatenation -self%nlocs = temp_self%nlocs + temp_other%nlocs -allocate(self%lat(self%nlocs), self%lon(self%nlocs), & - self%time(self%nlocs), self%indx(self%nlocs)) - -self%lat(1:temp_self%nlocs) = temp_self%lat(1:temp_self%nlocs) -self%lat(temp_self%nlocs+1:) = temp_other%lat(1:temp_other%nlocs) - -self%lon(1:temp_self%nlocs) = temp_self%lon(1:temp_self%nlocs) -self%lon(temp_self%nlocs+1:) = temp_other%lon(1:temp_other%nlocs) - -do n = 1, temp_self%nlocs - self%time(n) = temp_self%time(n) -end do -do n = 1, temp_other%nlocs - self%time(temp_self%nlocs + n) = temp_other%time(n) -end do - -self%indx(1:temp_self%nlocs) = temp_self%indx(1:temp_self%nlocs) -do n = 1, temp_other%nlocs - self%indx(temp_self%nlocs + n) = temp_other%indx(n) + & - temp_self%max_indx -end do -self%max_indx = temp_self%max_indx + temp_other%max_indx - -call ufo_locs_delete(temp_other) -call ufo_locs_delete(temp_self) - -end subroutine ufo_locs_concatenate - - -! -------------------------------------------------------------------------------------------------- - -subroutine ufo_locs_init(self, obss, t1, t2) - - implicit none - - type(ufo_locs), intent(inout) :: self - type(c_ptr), value, intent(in) :: obss - type(datetime), intent(in) :: t1, t2 - - integer :: nlocs - - character(len=*),parameter:: & - myname = "ufo_locs_init" - integer :: i - integer :: tw_nlocs - integer, dimension(:), allocatable :: tw_indx - real(kind_real), dimension(:), allocatable :: lon, lat - type(datetime), dimension(:), allocatable :: date_time - - ! Local copies pre binning - nlocs = obsspace_get_nlocs(obss) - - allocate(date_time(nlocs), lon(nlocs), lat(nlocs)) - - call obsspace_get_db(obss, "MetaData", "datetime", date_time) - - ! Generate the timing window indices - allocate(tw_indx(nlocs)) - tw_nlocs = 0 - do i = 1, nlocs - if (date_time(i) > t1 .and. date_time(i) <= t2) then - tw_nlocs = tw_nlocs + 1 - tw_indx(tw_nlocs) = i - endif - enddo - - call obsspace_get_db(obss, "MetaData", "longitude", lon) - call obsspace_get_db(obss, "MetaData", "latitude", lat) - - !Setup ufo locations - call ufo_locs_setup(self, tw_nlocs) - do i = 1, tw_nlocs - self%lon(i) = lon(tw_indx(i)) - self%lat(i) = lat(tw_indx(i)) - self%time(i) = date_time(tw_indx(i)) - enddo - self%indx = tw_indx(1:tw_nlocs) - - do i = 1, nlocs - call datetime_delete(date_time(i)) - enddo - deallocate(date_time, lon, lat, tw_indx) - - self%max_indx = obsspace_get_gnlocs(obss) - - -end subroutine ufo_locs_init - -! -------------------------------------------------------------------------------------------------- - -subroutine ufo_locs_time_mask(self, t1, t2, time_mask) - -type(ufo_locs), intent(in) :: self -type(datetime), intent(in) :: t1 -type(datetime), intent(in) :: t2 -logical, allocatable, intent(inout) :: time_mask(:) - -! Locals -integer :: n - -! Return a mask that is true where the location times are between t1 and t2 - -! Check for sensible inputs -if (t1>t2) call abor1_ftn("ufo_locs_mod.ufo_locs_time_mask t2 is not greater than or equal to t1") - -! Allocate the array to output -if (.not.allocated(time_mask)) allocate(time_mask(self%nlocs)) -time_mask = .false. - -! Loop over times and check if between two times -do n = 1, self%nlocs - - ! Check if in the time range - if (self%time(n) > t1 .and. self%time(n) <= t2 ) then - time_mask(n) = .true. - endif - -enddo - -end subroutine ufo_locs_time_mask - -! -------------------------------------------------------------------------------------------------- - -end module ufo_locs_mod diff --git a/src/ufo/ufo_variables_mod.F90 b/src/ufo/ufo_variables_mod.F90 index f89f05452..947021deb 100644 --- a/src/ufo/ufo_variables_mod.F90 +++ b/src/ufo/ufo_variables_mod.F90 @@ -14,7 +14,7 @@ module ufo_vars_mod integer, parameter, public :: n_aerosols_gocart_default=14,& &n_aerosols_gocart_merra_2=15,n_aerosols_other=1 -integer, parameter, public :: MAXVARLEN=60 +integer, parameter, public :: MAXVARLEN=100 character(len=MAXVARLEN), public, parameter :: var_tv = "virtual_temperature" character(len=MAXVARLEN), public, parameter :: var_ts = "air_temperature" character(len=MAXVARLEN), public, parameter :: var_t = "temperature" @@ -27,6 +27,7 @@ module ufo_vars_mod character(len=MAXVARLEN), public, parameter :: var_delp = "air_pressure_thickness" character(len=MAXVARLEN), public, parameter :: var_ps = "surface_pressure" character(len=MAXVARLEN), public, parameter :: var_z = "geopotential_height" +character(len=MAXVARLEN), public, parameter :: var_zm = "geometric_height" character(len=MAXVARLEN), public, parameter :: var_zi = "geopotential_height_levels" character(len=MAXVARLEN), public, parameter :: var_sfc_z= "surface_geopotential_height" character(len=MAXVARLEN), public, parameter :: var_oz = "mole_fraction_of_ozone_in_air" @@ -60,6 +61,10 @@ module ufo_vars_mod character(len=MAXVARLEN), public, parameter :: var_sfc_vegfrac = "vegetation_area_fraction" character(len=MAXVARLEN), public, parameter :: var_sfc_wspeed = "surface_wind_speed" character(len=MAXVARLEN), public, parameter :: var_sfc_wdir = "surface_wind_from_direction" +character(len=MAXVARLEN), public, parameter :: var_sfc_u10 = "uwind_at_10m" +character(len=MAXVARLEN), public, parameter :: var_sfc_v10 = "vwind_at_10m" +character(len=MAXVARLEN), public, parameter :: var_sfc_u = "surface_eastward_wind" +character(len=MAXVARLEN), public, parameter :: var_sfc_v = "surface_northward_wind" character(len=MAXVARLEN), public, parameter :: var_sfc_lai = "leaf_area_index" character(len=MAXVARLEN), public, parameter :: var_sfc_soilm = "volume_fraction_of_condensed_water_in_soil" character(len=MAXVARLEN), public, parameter :: var_sfc_soilt = "soil_temperature" @@ -77,62 +82,23 @@ module ufo_vars_mod character(len=MAXVARLEN), public, parameter :: var_radiance = "toa_outgoing_radiance_per_unit_wavenumber" character(len=MAXVARLEN), public, parameter :: var_tb = "brightness_temperature" character(len=MAXVARLEN), public, parameter :: var_tb_clr = "brightness_temperature_assuming_clear_sky" +character(len=MAXVARLEN), public, parameter :: var_total_transmit= "toa_total_transmittance" character(len=MAXVARLEN), public, parameter :: var_lvl_transmit= "transmittances_of_atmosphere_layer" character(len=MAXVARLEN), public, parameter :: var_lvl_weightfunc= "weightingfunction_of_atmosphere_layer" character(len=MAXVARLEN), public, parameter :: var_pmaxlev_weightfunc= "pressure_level_at_peak_of_weightingfunction" character(len=MAXVARLEN), public, parameter :: var_tsavg5 = "average_surface_temperature_within_field_of_view" +character(len=MAXVARLEN), public, parameter :: var_sea_fric_vel = "friction_velocity_over_water" +character(len=MAXVARLEN), public, parameter :: var_obk_length = "obukhov_length" character(len=MAXVARLEN), public, parameter :: var_refl = "equivalent_reflectivity_factor" character(len=MAXVARLEN), public, parameter :: var_w = "upward_air_velocity" -!@mzp strings have to be same MAXVARLEN length for array constructor character(len=MAXVARLEN), public, parameter :: var_rh = "relative_humidity" ! dimensionless (0 <= RH <= 1) character(len=MAXVARLEN), public, parameter :: var_water_type_rttov = "water_type" ! 0 (fresh), 1 (sea) character(len=MAXVARLEN), public, parameter :: var_surf_type_rttov = "surface_type" ! 0 (land), 1 (water), 2 (sea-ice) - - -character(len=MAXVARLEN), dimension(n_aerosols_gocart_default), public, parameter :: & - &var_aerosols_gocart_default = [& - &"sulf ",& - &"bc1 ",& - &"bc2 ",& - &"oc1 ",& - &"oc2 ",& - &"dust1 ",& - &"dust2 ",& - &"dust3 ",& - &"dust4 ",& - &"dust5 ",& - &"seas1 ",& - &"seas2 ",& - &"seas3 ",& - &"seas4 "] -!@mzp var_aerosols_gocart_merra_2 =[& -! &var_aerosols_gocart_default,& -! &"p25 "] -! won't compile -character(len=maxvarlen), dimension(n_aerosols_gocart_merra_2), public, parameter :: & - &var_aerosols_gocart_merra_2 = [& - &"sulf ",& - &"bc1 ",& - &"bc2 ",& - &"oc1 ",& - &"oc2 ",& - &"dust1 ",& - &"dust2 ",& - &"dust3 ",& - &"dust4 ",& - &"dust5 ",& - &"seas1 ",& - &"seas2 ",& - &"seas3 ",& - &"seas4 ",& - &"seas5 "] - -character(len=MAXVARLEN), dimension(n_aerosols_other), public, parameter :: & - &var_aerosols_other = [& - &"other "] +character(len=MAXVARLEN), public, parameter :: var_sfc_landmask = "landmask" ! 0 (sea), 1 (land) +character(len=MAXVARLEN), public, parameter :: var_sfc_seaicefrac = "seaice_fraction" character(len=MAXVARLEN), public :: var_seaicefrac = "sea_ice_category_area_fraction" character(len=MAXVARLEN), public :: var_seaicethick = "sea_ice_category_thickness" @@ -152,7 +118,6 @@ module ufo_vars_mod character(len=MAXVARLEN), public :: var_latent_heat = "upward_latent_heat_flux_in_air" character(len=MAXVARLEN), public :: var_sens_heat = "upward_sensible_heat_flux_in_air" character(len=MAXVARLEN), public :: var_lw_rad = "net_downwelling_longwave_radiation" -character(len=MAXVARLEN), public :: var_sea_fric_vel = "friction_velocity_over_water" character(len=MAXVARLEN), public, parameter :: var_du001 = "mass_fraction_of_dust001_in_air" character(len=MAXVARLEN), public, parameter :: var_du002 = "mass_fraction_of_dust002_in_air" @@ -168,11 +133,32 @@ module ufo_vars_mod character(len=MAXVARLEN), public, parameter :: var_bcphilic = "mass_fraction_of_hydrophilic_black_carbon_in_air" character(len=MAXVARLEN), public, parameter :: var_ocphobic = "mass_fraction_of_hydrophobic_organic_carbon_in_air" character(len=MAXVARLEN), public, parameter :: var_ocphilic = "mass_fraction_of_hydrophilic_organic_carbon_in_air" -character(len=MAXVARLEN), public, parameter :: var_sulfate = "mass_fraction_of_sulfate_aerosols_in_air" +character(len=MAXVARLEN), public, parameter :: var_sulfate = "mass_fraction_of_sulfate_in_air" character(len=MAXVARLEN), public, parameter :: var_no3an1 = "mass_fraction_of_nitrate001_in_air" character(len=MAXVARLEN), public, parameter :: var_no3an2 = "mass_fraction_of_nitrate002_in_air" character(len=MAXVARLEN), public, parameter :: var_no3an3 = "mass_fraction_of_nitrate003_in_air" +character(len=MAXVARLEN), public, parameter :: var_ext1 = "volume_extinction_in_air_due_to_aerosol_particles_lambda1" +character(len=MAXVARLEN), public, parameter :: var_ext2 = "volume_extinction_in_air_due_to_aerosol_particles_lambda2" +character(len=MAXVARLEN), public, parameter :: var_ext3 = "volume_extinction_in_air_due_to_aerosol_particles_lambda3" +character(len=MAXVARLEN), public, parameter :: var_airdens = "moist_air_density" +character(len=MAXVARLEN), dimension(n_aerosols_gocart_default), public, parameter :: & + &var_aerosols_gocart_default = [& + &var_sulfate,& + &var_bcphobic, var_bcphilic, var_ocphobic, var_ocphilic,& + &var_du001, var_du002, var_du003, var_du004, var_du005,& + &var_ss001, var_ss002, var_ss003, var_ss004] + +character(len=maxvarlen), dimension(n_aerosols_gocart_merra_2), public, parameter :: & + &var_aerosols_gocart_merra_2 = [& + &var_sulfate,& + &var_bcphobic, var_bcphilic, var_ocphobic, var_ocphilic,& + &var_du001, var_du002, var_du003, var_du004, var_du005,& + &var_ss001, var_ss002, var_ss003, var_ss004, var_ss005] + +character(len=MAXVARLEN), dimension(n_aerosols_other), public, parameter :: & + &var_aerosols_other = [& + &"other "] ! ------------------------------------------------------------------------------ contains @@ -201,6 +187,7 @@ end subroutine ufo_vars_read ! ------------------------------------------------------------------------------ integer function ufo_vars_getindex(vars, varname) +use ufo_utils_mod, only: cmp_strings implicit none character(len=*), intent(in) :: vars(:) character(len=*), intent(in) :: varname @@ -210,7 +197,7 @@ integer function ufo_vars_getindex(vars, varname) ufo_vars_getindex = -1 do ivar = 1, size(vars) - if (trim(vars(ivar)) == trim(varname)) then + if (cmp_strings(vars(ivar), varname)) then ufo_vars_getindex = ivar exit endif diff --git a/src/ufo/utils/CMakeLists.txt b/src/ufo/utils/CMakeLists.txt index 24e33c1e6..8bb41dd42 100644 --- a/src/ufo/utils/CMakeLists.txt +++ b/src/ufo/utils/CMakeLists.txt @@ -6,27 +6,53 @@ set ( utils_files ArrowProxy.h Constants.h + dataextractor/DataExtractor.h + dataextractor/DataExtractor.cc + dataextractor/DataExtractorBackend.h + dataextractor/DataExtractorCSVBackend.h + dataextractor/DataExtractorCSVBackend.cc + dataextractor/DataExtractorInput.h + dataextractor/DataExtractorNetCDFBackend.h + dataextractor/DataExtractorNetCDFBackend.cc DistanceCalculator.h EquispacedBinSelector.h GeodesicDistanceCalculator.h + IodaGroupIndices.cc + IodaGroupIndices.h MaxNormDistanceCalculator.h + metoffice/MetOfficeBMatrixStatic.cc + metoffice/MetOfficeBMatrixStatic.h + metoffice/MetOfficeBMatrixStatic.interface.h + metoffice/MetOfficeBMatrixStatic.interface.F90 metoffice/MetOfficeQCFlags.h + metoffice/MetOfficeRMatrixRadiance.cc + metoffice/MetOfficeRMatrixRadiance.h + metoffice/MetOfficeRMatrixRadiance.interface.h + metoffice/MetOfficeRMatrixRadiance.interface.F90 metoffice/MetOfficeObservationIDs.h + metoffice/ufo_metoffice_bmatrixstatic_mod.f90 + metoffice/ufo_metoffice_rmatrixradiance_mod.f90 + OperatorUtils.cc + OperatorUtils.h + parameters/ParameterTraitsVariable.cc parameters/ParameterTraitsVariable.h PiecewiseLinearInterpolation.cc PiecewiseLinearInterpolation.h - ParallelObsDistribution.cc - ParallelObsDistribution.h + PrimitiveVariables.cc + PrimitiveVariables.h ProbabilityOfGrossError.cc ProbabilityOfGrossError.h ProbabilityOfGrossErrorParameters.h RecursiveSplitter.cc RecursiveSplitter.h + RefractivityCalculator.F90 SpatialBinSelector.h SpatialBinSelector.cc StringUtils.cc StringUtils.h ufo_utils_mod.F90 + ufo_utils.interface.F90 + ufo_utils.interface.h VertInterp.interface.F90 VertInterp.interface.h vert_interp.F90 diff --git a/src/ufo/utils/Constants.h b/src/ufo/utils/Constants.h index 0731f0567..56ee7bfc2 100644 --- a/src/ufo/utils/Constants.h +++ b/src/ufo/utils/Constants.h @@ -10,61 +10,84 @@ #include -//------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------- namespace ufo { -//------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------- /// Some useful constants struct Constants { - static constexpr double deg2rad = M_PI / 180.; - static constexpr double rad2deg = 180. * M_1_PI; - static constexpr double grav = 9.80665e+0; - static constexpr double t0c = 2.7315e+2; // temperature at zero celsius (K) - static constexpr double ttp = 2.7316e+2; // temperature at h2o triple point (K) - static constexpr double rd = 2.8705e2; - static constexpr double rv = 4.6150e2; - static constexpr double cp = 1.0046e3; // heat capacity at constant pressure - // for air - static constexpr double cv = 7.1760e2; // heat capacity at constant volume - // for air - static constexpr double pref = 1.0e5; // Reference pressure for calculating - // exner - static constexpr double rd_over_rv = rd/rv; - static constexpr double rd_over_cp = rd/cp; - static constexpr double cv_over_cp = cv/cp; - static constexpr double rv_over_rd = rv/rd; - static constexpr double rd_over_g = rd/grav; - static constexpr double mean_earth_rad = 6371.0; - static constexpr double zero = 0.0; - static constexpr double quarter = 0.25; - static constexpr double half = 0.5; - static constexpr double one = 1.0; - static constexpr double two = 2.0; - static constexpr double four = 4.0; - static constexpr double five = 5.0; - static constexpr double ten = 10.0; - static constexpr double k_t = 0.65; // Thermal conductivity of water - static constexpr double L_e = 2.26e+06; // Latent heat of vaporization - static constexpr double eps = 0.1; // Albedo of sea water - static constexpr double sig = 5.67e-6; // Stefan-Boltzmann constant - static constexpr double alpha = 2.7e-4; // Water thermal expansion coefficient - static constexpr double cw = 0.015; // Water specific heat - static constexpr double v_w = 0.8e-6; // Water kinematic viscosity - static constexpr double S_B = 0.026; - static constexpr double gr = 9.81; - static constexpr double Rou = 1000.0; - static constexpr double DU = 21.4e-6; // Dobson unit, kg O3/m**2 - static constexpr double Lclr = 0.0065; // constant lapse rate - static constexpr double t2tv = 0.608; // constant lapse rate - static constexpr double von_karman = 0.41; // Von Karman Constant - static constexpr double es_w_0 = 611.2; // saturation vapor pressure of water at - // 0degC - static constexpr double euzc_0 = 34.0; // constant for estimating euphotic layer - static constexpr double euzc_1 = -0.39; // constant for estimating euphotic layer + static constexpr double deg2rad = M_PI / 180.; + static constexpr double rad2deg = 180. * M_1_PI; + static constexpr double grav = 9.80665e+0; + static constexpr double t0c = 2.7315e+2; // temperature at zero celsius (K) + static constexpr double ttp = 2.7316e+2; // temperature at h2o triple point (K) + static constexpr double rd = 2.8705e2; + static constexpr double rv = 4.6150e2; + static constexpr double cp = 1.0046e3; // heat capacity at constant pressure + // for air + static constexpr double cv = 7.1760e2; // heat capacity at constant volume + // for air + static constexpr double pref = 1.0e5; // Reference pressure for calculating + // exner + static constexpr double rd_over_rv = rd/rv; + static constexpr double rd_over_cp = rd/cp; + static constexpr double cv_over_cp = cv/cp; + static constexpr double rv_over_rd = rv/rd; + static constexpr double rd_over_g = rd/grav; + static constexpr double g_over_rd = grav / rd; + static constexpr double mean_earth_rad = 6371.0; + static constexpr double zero = 0.0; + static constexpr double quarter = 0.25; + static constexpr double half = 0.5; + static constexpr double one = 1.0; + static constexpr double two = 2.0; + static constexpr double four = 4.0; + static constexpr double five = 5.0; + static constexpr double ten = 10.0; + static constexpr double k_t = 0.65; // Thermal conductivity of water + static constexpr double L_e = 2.26e+06; // Latent heat of vaporization + static constexpr double eps = 0.1; // Albedo of sea water + static constexpr double sig = 5.67e-6; // Stefan-Boltzmann constant + static constexpr double alpha = 2.7e-4; // Water thermal expansion coefficient + static constexpr double cw = 0.015; // Water specific heat + static constexpr double v_w = 0.8e-6; // Water kinematic viscosity + static constexpr double S_B = 0.026; + static constexpr double gr = 9.81; + static constexpr double Rou = 1000.0; + static constexpr double DU = 21.4e-6; // Dobson unit, kg O3/m**2 + static constexpr double Lclr = 0.0065; // constant lapse rate + static constexpr double t2tv = 0.608; // constant lapse rate + static constexpr double von_karman = 0.41; // Von Karman Constant + static constexpr double es_w_0 = 611.2; // saturation vapor pressure of water at + // 0degC + static constexpr double euzc_0 = 34.0; // constant for estimating euphotic layer + static constexpr double euzc_1 = -0.39; // constant for estimating euphotic layer + static constexpr double epsilon = 0.62198; // Ratio of molecular weight of + // water and dry air + + // International Civil Aviation Organization (ICAO) atmosphere. + // https://en.wikipedia.org/wiki/International_Standard_Atmosphere#ICAO_Standard_Atmosphere + // with gpm = height in geopotential metres + static constexpr double icao_lapse_rate_l = 6.5E-03; // Lapse rate for levels up + // to 11,000 [gpm] + // (isothermal layer) + static constexpr double icao_lapse_rate_u = -1.0E-03; // Lapse rate for levels above + // 20,000 [gpm] + static constexpr double icao_height_l = 11000.0; // Height limit for assumed + // lower lapse rate [m] + static constexpr double icao_height_u = 20000.0; // Height of top of isothermal + // layer [m] + static constexpr double icao_temp_surface = 288.15; // Surface temperature [K] + static constexpr double icao_temp_isothermal_layer = 216.65; // Temperature of isothermal + // layer [K] + static constexpr double icao_pressure_surface = 1013.25; // Assumed surface pressure + static constexpr double icao_pressure_l = 226.32; // Assumed pressure at 11,000 gpm + static constexpr double icao_pressure_u = 54.7487; // Assumed pressure at 20,000 gpm }; -//------------------------------------------------------------------------------------------------------ + +//-------------------------------------------------------------------------------------------------- } // namespace ufo #endif // UFO_UTILS_CONSTANTS_H_ diff --git a/src/ufo/utils/IodaGroupIndices.cc b/src/ufo/utils/IodaGroupIndices.cc new file mode 100644 index 000000000..1ea4962d1 --- /dev/null +++ b/src/ufo/utils/IodaGroupIndices.cc @@ -0,0 +1,92 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/utils/IodaGroupIndices.h" + +#include "eckit/exception/Exceptions.h" +#include "ioda/ObsGroup.h" +#include "oops/base/Variables.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +/// Returns indices of all elements in the range +/// [\p elements_to_look_for_begin, \p elements_to_look_for_end) in the \p all_elements vector, +/// in the same order that the former are in. +/// Throws an exception if at least one of elements looked for is missing +/// from \p all_elements. +template +std::vector getAllIndices(const std::vector & all_elements, + typename std::vector::const_iterator elements_to_look_for_begin, + typename std::vector::const_iterator elements_to_look_for_end) { + std::vector result; + for (typename std::vector::const_iterator sought_it = elements_to_look_for_begin; + sought_it != elements_to_look_for_end; ++sought_it) { + const T &sought = *sought_it; + const auto found_it = std::find(all_elements.begin(), all_elements.end(), sought); + if (found_it != all_elements.end()) { + result.push_back(std::distance(all_elements.begin(), found_it)); + } else { + const std::string errormsg = "getAllIndices: Can't find element in the vector"; + throw eckit::BadParameter(errormsg, Here()); + } + } + return result; +} + +// ----------------------------------------------------------------------------- +/// Returns indices of all elements in \p elements_to_look_for in the \p all_elements vector, +/// in the same order that \p elements_to_look_for are in. +/// Throws an exception if at least one of elements looked for is missing +/// from \p all_elements. +template +std::vector getAllIndices(const std::vector & all_elements, + const std::vector & elements_to_look_for) { + return getAllIndices(all_elements, elements_to_look_for.begin(), elements_to_look_for.end()); +} + +// ----------------------------------------------------------------------------- +std::vector getRequiredVariableIndices(const ioda::ObsGroup &obsgroup, + const std::string &varname, + typename std::vector::const_iterator elements_to_look_for_begin, + typename std::vector::const_iterator elements_to_look_for_end) { + ioda::Variable var = obsgroup.vars.open(varname); + std::vector elements_in_group; + var.read(elements_in_group); + return getAllIndices(elements_in_group, elements_to_look_for_begin, elements_to_look_for_end); +} + +// ----------------------------------------------------------------------------- + +std::vector getRequiredVarOrChannelIndices(const ioda::ObsGroup &obsgroup, + const oops::Variables &vars_to_look_for) { + if (vars_to_look_for.channels().empty()) { + // Read all variables from the file into std vector + ioda::Variable variablesvar = obsgroup.vars.open("variables"); + std::vector variables; + variablesvar.read(variables); + + // Find the indices of the ones we need + return getAllIndices(variables, vars_to_look_for.variables()); + } else { + // At present this function can search either for channel numbers or variable names, + // but not both. So make sure there's only one multi-channel variable. + ASSERT(vars_to_look_for.variables().size() == vars_to_look_for.channels().size()); + + // Read all channels from the file into std vector + ioda::Variable channelsvar = obsgroup.vars.open("channels"); + std::vector channels; + channelsvar.read(channels); + + // Find the indices of the ones we need + return getAllIndices(channels, vars_to_look_for.channels()); + } +} + +// ----------------------------------------------------------------------------- + +} // namespace ufo diff --git a/src/ufo/utils/IodaGroupIndices.h b/src/ufo/utils/IodaGroupIndices.h new file mode 100644 index 000000000..6a1a37507 --- /dev/null +++ b/src/ufo/utils/IodaGroupIndices.h @@ -0,0 +1,40 @@ +/* + * (C) Copyright 2021 UCAR + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_IODAGROUPINDICES_H_ +#define UFO_UTILS_IODAGROUPINDICES_H_ + +#include +#include + +namespace ioda { + class ObsGroup; +} + +namespace oops { + class Variables; +} + +namespace ufo { + +/// Return the vector of indices of all elements in the range +/// [\p elements_to_look_for_begin, \p elements_to_look_for_end) in the \p varname ioda::Variable +/// from \p obsgroup ioda::ObsGroup, in the same order that the former are in. +/// Throws an exception if at least one of elements looked for is missing. +std::vector getRequiredVariableIndices(const ioda::ObsGroup &obsgroup, + const std::string &varname, + typename std::vector::const_iterator elements_to_look_for_begin, + typename std::vector::const_iterator elements_to_look_for_end); + +/// Return the vector of indices of variables or channels \p vars_to_look_for in the +/// "variables" or "channels" ioda::Variable from \p obsgroup ioda::ObsGroup +std::vector getRequiredVarOrChannelIndices(const ioda::ObsGroup &obsgroup, + const oops::Variables &vars_to_look_for); + +} // namespace ufo + +#endif // UFO_UTILS_IODAGROUPINDICES_H_ diff --git a/src/ufo/utils/OperatorUtils.cc b/src/ufo/utils/OperatorUtils.cc new file mode 100644 index 000000000..f19991d90 --- /dev/null +++ b/src/ufo/utils/OperatorUtils.cc @@ -0,0 +1,43 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include + +#include "ioda/ObsSpace.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/OperatorUtils.h" + +namespace ufo { + +void getOperatorVariables(const eckit::Configuration &conf, + const oops::Variables &simulatedVariables, + oops::Variables &operatorVariables, + std::vector &operatorVariableIndices) { + if (conf.has("variables")) { + operatorVariables = + ufo::Variables(conf.getSubConfigurations("variables")).toOopsVariables(); + + std::map simulatedVariableIndices; + for (int i = 0; i < simulatedVariables.size(); ++i) + simulatedVariableIndices[simulatedVariables[i]] = i; + + for (int i = 0; i < operatorVariables.size(); ++i) { + auto it = simulatedVariableIndices.find(operatorVariables[i]); + if (it == simulatedVariableIndices.end()) + throw eckit::BadValue("Operator variable '" + operatorVariables[i] + + "' isn't one of the simulated variables", Here()); + operatorVariableIndices.push_back(it->second); + } + } else { + operatorVariables = simulatedVariables; + operatorVariableIndices.resize(operatorVariables.size()); + std::iota(operatorVariableIndices.begin(), operatorVariableIndices.end(), 0); + } +} + +} // namespace ufo diff --git a/src/ufo/utils/OperatorUtils.h b/src/ufo/utils/OperatorUtils.h new file mode 100644 index 000000000..a466662c7 --- /dev/null +++ b/src/ufo/utils/OperatorUtils.h @@ -0,0 +1,44 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_OPERATORUTILS_H_ +#define UFO_UTILS_OPERATORUTILS_H_ + +#include + +namespace eckit { +class Configuration; +} + +namespace oops { +class Variables; +} + +namespace ioda { +class ObsSpace; +} + +namespace ufo { + /// Fill the list of variables to be simulated by an obs operator. + /// + /// \param conf + /// Configuration of the obs operator. + /// \param simulatedVariables + /// List of all simulated variables in the obs space associated with the obs operator. + /// \param[out] operatorVariables + /// Set to the list of variables taken from the `variables` option in \p conf if this + /// option is present or to \p simulatedVariables if not. + /// \param[out] operatorVariableIndices + /// Indices of the elements of \p simulatedVariables corresponding to the variables in + /// \p operatorVariables. + void getOperatorVariables(const eckit::Configuration &conf, + const oops::Variables &simulatedVariables, + oops::Variables &operatorVariables, + std::vector &operatorVariableIndices); +} // namespace ufo + +#endif // UFO_UTILS_OPERATORUTILS_H_ diff --git a/src/ufo/utils/ParallelObsDistribution.cc b/src/ufo/utils/ParallelObsDistribution.cc deleted file mode 100644 index e61929061..000000000 --- a/src/ufo/utils/ParallelObsDistribution.cc +++ /dev/null @@ -1,72 +0,0 @@ -/* - * (C) Copyright 2020 Met Office UK - * - * This software is licensed under the terms of the Apache Licence Version 2.0 - * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - */ - -#include "ufo/utils/ParallelObsDistribution.h" -#include "oops/mpi/mpi.h" - -namespace ufo { - -ParallelObsDistribution::ParallelObsDistribution(const ioda::ObsSpace &obsspace) - : globalObsCount_(obsspace.gnlocs()) -{ - const size_t numProcs = obsspace.comm().size(); - - localObsCounts_.resize(numProcs); - obsspace.comm().allGather(static_cast(obsspace.nlocs()), - localObsCounts_.begin(), localObsCounts_.end()); - - localObsIdDisplacements_.resize(numProcs); - localObsIdDisplacements_[0] = 0; - for (size_t i = 1; i < numProcs; ++i) - localObsIdDisplacements_[i] = localObsIdDisplacements_[i - 1] + localObsCounts_[i - 1]; -} - -// Generic implementation -template -std::vector getGlobalVariableValues(const ioda::ObsSpace &obsspace, - const ParallelObsDistribution &obsDistribution, - const std::string &variable, - const std::string &group) { - std::vector localValues(obsspace.nlocs()); - obsspace.get_db(group, variable, localValues); - - std::vector globalValues(obsDistribution.globalObsCount()); - obsspace.comm().allGatherv(localValues.begin(), localValues.end(), - globalValues.begin(), - obsDistribution.localObsCounts().data(), - obsDistribution.localObsIdDisplacements().data()); - return globalValues; -} - -// Specialisation for date/time variables. -template <> -std::vector getGlobalVariableValues(const ioda::ObsSpace &obsspace, - const ParallelObsDistribution &obsDistribution, - const std::string &variable, - const std::string &group) { - std::vector localValues(obsspace.nlocs()); - obsspace.get_db(group, variable, localValues); - - std::vector globalValues(obsDistribution.globalObsCount()); - oops::mpi::allGathervUsingSerialize(obsspace.comm(), - localValues.begin(), localValues.end(), globalValues.begin()); - return globalValues; -} - -// Explicit instantiations for the variable types supported by ioda::ObsSpace. -#define INSTANTIATE_GET_GLOBAL_VARIABLE_VALUES(TYPE) \ - template std::vector getGlobalVariableValues(const ioda::ObsSpace &, \ - const ParallelObsDistribution &, \ - const std::string &, \ - const std::string &) -INSTANTIATE_GET_GLOBAL_VARIABLE_VALUES(int); -INSTANTIATE_GET_GLOBAL_VARIABLE_VALUES(float); -INSTANTIATE_GET_GLOBAL_VARIABLE_VALUES(double); -// It's unnecessary to instantiate the template function for T = util::DateTime, since a -// specialization exists for that value of T. - -} // namespace ufo diff --git a/src/ufo/utils/ParallelObsDistribution.h b/src/ufo/utils/ParallelObsDistribution.h deleted file mode 100644 index 3ba8721dc..000000000 --- a/src/ufo/utils/ParallelObsDistribution.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * (C) Copyright 2020 Met Office UK - * - * This software is licensed under the terms of the Apache Licence Version 2.0 - * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - */ - -#ifndef UFO_UTILS_PARALLELOBSDISTRIBUTION_H_ -#define UFO_UTILS_PARALLELOBSDISTRIBUTION_H_ - -#include -#include - -#include "ioda/ObsSpace.h" - -namespace ufo { - -/// \brief Describes how observations in an ObsSpace are distributed across MPI processes. -class ParallelObsDistribution { - public: - /// \brief Construct an object describing the distribution of observations in \p obsspace across - /// MPI processes. - explicit ParallelObsDistribution(const ioda::ObsSpace &obsspace); - - /// \brief Return the total number of observations held by all MPI processes. - size_t globalObsCount() const { return globalObsCount_; } - - /// \brief Return a vector whose ith element is the number of observations held by the MPI process - /// with rank i. - /// - /// The returned vector can be passed to the \c recvcounts parameter of - /// eckit::mpi::Comm::allGatherv(). - const std::vector &localObsCounts() const { return localObsCounts_; } - - /// \brief Return a vector whose ith element is the total number of observations held by the MPI - /// processes with ranks less than i. - /// - /// The returned vector can be passed to the \c recvcounts parameter of - /// eckit::mpi::Comm::displs(). - const std::vector &localObsIdDisplacements() const { return localObsIdDisplacements_; } - - private: - size_t globalObsCount_; - std::vector localObsCounts_; - std::vector localObsIdDisplacements_; -}; - -/// \brief Return a vector containing the values of variable \c variable@group for all observations -/// held by process 0, then all observations held by process 1 etc. -/// -/// \tparam T -/// Type of the variable values. Must be int, float, double or util::DateTime, -/// otherwise a linking error will occur. -/// -/// \related ParallelObsDistribution -template -std::vector getGlobalVariableValues(const ioda::ObsSpace &obsspace, - const ParallelObsDistribution &obsDistribution, - const std::string &variable, - const std::string &group); - -} // namespace ufo - -#endif // UFO_UTILS_PARALLELOBSDISTRIBUTION_H_ diff --git a/src/ufo/utils/PrimitiveVariables.cc b/src/ufo/utils/PrimitiveVariables.cc new file mode 100644 index 000000000..8262607a1 --- /dev/null +++ b/src/ufo/utils/PrimitiveVariables.cc @@ -0,0 +1,27 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/ObsFilterData.h" +#include "ufo/utils/PrimitiveVariables.h" + +namespace ufo { + +void PrimitiveVariablesIterator::loadCurrentVariable() { + if (variableIndex_ < variables_.size()) { + const Variable &variable = variables_[variableIndex_]; + vector_.reset(new ioda::ObsDataVector(data_.obsspace(), variable.toOopsVariables())); + if (variable.group() == "ObsFunction") { + data_.get(variable, *vector_); + } else { + for (size_t i = 0; i < variable.size(); ++i) { + data_.get(variable[i], (*vector_)[i]); + } + } + } +} + +} // namespace ufo diff --git a/src/ufo/utils/PrimitiveVariables.h b/src/ufo/utils/PrimitiveVariables.h new file mode 100644 index 000000000..7fd555593 --- /dev/null +++ b/src/ufo/utils/PrimitiveVariables.h @@ -0,0 +1,168 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_PRIMITIVEVARIABLES_H_ +#define UFO_UTILS_PRIMITIVEVARIABLES_H_ + +#include +#include +#include + +#include "ioda/ObsDataVector.h" +#include "ufo/filters/Variable.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/ArrowProxy.h" + +namespace ioda { + template class ObsDataVector; + class ObsSpace; +} + +namespace ufo { + +class ObsFilterData; + +/// A proxy object providing access to the name, group and values of a "primitive", i.e. +/// single-channel, variable pointed to by a PrimitiveVariablesIterator. +/// +/// \see PrimitiveVariables for an example of its use. +/// +/// \warning Never use a PrimitiveVariable object obtained by dereferencing a +/// PrimitiveVariablesIterator after incrementing this iterator or when the iterator has gone out +/// of scope. +class PrimitiveVariable { + public: + PrimitiveVariable(const Variable &variable, size_t channelIndex, + const std::vector &values) + : variable_(variable), channelIndex_(channelIndex), values_(values) + {} + + /// Return the name of the primitive variable. + std::string name() const { return variable_.variable(channelIndex_); } + /// Return the group of the primitive variable. + const std::string &group() const { return variable_.group(); } + /// Return a Variable object representing the primitive variable. + Variable variable() const { return variable_[channelIndex_]; } + /// Return the values of the primitive variable at all observation locations. + const std::vector &values() const { return values_; } + + private: + const Variable &variable_; + size_t channelIndex_; + const std::vector &values_; +}; + +/// \brief Iterator over the names and values of primitive variables held in a Variables object. +/// +/// \see PrimitiveVariables +/// +/// Note: this iterator exists to support range-based for loops, but it doesn't have a copy +/// constructor (it would need to be very costly -- requiring a copy of the ObsDataVector stored +/// in the iterator), so it can't be passed to STL algorithms that take iterators by value. +class PrimitiveVariablesIterator { + public: + typedef ptrdiff_t difference_type; + typedef PrimitiveVariable value_type; + typedef PrimitiveVariable reference; + typedef ArrowProxy pointer; + typedef std::forward_iterator_tag iterator_category; + + struct BeginTag {}; + struct EndTag {}; + + /// \brief Create an iterator pointing to the first primitive variable in \p variables. + PrimitiveVariablesIterator(const Variables &variables, const ObsFilterData &data, BeginTag) + : variables_(variables), data_(data), variableIndex_(0), channelIndex_(0) + { + loadCurrentVariable(); + } + + /// \brief Create an iterator pointing past the range of primitive variables in \p variables. + PrimitiveVariablesIterator(const Variables &variables, const ObsFilterData &data, EndTag) + : variables_(variables), data_(data), variableIndex_(variables_.size()), channelIndex_(0) + {} + + /// \brief Dereference the iterator, returning a proxy object whose methods such as name(), + /// group() and values() can be called to get access to the name, group and values of the + /// primitive variable pointed to by the iterator. + PrimitiveVariable operator*() const { + return PrimitiveVariable(variables_[variableIndex_], channelIndex_, (*vector_)[channelIndex_]); + } + + ArrowProxy operator->() const { + return ArrowProxy(operator*()); + } + + PrimitiveVariablesIterator& operator++() { + if (variableIndex_ < variables_.size()) { + ++channelIndex_; + if (channelIndex_ == variables_[variableIndex_].size()) { + ++variableIndex_; + channelIndex_ = 0; + loadCurrentVariable(); + } + } + return *this; + } + + bool operator==(const PrimitiveVariablesIterator& other) const { + // In principle we should also check variables_ and data_ for equality. + // We don't do it for efficiency. + return variableIndex_ == other.variableIndex_; + return channelIndex_ == other.channelIndex_; + } + + bool operator!=(const PrimitiveVariablesIterator& other) const { + return !operator==(other); + } + + private: + void loadCurrentVariable(); + + private: + const Variables &variables_; + const ObsFilterData &data_; + size_t variableIndex_; + size_t channelIndex_; + std::unique_ptr> vector_; +}; + +/// \brief A range covering all "primitive" (single-channel) variables in a Variables object. +/// +/// It makes it possible to iterate over all these primitive variables using a range-based for loop. +/// Each iteration has access to the name and group of a single variable as well as the vector of +/// its values at all observation locations. For example: +/// +/// Variables vars = ...; +/// ObsFilterData data = ...; // within a filter, we'd typically use the data_ member variable +/// for (PrimitiveVariable var : PrimitiveVariables(vars, data)) { +/// // Access to the name of the current primitive variable: var.name() +/// // Access to its group: var.group() +/// // Access to its values (a vector of floats): var.values() +/// } +class PrimitiveVariables { + public: + PrimitiveVariables(const Variables &variables, const ObsFilterData &data) + : variables_(variables), data_(data) + {} + + PrimitiveVariablesIterator begin() const { + return PrimitiveVariablesIterator(variables_, data_, PrimitiveVariablesIterator::BeginTag()); + } + + PrimitiveVariablesIterator end() const { + return PrimitiveVariablesIterator(variables_, data_, PrimitiveVariablesIterator::EndTag()); + } + + private: + const Variables &variables_; + const ObsFilterData &data_; +}; + +} // namespace ufo + +#endif // UFO_UTILS_PRIMITIVEVARIABLES_H_ diff --git a/src/ufo/utils/ProbabilityOfGrossError.cc b/src/ufo/utils/ProbabilityOfGrossError.cc index be0f1f4c0..2d917a6c3 100644 --- a/src/ufo/utils/ProbabilityOfGrossError.cc +++ b/src/ufo/utils/ProbabilityOfGrossError.cc @@ -39,8 +39,8 @@ namespace ufo { const double SDiffCrit = obsVal2 && bkgVal2 ? options.PGE_SDiffCrit.value() * 2.0 : options.PGE_SDiffCrit.value(); - // Number of levels in profile. - const size_t numLevelsToCheck = obsVal.size(); + // Number of levels in profile, or total number of single-level obs. + const size_t numLocs = obsVal.size(); // Initialise buddy check PGE to missing data indicator. PGEBd.assign(obsVal.size(), PGEMDI); @@ -57,12 +57,12 @@ namespace ufo { const bool obsErrEmpty = obsErr.empty(); const bool bkgErrEmpty = bkgErr.empty(); - for (size_t jlev = 0; jlev < numLevelsToCheck; ++jlev) { + for (size_t jloc = 0; jloc < numLocs; ++jloc) { // Calculate combined error variance. if (!obsErrEmpty && !bkgErrEmpty && - obsErr[jlev] >= 0 && bkgErr[jlev] >= 0) { - ErrVar = std::pow(ObErrMult * obsErr[jlev], 2) + - std::pow(BkgErrMult * bkgErr[jlev], 2); + obsErr[jloc] >= 0 && bkgErr[jloc] >= 0) { + ErrVar = std::pow(ObErrMult * obsErr[jloc], 2) + + std::pow(BkgErrMult * bkgErr[jloc], 2); } else { ErrVar = missingValueFloat; } @@ -72,31 +72,31 @@ namespace ufo { } // Update PGE (if all of the required values are present). - if (obsVal[jlev] != missingValueFloat && - bkgVal[jlev] != missingValueFloat && + if (obsVal[jloc] != missingValueFloat && + bkgVal[jloc] != missingValueFloat && ErrVar != missingValueFloat) { if (obsVal2 && bkgVal2 && - (*obsVal2)[jlev] != missingValueFloat && - (*bkgVal2)[jlev] != missingValueFloat) { // Vector observable. - SDiff = (std::pow(obsVal[jlev] - bkgVal[jlev], 2) + - std::pow((*obsVal2)[jlev] - (*bkgVal2)[jlev], 2)) / static_cast(ErrVar); + (*obsVal2)[jloc] != missingValueFloat && + (*bkgVal2)[jloc] != missingValueFloat) { // Vector observable. + SDiff = (std::pow(obsVal[jloc] - bkgVal[jloc], 2) + + std::pow((*obsVal2)[jloc] - (*bkgVal2)[jloc], 2)) / static_cast(ErrVar); // Bivariate normal distribution; square root does not appear in denominator. PdGood = std::exp(-0.5 * std::min(SDiff, 2.0 * ExpArgMax)) / (2.0 * M_PI * ErrVar); } else { // Scalar observable. - SDiff = std::pow(obsVal[jlev] - bkgVal[jlev], 2) / ErrVar; + SDiff = std::pow(obsVal[jloc] - bkgVal[jloc], 2) / ErrVar; // Univariate normal distribution; square root appears in denominator. PdGood = std::exp(-0.5 * std::min(SDiff, 2.0 * ExpArgMax)) / std::sqrt(2.0 * M_PI * ErrVar); } // Calculate PGE after background check, normalising appropriately. - PGEBk = (PdBad[jlev] * PGE[jlev]) / - (PdBad[jlev] * PGE[jlev] + PdGood * (1.0 - PGE[jlev])); + PGEBk = (PdBad[jloc] * PGE[jloc]) / + (PdBad[jloc] * PGE[jloc] + PdGood * (1.0 - PGE[jloc])); // Set QC flags. - flags[jlev] |= ufo::MetOfficeQCFlags::Elem::BackPerfFlag; + flags[jloc] |= ufo::MetOfficeQCFlags::Elem::BackPerfFlag; if (PGEBk >= PGECrit) { - flags[jlev] |= ufo::MetOfficeQCFlags::Elem::BackRejectFlag; + flags[jloc] |= ufo::MetOfficeQCFlags::Elem::BackRejectFlag; } } else { // Deal with missing data. @@ -105,19 +105,19 @@ namespace ufo { } // Pack PGEs for use in later routines. - PGE[jlev] = trunc(PGEBk * PGEMult) + PGE[jlev]; + PGE[jloc] = trunc(PGEBk * PGEMult) + PGE[jloc]; // Model-level data may have additional processing. if (ModelLevels && (SDiff >= SDiffCrit || - flags[jlev] & ufo::MetOfficeQCFlags::Elem::PermRejectFlag || - flags[jlev] & ufo::MetOfficeQCFlags::Elem::FinalRejectFlag)) { + flags[jloc] & ufo::MetOfficeQCFlags::Elem::PermRejectFlag || + flags[jloc] & ufo::MetOfficeQCFlags::Elem::FinalRejectFlag)) { PGEBk = PGEMDI; // Do not apply buddy check in this case. - flags[jlev] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; + flags[jloc] |= ufo::MetOfficeQCFlags::Elem::FinalRejectFlag; } // Update PGE for buddy check. - PGEBd[jlev] = PGEBk; + PGEBd[jloc] = PGEBk; } } } // namespace ufo diff --git a/src/ufo/utils/ProbabilityOfGrossError.h b/src/ufo/utils/ProbabilityOfGrossError.h index 8e17f43f4..4f86fc547 100644 --- a/src/ufo/utils/ProbabilityOfGrossError.h +++ b/src/ufo/utils/ProbabilityOfGrossError.h @@ -18,8 +18,9 @@ #include "ufo/utils/ProbabilityOfGrossErrorParameters.h" namespace ufo { - /// \brief Bayesian update of probability of gross error (PGE) for - /// scalar (1D) or vector (2D) observables. + /// \brief Bayesian update of probability of gross error (PGE) + /// \details Update PGE across locations according to (obsVal-bkgVal), obsErr, and bkgErr, + /// and update flags to say BG check performed, and whether obs rejected or not. /// This routine can process both single observations and observations on profile levels. /// In the vector case, the variance is assumed to be isotropic /// and obsErr and bkgErr give the error in a single vector component. @@ -37,6 +38,7 @@ namespace ufo { /// \param[in] ErrVarMax: (Optional) Maximum error variance. /// \param[in] obsVal2: (Optional) Second component of 2D observation values. /// \param[in] bkgVal2: (Optional) Second component of 2D background values. + void BayesianPGEUpdate(const ProbabilityOfGrossErrorParameters &options, const std::vector &obsVal, const std::vector &obsErr, diff --git a/src/ufo/utils/ProbabilityOfGrossErrorParameters.h b/src/ufo/utils/ProbabilityOfGrossErrorParameters.h index 88c1260d8..044705010 100644 --- a/src/ufo/utils/ProbabilityOfGrossErrorParameters.h +++ b/src/ufo/utils/ProbabilityOfGrossErrorParameters.h @@ -27,19 +27,19 @@ namespace ufo { public: // variables /// Maximum value of exponent in background QC. - oops::Parameter PGE_ExpArgMax{"PGE_ExpArgMax", 80.0, this}; + oops::Parameter PGE_ExpArgMax{"max exponent", 80.0, this}; /// PGE rejection limit. - oops::Parameter PGE_PGECrit{"PGE_PGECrit", 0.1, this}; + oops::Parameter PGE_PGECrit{"PGE threshold", 0.1, this}; /// Multiplication factor for observation errors. - oops::Parameter PGE_ObErrMult{"PGE_ObErrMult", 1.0, this}; + oops::Parameter PGE_ObErrMult{"obs error multiplier", 1.0, this}; /// Multiplication factor for background errors. - oops::Parameter PGE_BkgErrMult{"PGE_BkgErrMult", 1.0, this}; + oops::Parameter PGE_BkgErrMult{"BG error multiplier", 1.0, this}; /// Critical value for squared difference from background / ErrVar. - oops::Parameter PGE_SDiffCrit{"PGE_SDiffCrit", 100.0, this}; + oops::Parameter PGE_SDiffCrit{"obs minus BG threshold", 100.0, this}; }; } // namespace ufo diff --git a/src/ufo/utils/RecursiveSplitter.cc b/src/ufo/utils/RecursiveSplitter.cc index e0a1afb07..a364ef910 100644 --- a/src/ufo/utils/RecursiveSplitter.cc +++ b/src/ufo/utils/RecursiveSplitter.cc @@ -74,16 +74,6 @@ void RecursiveSplitter::groupByImpl(const std::vector &categories) { encodedGroups_[lastIndexInLastGroup + 1] = numIds; } -void RecursiveSplitter::shuffleGroups(unsigned int seed) { - for (Group group : multiElementGroups()) { - std::vector::iterator nonConstGroupBegin = - orderedIds_.begin() + (group.begin() - orderedIds_.cbegin()); - std::vector::iterator nonConstGroupEnd = - orderedIds_.begin() + (group.end() - orderedIds_.cbegin()); - util::shuffle(nonConstGroupBegin, nonConstGroupEnd, seed); - } -} - void RecursiveSplitter::shuffleGroups() { for (Group group : multiElementGroups()) { std::vector::iterator nonConstGroupBegin = @@ -94,6 +84,11 @@ void RecursiveSplitter::shuffleGroups() { } } +void RecursiveSplitter::setSeed(unsigned int seed, bool force) { + std::vector dummy; + util::shuffle(dummy.begin(), dummy.end(), seed, force); +} + template void RecursiveSplitter::groupByImpl(const std::vector &); template void RecursiveSplitter::groupByImpl(const std::vector &); template void RecursiveSplitter::groupByImpl(const std::vector &); diff --git a/src/ufo/utils/RecursiveSplitter.h b/src/ufo/utils/RecursiveSplitter.h index 1d715ca38..1b18a6431 100644 --- a/src/ufo/utils/RecursiveSplitter.h +++ b/src/ufo/utils/RecursiveSplitter.h @@ -300,17 +300,16 @@ class RecursiveSplitter void sortGroupsBy(Compare comp); /// \brief Randomly shuffle the elements of each equivalence class. - /// - /// \param seed - /// Seed with which to initialise the random number generator used by the shuffling algorithm - /// if this hasn't been done before (in a previous call to shuffleGroups() or another function - /// calling util::shuffle()). - void shuffleGroups(unsigned int seed); + void shuffleGroups(); - /// \brief Randomly shuffle the elements of each equivalence class. + /// \brief Initialise the random number generator used by shuffleGroups() with a seed. /// - /// This overload uses the defaul seed. - void shuffleGroups(); + /// \param seed + /// Seed with which to initialise the generator. + /// \param force + /// If false, the seed will only be reset if the program has not made any calls to + /// util::shuffle() yet. + void setSeed(unsigned int seed, bool force); private: void initializeEncodedGroups(); diff --git a/src/ufo/utils/RefractivityCalculator.F90 b/src/ufo/utils/RefractivityCalculator.F90 new file mode 100644 index 000000000..f413b1782 --- /dev/null +++ b/src/ufo/utils/RefractivityCalculator.F90 @@ -0,0 +1,713 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2021 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +!------------------------------------------------------------------------------- +!> \brief Module for containing a general refractivity forward operator and its K-matrix +!! +!! \author Neill Bowler (Met Office) +!! +!! \date 6 May 2021 +!! +!------------------------------------------------------------------------------- + +module ufo_utils_refractivity_calculator + +use fckit_log_module, only: fckit_log +use missing_values_mod +use ufo_constants_mod, only: & + rd, & ! Gas constant for dry air + cp, & ! Heat capacity at constant pressure for air + rd_over_cp, & ! Ratio of gas constant to heat capacity + pref, & ! Reference pressure for calculating exner + pi, & ! Something to do with circles... + grav, & ! Gravitational field strength + ecc, & ! eccentricity + k_somig, & ! Somigliana's constant + g_equat, & ! equatorial gravity (ms-2) + a_earth, & ! semi-major axis of earth (m) + flatt, & ! flattening + m_ratio, & ! gravity ratio + mw_ratio, & ! Ratio of molecular weights of water and dry air + c_virtual, & ! Related to mw_ratio + n_alpha, & ! Refractivity constant a + n_beta ! Refractivity constant b +use kinds, only: kind_real + +implicit none + +public :: ufo_calculate_refractivity +public :: ufo_refractivity_kmat + +contains + +!------------------------------------------------------------------------------- +!> \brief Calculation of the refractivity from the pressure and specific humidity +!! +!! \details **ufo_calculate_refractivity** +!! * Checks the inputs that the pressure is not missing, is monotonically +!! decreasing and not negative. +!! * Perform the calculation on the model temperature levels. It first calculates +!! the exner function and virtual temperature from the inputs. Then using +!! these calculates the wet and dry components of the refractivity. +!! * If pseudo-level processing is being used, then calculate the temperature, +!! pressure and specific humidity on the pseudo-levels by interpolation. Then +!! calculate the refractivity on all levels. +!! +!! \author Neill Bowler (Met Office) +!! +!! \date 6 May 2021 +!! +!------------------------------------------------------------------------------- + +SUBROUTINE ufo_calculate_refractivity (nlevP, & + nlevq, & + za, & + zb, & + P, & + q, & + vert_interp_ops, & + pseudo_ops, & + min_temp_grad, & + refracerr, & + nRefLevels, & + refractivity, & + model_heights, & + temperature, & + interp_pressure) + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: nlevP !< no. of p levels in state vec. +INTEGER, INTENT(IN) :: nlevq !< no. of theta levels +REAL(kind_real), INTENT(IN) :: za(nlevp) !< heights of rho levs +REAL(kind_real), INTENT(IN) :: zb(nlevq) !< heights of theta levs +REAL(kind_real), INTENT(IN) :: P(nlevp) !< state vector +REAL(kind_real), INTENT(IN) :: q(nlevq) !< state vector +LOGICAL, INTENT(IN) :: vert_interp_ops !< Use log(p) for vertical interpolation? +LOGICAL, INTENT(IN) :: pseudo_ops !< Use pseudo-levels to reduce errors? +REAL(kind_real), INTENT(IN) :: min_temp_grad !< Minimum value for the vertical temperature gradient +LOGICAL, INTENT(OUT) :: refracerr !< refractivity error +INTEGER, INTENT(OUT) :: nRefLevels !< no. of pseudo levs +REAL(kind_real), ALLOCATABLE, INTENT(OUT) :: refractivity(:) !< Ref. on pseudo levs +REAL(kind_real), ALLOCATABLE, INTENT(OUT) :: model_heights(:) !< height of pseudo levs +REAL(kind_real), OPTIONAL, INTENT(OUT) :: temperature(nlevq) !< Calculated temperature on model levels +REAL(kind_real), OPTIONAL, INTENT(OUT) :: interp_pressure(nlevq) !< Model pressure, interpolated to temperature levels + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_calculate_refractivity" +INTEGER :: i +INTEGER :: counter +REAL(kind_real) :: Exner(nlevP) +REAL(kind_real) :: Pb(nlevq) +REAL(kind_real) :: T_local(nlevq) ! Temp. on theta levs +REAL(kind_real) :: T_virtual +REAL(kind_real) :: Ex_theta +REAL(kind_real) :: pwt1 +REAL(kind_real) :: pwt2 +REAL(kind_real) :: Ndry +REAL(kind_real) :: Nwet +REAL(kind_real), ALLOCATABLE :: P_pseudo(:) +REAL(kind_real), ALLOCATABLE :: q_pseudo(:) +REAL(kind_real), ALLOCATABLE :: T_pseudo(:) +REAL(kind_real) :: refracModel(1:nlevq) +REAL(kind_real) :: gamma +REAL(kind_real) :: beta +REAL(kind_real) :: c ! continuity constant for hydrostatic pressure +CHARACTER(LEN=200) :: message ! Message to be output to user + +! Allocate arrays for pseudo-level processing and output +IF (pseudo_ops) THEN + nRefLevels = 2 * nlevq - 1 + ALLOCATE (P_pseudo(nRefLevels)) + ALLOCATE (q_pseudo(nRefLevels)) + ALLOCATE (T_pseudo(nRefLevels)) +ELSE + nRefLevels = nlevq +END IF +ALLOCATE (model_heights(nRefLevels)) +ALLOCATE (refractivity(nRefLevels)) + +! Set up the P and q vectors from x + +! Initialise refractivity arrays to missing Data +refracModel(:) = missing_value(refracModel(1)) +refractivity(:) = missing_value(refractivity(1)) +T_local(:) = missing_value(T_local(1)) +refracerr = .FALSE. + +DO i = 1, nlevq + IF (P(i) == missing_value(P(i))) THEN ! pressure missing + refracerr = .TRUE. + WRITE(message, *) RoutineName, "Input pressure missing", i + CALL fckit_log % warning(message) + EXIT + END IF + + IF (P(i) - P(i + 1) < 0.0) THEN ! or non-monotonic pressure + refracerr = .TRUE. + WRITE(message, *) RoutineName, "Input pressure non-monotonic", i + CALL fckit_log % warning(message) + EXIT + END IF +END DO + +IF (ANY (P(:) <= 0.0)) THEN ! pressure zero or negative + refracerr = .TRUE. + WRITE(message, *) RoutineName, "Input pressure not physical" + CALL fckit_log % warning(message) +END IF + +! only proceed if pressure is valid +IF (.NOT. refracerr) THEN + + ! Calculate exner on rho levels. + + Exner(:) = (P(:) / Pref) ** rd_over_cp + + ! Calculate the refractivity on the same model levels as specific humidity + DO i = 1, nlevq + ! Calc. pressure pb + pwt1 = (za(i + 1) - zb(i)) / (za(i + 1) - za(i)) + + pwt2 = 1.0 - pwt1 + + ! Calculate the pressure on the theta level. + IF (vert_interp_ops) THEN + ! Assume ln(P) linear with height + Pb(i) = EXP (pwt1 * LOG (P(i)) + pwt2 * LOG (P(i + 1))) + ELSE + ! Assume Exner varies linearly with height + Pb(i) = Pref * (pwt1 * (P(i) / Pref) ** rd_over_cp + pwt2 * (P(i + 1) / Pref) ** rd_over_cp) ** (1.0 / rd_over_cp) + END IF + + ! Calculate the Exner on the theta level. + Ex_theta = (Pb(i) / Pref) ** rd_over_cp + + ! Calculate mean layer T_virtual on staggered vertical levels + + T_virtual = grav * (za(i + 1) - za(i)) * Ex_theta / & + (Cp * (Exner(i) - Exner(i + 1))) + + T_local(i) = T_virtual / (1.0 + C_virtual * q(i)) + + ! Wet component + Nwet = n_beta * Pb(i) * q(i) / (T_local(i) ** 2 * (mw_ratio + (1.0 - mw_ratio) * q(i))) + + Ndry = n_alpha * Pb(i) / T_local(i) + + refracModel(i) = Ndry + Nwet + + END DO + + ! Do pseudo-level processing + IF (pseudo_ops) THEN + counter = 1 + DO i = 1, nRefLevels + + ! Odd 'i' (i.e. copies of actual model level values) + IF (MOD (i, 2) > 0) THEN + model_heights(i) = zb(counter) + P_pseudo(i) = Pb(counter) + q_pseudo(i) = q(counter) + T_pseudo(i) = T_local(counter) + counter = counter + 1 + + ! Even 'i' (i.e. intermediate pseudo-levels) + ELSE + model_heights(i) = (zb(counter - 1) + zb(counter)) / 2.0 + + ! Assume exponential variation when humidities are positive + IF (MIN (q(counter - 1), q(counter)) > 0.0) THEN + gamma = LOG (q(counter - 1) / q(counter)) / (zb(counter) - zb(counter - 1)) + q_pseudo(i) = q(counter - 1) * EXP (-gamma * (model_heights(i) - model_heights(i - 1))) + + ! Assume linear variation if humidities are -ve + ELSE + q_pseudo(i) = q(counter - 1) + (q(counter) - q(counter - 1)) / (zb(counter) - & + zb(counter - 1)) * (model_heights(i) - zb(counter - 1)) + END IF + + ! T varies linearly with height + beta = (T_local(counter) - T_local(counter - 1)) / (zb(counter) - zb(counter - 1)) + T_pseudo(i) = T_local(counter - 1) + beta * (model_heights(i) - zb(counter - 1)) + + ! Pressure varies to maintain hydrostatic balance + IF (ABS (T_local(counter) - T_local(counter - 1)) > min_temp_grad) THEN + c = ((Pb(counter) / Pb(counter - 1)) * (T_local(counter) / T_local(counter - 1)) ** (grav / (rd * beta)) - & + 1.0) / (zb(counter) - zb(counter - 1)) + P_pseudo(i) = (Pb(counter - 1) * (T_pseudo(i) / T_local(counter - 1)) ** & + (-grav / (rd * beta))) * (1.0 + c * (model_heights(i) - zb(counter - 1))) + ELSE + ! If layer is isothermal, explicitly force P to vary exponentially to avoid singularity + P_pseudo(i) = Pb(counter - 1) * EXP (LOG (Pb(counter) / Pb(counter - 1)) * & + ((model_heights(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1)))) + END IF + END IF + END DO + + refractivity = n_alpha * P_pseudo / T_pseudo + n_beta * P_pseudo * q_pseudo / & + (T_pseudo ** 2 * (mw_ratio + (1.0 - mw_ratio) * q_pseudo)) + ELSE + refractivity = refracModel + model_heights = zb + END IF + +END IF + +IF (PRESENT(temperature)) THEN + temperature(:) = T_local(:) +END IF + +IF (PRESENT(interp_pressure)) THEN + interp_pressure(:) = Pb(:) +END IF + +IF (pseudo_ops) THEN + IF (ALLOCATED (P_pseudo)) DEALLOCATE (P_pseudo) + IF (ALLOCATED (q_pseudo)) DEALLOCATE (q_pseudo) + IF (ALLOCATED (T_pseudo)) DEALLOCATE (T_pseudo) +END IF + +END SUBROUTINE ufo_calculate_refractivity + +!------------------------------------------------------------------------------- +!> \brief Calculate general refractivity K matrix. +!! +!! \details **ufo_refractivity_kmat** +!! * Allocate intermediate matrices used in calculation. +!! * Calculate the refractivity and basic gradients on the temperature/theta +!! levels. +!! * If using pseudo-levels, then calculate the partial matrices on these +!! levels, and use these to calculate the final matrices (dref_dp and dref_dq). +!! * If not using pseudo-levels, then calculate the final matrices. +!! * Deallocate the temporary matrices. +!! +!! \author Neill Bowler (Met Office) +!! +!! \date 6 May 2021 +!! +!------------------------------------------------------------------------------- + +!------------------------------------------------------------------------------- +!> Calculate general refractivity K matrix. +!------------------------------------------------------------------------------- + +SUBROUTINE ufo_refractivity_kmat(nlevP, & + nlevq, & + nRefLevels, & + za, & + zb, & + P, & + q, & + pseudo_ops, & + vert_interp_ops, & + min_temp_grad, & + dref_dP, & + dref_dq, & + dPb_dP, & + refractivity) + +IMPLICIT NONE + +! Subroutine arguments: +INTEGER, INTENT(IN) :: nlevP !< no. of pressure levels +INTEGER, INTENT(IN) :: nlevq !< no. of specific humidity levels +INTEGER, INTENT(IN) :: nRefLevels !< no. of refractivity levels +REAL(kind_real), INTENT(IN) :: za(nlevp) !< Height of the pressure levels +REAL(kind_real), INTENT(IN) :: zb(nlevq) !< Height of the specific humidity levels +REAL(kind_real), INTENT(IN) :: P(nlevp) !< Pressure +REAL(kind_real), INTENT(IN) :: q(nlevq) !< Specific humidity +LOGICAL, INTENT(IN) :: pseudo_ops !< Whether to use pseudo-levels in calculation +LOGICAL, INTENT(IN) :: vert_interp_ops !< Whether to interpolate vertically using exner or ln(p) +REAL(kind_real), INTENT(IN) :: min_temp_grad !< Minimum value for the vertical temperature gradient +REAL(kind_real), ALLOCATABLE, INTENT(OUT) :: dref_dP(:,:) !< kmatrix for p +REAL(kind_real), ALLOCATABLE, INTENT(OUT) :: dref_dq(:,:) !< kmatrix for q +REAL(kind_real), OPTIONAL, INTENT(OUT) :: dPb_dP(nlevq,nlevP) !< Gradient of pressure on theta levels wrt pressure on pressure levels +REAL(kind_real), OPTIONAL, INTENT(OUT), ALLOCATABLE :: refractivity(:) !< Calculated refractivity + +! Local declarations: +CHARACTER(len=*), PARAMETER :: RoutineName = "ufo_refractivity_kmat" +INTEGER :: i +INTEGER :: counter +REAL(kind_real) :: Exner(nlevP) +REAL(kind_real) :: Pb(nlevq) +REAL(kind_real) :: T_virtual(nlevq) +REAL(kind_real) :: T(nlevq) +REAL(kind_real) :: Extheta +REAL(kind_real) :: pwt1 +REAL(kind_real) :: pwt2 +REAL(kind_real) :: Ndry +REAL(kind_real) :: Nwet +REAL(kind_real) :: dEx_dP(nlevP,nlevP) +REAL(kind_real) :: dPb_dP_local(nlevq,nlevP) +REAL(kind_real) :: dExtheta_dPb(nlevq,nlevq) +REAL(kind_real) :: dTv_dExtheta(nlevq,nlevq) +REAL(kind_real) :: dTv_dEx(nlevq,nlevP) +REAL(kind_real) :: dT_dTv(nlevq,nlevq) +REAL(kind_real) :: dT_dq(nlevq,nlevq) +REAL(kind_real) :: dref_dPb(nlevq,nlevq) +REAL(kind_real) :: dref_dT(nlevq,nlevq) +REAL(kind_real) :: m1(nRefLevels,nlevq) +REAL(kind_real) :: m2(nRefLevels,nlevP) +REAL(kind_real) :: m3(nRefLevels,nlevq) +REAL(kind_real) :: m4(nRefLevels,nlevq) +REAL(kind_real), ALLOCATABLE :: P_pseudo(:) +REAL(kind_real), ALLOCATABLE :: q_pseudo(:) +REAL(kind_real), ALLOCATABLE :: T_pseudo(:) +REAL(kind_real), ALLOCATABLE :: model_heights(:) +REAL(kind_real) :: gamma +REAL(kind_real) :: beta +REAL(kind_real) :: c ! continuity constant for hydrostatic pressure +REAL(kind_real) :: g_RB ! Frequently used term +REAL(kind_real) :: c_ZZ ! Frequently used term +REAL(kind_real) :: dPp_dT1 ! dP_pseudo / dT_below +REAL(kind_real) :: dPp_dTp ! dP_pseudo / dT_pseudo +REAL(kind_real) :: dTp_dT1 ! dT_pseudo / dT_below +REAL(kind_real) :: dPp_dbeta ! dP_pseudo / dbeta +REAL(kind_real) :: dbeta_dT1 ! dbeta / dT_below +REAL(kind_real) :: dTp_dbeta ! dT_pseudo / dbeta +REAL(kind_real) :: dbeta_dT2 ! dbeta / dT_above +REAL(kind_real) :: dPp_dc ! dP_pseudo / dc +REAL(kind_real) :: dc_dT1 ! dc / dT_below +REAL(kind_real) :: dc_dbeta ! dc / dbeta +REAL(kind_real) :: dc_dT2 ! dc / dT_above +REAL(kind_real) :: dPp_dP1 ! dP_pseudo / dP_below +REAL(kind_real) :: dc_dP1 ! dc / dP_below +REAL(kind_real) :: dc_dP2 ! dc / dP_above +REAL(kind_real), ALLOCATABLE :: dref_dPpseudo(:,:) +REAL(kind_real), ALLOCATABLE :: dref_dTpseudo(:,:) +REAL(kind_real), ALLOCATABLE :: dref_dqpseudo(:,:) +REAL(kind_real), ALLOCATABLE :: dPpseudo_dPb(:,:) +REAL(kind_real), ALLOCATABLE :: dTpseudo_dTb(:,:) +REAL(kind_real), ALLOCATABLE :: dqpseudo_dqb(:,:) +REAL(kind_real), ALLOCATABLE :: dPpseudo_dT(:,:) +REAL(kind_real), ALLOCATABLE :: dTb_dP(:,:) +REAL(kind_real), ALLOCATABLE :: dPpseudo_dP(:,:) +REAL(kind_real), ALLOCATABLE :: dTpseudo_dP(:,:) + +ALLOCATE (dref_dP(nRefLevels,nlevP)) +ALLOCATE (dref_dq(nRefLevels,nlevq)) + +IF (pseudo_ops) THEN + ALLOCATE (P_pseudo(nRefLevels)) + ALLOCATE (q_pseudo(nRefLevels)) + ALLOCATE (T_pseudo(nRefLevels)) + ALLOCATE (model_heights(nRefLevels)) + IF (PRESENT(refractivity)) ALLOCATE (refractivity(nRefLevels)) + + ALLOCATE (dref_dPpseudo(nRefLevels,nRefLevels)) + ALLOCATE (dref_dTpseudo(nRefLevels,nRefLevels)) + ALLOCATE (dref_dqpseudo(nRefLevels,nRefLevels)) + ALLOCATE (dPpseudo_dPb(nRefLevels,nlevq)) + ALLOCATE (dPpseudo_dT(nRefLevels,nlevq)) + ALLOCATE (dTpseudo_dTb(nRefLevels,nlevq)) + ALLOCATE (dqpseudo_dqb(nRefLevels,nlevq)) + + ALLOCATE (dTb_dP(nlevq,nlevP)) + ALLOCATE (dPpseudo_dP(nRefLevels,nlevP)) + ALLOCATE (dTpseudo_dP(nRefLevels,nlevP)) + + dref_dPpseudo(:,:) = 0.0 + dref_dTpseudo(:,:) = 0.0 + dref_dqpseudo(:,:) = 0.0 + dPpseudo_dPb(:,:) = 0.0 + dPpseudo_dT(:,:) = 0.0 + dTpseudo_dTb(:,:) = 0.0 + dqpseudo_dqb(:,:) = 0.0 + dPpseudo_dP(:,:) = 0.0 + dTpseudo_dP(:,:) = 0.0 +END IF + +!----------------------- +! 1. Initialise matrices +!----------------------- + +dPb_dP_local(:,:) = 0.0 +dExtheta_dPb(:,:) = 0.0 +dEx_dP(:,:) = 0.0 +dTv_dExtheta(:,:) = 0.0 +dTv_dEx(:,:) = 0.0 +dT_dTv(:,:) = 0.0 +dT_dq(:,:) = 0.0 +dref_dpb(:,:) = 0.0 +dref_dT(:,:) = 0.0 +dref_dq(:,:) = 0.0 +dref_dp(:,:) = 0.0 + +! Calculate exner on rho levels. + +Exner(:) = (P(:) / Pref) ** rd_over_cp + +DO i = 1,nlevp + + dEx_dP(i,i) = rd_over_cp / Pref * (P(i) / Pref) ** (rd_over_cp - 1.0) + +END DO + +!---------------------------------------------- +! 2. Calculate the refractivity on the temperature/theta levels +!---------------------------------------------- + +DO i = 1, nlevq + + ! Calc. pressure on b levels + + pwt1 = (za(i + 1) - zb(i)) / (za(i + 1) - za(i)) + + pwt2 = 1.0 - pwt1 + + ! calculate the pressure on the theta level. + IF (vert_interp_ops) THEN + Pb(i) = EXP (pwt1 * LOG (P(i)) + pwt2 * LOG (P(i + 1))) + + dPb_dP_local(i,i) = Pb(i) * pwt1 / P(i) + dPb_dP_local(i,i + 1) = Pb(i) * pwt2 / P(i + 1) + ELSE + ! Assume Exner varies linearly with height + Pb(i) = Pref * (pwt1 * (P(i) / Pref) ** rd_over_cp + pwt2 * (P(i + 1) / Pref) ** rd_over_cp) ** (1.0 / rd_over_cp) + + dPb_dP_local(i,i) = pwt1 * (pwt1 * (P(i) / Pref) ** rd_over_cp + pwt2 * & + (P(i + 1) / Pref) ** rd_over_cp) ** (1.0 / rd_over_cp - 1.0) * (P(i) / Pref) ** (rd_over_cp - 1.0) + dPb_dP_local(i,i + 1) = pwt2 * (pwt1 * (P(i) / Pref) ** rd_over_cp + pwt2 * & + (P(i + 1) / Pref) ** rd_over_cp) ** (1.0 / rd_over_cp - 1.0) * (P(i + 1) / Pref) ** (rd_over_cp-1.0) + END IF + + ! calculate Exner on the theta level. + + Extheta = (Pb(i) / Pref) ** rd_over_cp + + dExtheta_dPb(i,i) = rd_over_cp * (Pb(i) ** (rd_over_cp - 1.0)) / (Pref ** rd_over_cp) + + ! Calculate mean layer T_virtual on staggered vertical levels + + T_virtual(i) = grav * (za(i + 1) - za(i)) * Extheta / (Cp * (Exner(i) - Exner(i + 1))) + + dTv_dExtheta(i,i) = T_virtual(i) / Extheta + + dTv_dEx(i,i) = -T_virtual(i) / (Exner(i) - Exner(i + 1)) + + dTv_dEx(i,i + 1) = T_virtual(i) / (Exner(i) - Exner(i + 1)) + + IF (i > nlevq) THEN + + T(i) = T_virtual(i) + + dT_dTv(i,i) = 1.0 + + ! no wet component + + Nwet = 0.0 + + ELSE + + T(i) = T_virtual(i) / (1.0 + C_virtual * q(i)) + + dT_dTv(i,i) = 1.0 / (1.0 + C_virtual * q(i)) + + dT_dq(i,i) = -C_virtual * T(i) / (1.0 + C_virtual * q(i)) + + ! wet component + + Nwet = n_beta * Pb(i) * q(i) / (T(i) ** 2 * (mw_ratio + (1.0 - mw_ratio) * q(i))) + + dref_dq(i,i) = n_beta * Pb(i) * mw_ratio / (T(i) * (mw_ratio + (1.0 - mw_ratio) * q(i))) ** 2 + + END IF + + Ndry = n_alpha * Pb(i) / T(i) + + if (PRESENT(refractivity) .AND. .NOT. pseudo_ops) refractivity(i) = Ndry + Nwet + + dref_dPb(i,i) = (Ndry + Nwet) / Pb(i) + + dref_dT(i,i) = -(Ndry + 2.0 * Nwet) / T(i) + +END DO + +IF (pseudo_ops) THEN + !----------------------------------! + !- Add intermediate pseudo-levels -! + !----------------------------------! + dTb_dP = MATMUL (dT_dTv, MATMUL (MATMUL (dTv_dExtheta, dExtheta_dPb), dPb_dP_local) + MATMUL (dTv_dEx, dEx_dP)) + counter = 1 + DO i = 1, nRefLevels + ! Odd 'i' (i.e. copies of actual model level values) + IF (MOD (i, 2) > 0) THEN + model_heights(i) = zb(counter) + P_pseudo(i) = Pb(counter) + q_pseudo(i) = q(counter) + T_pseudo(i) = T(counter) + IF (PRESENT(refractivity)) refractivity(i) = n_alpha * P_pseudo(i) / T_pseudo(i) + & + n_beta * P_pseudo(i) * q_pseudo(i) / & + (T_pseudo(i) ** 2 * (mw_ratio + (1.0 - mw_ratio) * q_pseudo(i))) + + dref_dPpseudo(i,i) = dref_dPb(counter,counter) + dref_dTpseudo(i,i) = dref_dT(counter,counter) + dref_dqpseudo(i,i) = dref_dq(counter,counter) + + dPpseudo_dPb(i,counter) = 1.0 + dTpseudo_dTb(i,counter) = 1.0 + dqpseudo_dqb(i,counter) = 1.0 + + counter = counter + 1 + + ! Even 'i' (i.e. intermediate pseudo-levels) + ELSE + model_heights(i) = (zb(counter - 1) + zb(counter)) / 2.0 + IF (MIN (q(counter - 1), q(counter)) > 0.0) THEN + gamma = LOG (q(counter - 1) / q(counter)) / (zb(counter) - zb(counter - 1)) + q_pseudo(i) = q(counter - 1) * EXP (-gamma * (model_heights(i) - model_heights(i - 1))) + ELSE + q_pseudo(i) = q(counter - 1) + (q(counter) - q(counter - 1)) / & + (zb(counter) - zb(counter - 1)) * (model_heights(i) - zb(counter - 1)) + END IF + + beta = (T(counter) - T(counter - 1)) / (zb(counter) - zb(counter - 1)) + T_pseudo(i) = T(counter - 1) + beta * (model_heights(i) - zb(counter - 1)) + IF (ABS (T(counter) - T(counter - 1)) > min_temp_grad) THEN + c = ((Pb(counter) / Pb(counter - 1)) * (T(counter) / T(counter - 1)) ** (grav / (rd * beta)) - 1.0) / & + (zb(counter) - zb(counter - 1)) + P_pseudo(i) = (Pb(counter - 1) * (T_pseudo(i) / T(counter - 1)) ** & + (-grav / (rd * beta))) * (1.0 + c * (model_heights(i) - zb(counter - 1))) + ELSE + P_pseudo(i) = Pb(counter - 1) * EXP (LOG (Pb(counter) / Pb(counter - 1)) * & + ((model_heights(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1)))) + END IF + + Ndry = n_alpha * P_pseudo(i) / T_pseudo(i) + Nwet = n_beta * P_pseudo(i) * q_pseudo(i) / (T_pseudo(i) ** 2 & + *(mw_ratio + (1.0 - mw_ratio) * q_pseudo(i))) + + IF (PRESENT(refractivity)) refractivity(i) = Ndry + Nwet + + dref_dPpseudo(i,i) = (Ndry + Nwet) / P_pseudo(i) + dref_dTpseudo(i,i) = -(Ndry + 2.0 * Nwet) / T_pseudo(i) + dref_dqpseudo(i,i) = n_beta * P_pseudo(i) * mw_ratio / (T_pseudo(i) * (mw_ratio + & + (1.0 - mw_ratio) * q_pseudo(i))) ** 2 + + ! Transform P, q and T to pseudo-levels + IF (MIN (q(counter - 1), q(counter)) > 0.0) THEN + dqpseudo_dqb(i,counter - 1) = (q_pseudo(i) / q(counter - 1)) * (1.0 - & + (model_heights(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1))) + + dqpseudo_dqb(i,counter) = (q_pseudo(i) / q(counter)) * ((model_heights(i) - zb(counter - 1)) / & + (zb(counter) - zb(counter - 1))) + ELSE + dqpseudo_dqb(i,counter - 1) = (zb(counter) - model_heights(i)) / (zb(counter) - zb(counter - 1)) + + dqpseudo_dqb(i,counter) = (model_heights(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1)) + END IF + + dTpseudo_dTb(i,counter - 1) = (zb(counter) - model_heights(i)) / (zb(counter) - zb(counter - 1)) + + dTpseudo_dTb(i,counter) = (model_heights(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1)) + + ! Items that need changing for dPpseudo/dTb: + + g_RB = grav / (rd * beta) + c_ZZ = c + 1.0 / (zb(counter) - zb(counter - 1)) + + dPp_dT1 = (g_RB / T(counter - 1)) * P_pseudo(i) + dPp_dTp = -(g_RB / T_pseudo(i)) * P_pseudo(i) + dTp_dT1 = dTpseudo_dTb(i,counter - 1) + dPp_dbeta = (g_RB / beta) * LOG (T_pseudo(i) / T(counter - 1)) * P_pseudo(i) + dbeta_dT1 = -1.0 / (zb(counter) - zb(counter - 1)) + dTp_dbeta = model_heights(i) - zb(counter - 1) + dbeta_dT2 = 1.0 / (zb(counter) - zb(counter - 1)) + + IF (ABS (T(counter) - T(counter - 1)) > min_temp_grad) THEN + ! Incomplete computation of dPpseudo_dPb. Temperature derivatives done below + dPp_dc = (model_heights(i) - zb(counter - 1)) * Pb(counter - 1) * (T_pseudo(i) / T(counter - 1)) ** (-g_RB) + dc_dT1 = -(g_RB / (T(counter - 1))) * c_ZZ + dc_dbeta = -(g_RB / beta) * LOG (T(counter) / T(counter - 1)) * c_ZZ + dc_dT2 = (g_RB / T(counter)) * c_ZZ + dPp_dP1 = P_pseudo(i) / Pb(counter - 1) + dPpseudo_dT(i,counter - 1) = dPp_dT1 + dPp_dTp * dTp_dT1 + dPp_dbeta * dbeta_dT1 + dPp_dc * & + (dc_dT1 + dc_dbeta * dbeta_dT1) + dPpseudo_dT(i,counter) = (dPp_dbeta + dPp_dTp * dTp_dbeta + dPp_dc * dc_dbeta) * & + dbeta_dT2 + dPp_dc * dc_dT2 + + dc_dP1 = -(1.0 / Pb(counter - 1)) * c_ZZ + dc_dP2 = (1.0 / Pb(counter)) * c_ZZ + + dPpseudo_dPb(i,counter - 1) = dPp_dP1 + dPp_dc * dc_dP1 + dPpseudo_dPb(i,counter) = dPp_dc * dc_dP2 + ELSE + dPpseudo_dPb(i,counter - 1) = EXP (LOG (Pb(counter) / Pb(counter - 1)) * ((model_heights(i) - zb(counter - 1)) / & + (zb(counter) - zb(counter - 1)))) * (1.0 - ((model_heights(i) - zb(counter - 1)) / (zb(counter) - zb(counter - 1)))) + dPpseudo_dPb(i,counter) = (Pb(counter - 1) / Pb(counter)) * ((model_heights(i) - zb(counter - 1)) / & + (zb(counter) - zb(counter - 1))) * EXP (LOG (Pb(counter) / Pb(counter - 1)) * ((model_heights(i) - zb(counter - 1)) / & + (zb(counter) - zb(counter - 1)))) + END IF + + END IF + + END DO + + ! temperature derivatives: + dPpseudo_dP = MATMUL (dPpseudo_dPb, dPb_dP_local) + MATMUL (dPpseudo_dT, dTb_dP) + dTpseudo_dP = MATMUL (dTpseudo_dTb, dTb_dP) + + !------------------------------------------------- + ! 3. Evaluate the Kmatrix by matrix multiplication + !------------------------------------------------- + + ! calc K matrix for P on rho levels + dref_dP(:,:) = MATMUL (dref_dPpseudo, dPpseudo_dP) + MATMUL (dref_dTpseudo, dTpseudo_dP) + + ! calc Kmatrix for q on theta levels + dref_dq(:,:) = MATMUL (dref_dqpseudo, dqpseudo_dqb) + MATMUL (MATMUL (dref_dTpseudo, dTpseudo_dTb), dT_dq) + & + MATMUL (MATMUL (dref_dPpseudo, dPpseudo_dT), dT_dq) + +! Normal model levels +ELSE + + !------------------------------------------------- + ! 3. Evaluate the Kmatrix by matrix multiplication + !------------------------------------------------- + + ! calc K matrix for P on rho levels + ! dNmod/dP = (dNmod/dPb * dPb/dP) + .... + dref_dP(:,:) = MATMUL (dref_dPb, dPb_dP_local) + + ! .... (dNmod/dT * dT/dTv * dTv/dEx *dEx/dP) + ..... + m1(:,:) = MATMUL (dref_dT, dT_dTv) + m2(:,:) = MATMUL (m1, dTv_dEx) + dref_dP(:,:) = dref_dP(:,:) + MATMUL (m2, dEx_dP) + + ! .... (dNmod/dT * dT/dTv * dTv/dExtheta *dExtheta/dPb*dPb/dP) + m3(:,:) = MATMUL (m1, dTv_dExtheta) + m4(:,:) = MATMUL (m3, dExtheta_dPb) + dref_dP(:,:) = dref_dP(:,:) + MATMUL (m4, dPb_dP_local) + + ! calc Kmatrix for q on theta levels + ! dNmod/dq = (dNmod/dq) + (dNmod/dT*dT/dq) + dref_dq(:,:) = dref_dq(:,:) + MATMUL (dref_dT, dT_dq) + +END IF + +if (present(dPb_dP)) dPb_dP(:,:) = dPb_dP_local(:,:) + +IF (ALLOCATED (P_pseudo)) DEALLOCATE (P_pseudo) +IF (ALLOCATED (q_pseudo)) DEALLOCATE (q_pseudo) +IF (ALLOCATED (T_pseudo)) DEALLOCATE (T_pseudo) +IF (ALLOCATED (model_heights)) DEALLOCATE (model_heights) +IF (ALLOCATED (dref_dPpseudo)) DEALLOCATE (dref_dPpseudo) +IF (ALLOCATED (dref_dTpseudo)) DEALLOCATE (dref_dTpseudo) +IF (ALLOCATED (dref_dqpseudo)) DEALLOCATE (dref_dqpseudo) +IF (ALLOCATED (dPpseudo_dPb)) DEALLOCATE (dPpseudo_dPb) +IF (ALLOCATED (dTpseudo_dTb)) DEALLOCATE (dTpseudo_dTb) +IF (ALLOCATED (dqpseudo_dqb)) DEALLOCATE (dqpseudo_dqb) +IF (ALLOCATED (dPpseudo_dT)) DEALLOCATE (dPpseudo_dT) +IF (ALLOCATED (dTb_dP)) DEALLOCATE (dTb_dP) +IF (ALLOCATED (dPpseudo_dP)) DEALLOCATE (dPpseudo_dP) +IF (ALLOCATED (dTpseudo_dP)) DEALLOCATE (dTpseudo_dP) + +END SUBROUTINE ufo_refractivity_kmat + +end module ufo_utils_refractivity_calculator diff --git a/src/ufo/utils/dataextractor/DataExtractor.cc b/src/ufo/utils/dataextractor/DataExtractor.cc new file mode 100644 index 000000000..174b08500 --- /dev/null +++ b/src/ufo/utils/dataextractor/DataExtractor.cc @@ -0,0 +1,192 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include + +#include "eckit/utils/StringTools.h" + +#include "ioda/Misc/StringFuncs.h" + +#include "ufo/utils/dataextractor/DataExtractor.h" +#include "ufo/utils/dataextractor/DataExtractorCSVBackend.h" +#include "ufo/utils/dataextractor/DataExtractorInput.h" +#include "ufo/utils/dataextractor/DataExtractorNetCDFBackend.h" + +/// \brief Boost visitor which allows us to sort a vector. +class SortUpdateVisitor : public boost::static_visitor { + public: + explicit SortUpdateVisitor(ufo::RecursiveSplitter &splitter) : splitter(splitter) {} + + template + void operator()(const std::vector &coord) { + splitter.groupBy(coord); + } + + void operator()(const std::vector &coord) { + splitter.sortGroupsBy( + [&coord](int indexA, int indexB) { + return coord[static_cast(indexA)] < coord[static_cast(indexB)]; + }); + } + + ufo::RecursiveSplitter &splitter; +}; + + +/// \brief Boost visitor which allows us to sort a vector. +class SortVisitor : public boost::static_visitor { + public: + explicit SortVisitor(const ufo::RecursiveSplitter &splitter) : splitter(splitter) {} + + template + void operator()(std::vector &coord) { + std::vector newCoord; + newCoord.reserve(coord.size()); + for (const auto &group : splitter.groups()) { + for (const auto &index : group) { + newCoord.push_back(coord[index]); + } + } + // Replace the coordinate with the sorted one. + coord = std::move(newCoord); + } + + const ufo::RecursiveSplitter &splitter; +}; + + +namespace ufo { + +DataExtractor::DataExtractor(const std::string &filepath, const std::string &group) { + // Read the data from the file + load(filepath, group); + // Start by constraining to the full range of our data + resetExtract(); + // Initialise splitter for both dimensions + splitter_.emplace_back(ufo::RecursiveSplitter(static_cast(interpolatedArray2D_.rows()))); + splitter_.emplace_back(ufo::RecursiveSplitter(static_cast(interpolatedArray2D_.cols()))); +} + + +void DataExtractor::load(const std::string &filepath, + const std::string &interpolatedArrayGroup) { + std::unique_ptr backend = createBackendFor(filepath); + DataExtractorInput input = backend->loadData(interpolatedArrayGroup); + coord2DimMapping_ = std::move(input.coord2DimMapping); + dim2CoordMapping_ = std::move(input.dim2CoordMapping); + coordsVals_ = std::move(input.coordsVals); + interpolatedArray2D_ = std::move(input.payloadArray); +} + +std::unique_ptr DataExtractor::createBackendFor( + const std::string &filepath) { + const std::string lowercasePath = eckit::StringTools::lower(filepath); + if (eckit::StringTools::endsWith(lowercasePath, ".nc") || + eckit::StringTools::endsWith(lowercasePath, ".nc4")) + return boost::make_unique(filepath); + else if (eckit::StringTools::endsWith(lowercasePath, ".csv")) + return boost::make_unique(filepath); + else + throw eckit::BadValue("File '" + filepath + "' has an unrecognized extension. " + "The supported extensions are .nc, .nc4 and .csv", Here()); +} + + +void DataExtractor::sort() { + Eigen::ArrayXXf sortedArray = interpolatedArray2D_; + nextCoordToExtractBy_ = coordsToExtractBy_.begin(); + + for (size_t dim = 0; dim < dim2CoordMapping_.size(); ++dim) { + // Reorder coordinates + for (auto &coord : dim2CoordMapping_[dim]) { + auto &coordVal = coordsVals_[coord]; + SortVisitor visitor(splitter_[dim]); + boost::apply_visitor(visitor, coordVal); + } + // Reorder the array to be interpolated + if (dim == 0) { + int ind = -1; + for (const auto &group : splitter_[dim].groups()) { + for (const auto &index : group) { + ind++; + oops::Log::debug() << "Sort index dim0; index-from: " << ind << " index-to: " << + index << std::endl; + for (Eigen::Index j = 0; j < interpolatedArray2D_.cols(); j++) { + sortedArray(ind, j) = interpolatedArray2D_(static_cast(index), j); + } + } + } + // Replace the unsorted array with the sorted one. + interpolatedArray2D_ = sortedArray; + } else if (dim == 1) { + int ind = -1; + for (const auto &group : splitter_[dim].groups()) { + for (const auto &index : group) { + ind++; + oops::Log::debug() << "Sort index dim1; index-from: " << ind << " index-to: " << + index << std::endl; + for (Eigen::Index i = 0; i < interpolatedArray2D_.rows(); i++) { + sortedArray(i, ind) = interpolatedArray2D_(i, static_cast(index)); + } + } + } + // Replace the unsorted array with the sorted one. + interpolatedArray2D_ = sortedArray; + } else { + throw eckit::Exception("Unable to reorder the array to be interpolated: " + "it has more than 2 dimensions.", Here()); + } + } +} + + +void DataExtractor::scheduleSort(const std::string &varName, const InterpMethod &method) { + // Map any names of the form var@Group to Group/var + const std::string canonicalVarName = ioda::convertV1PathToV2Path(varName); + + const CoordinateValues &coordVal = coordsVals_.at(canonicalVarName); + const int dimIndex = coord2DimMapping_.at(canonicalVarName); + + SortUpdateVisitor visitor(splitter_[static_cast(dimIndex)]); + boost::apply_visitor(visitor, coordVal); + + // Update our map between coordinate (variable) and interpolation/extract method + coordsToExtractBy_.emplace_back(Coordinate{varName, coordVal, method, dimIndex}); +} + + +void DataExtractor::resetExtract() { + constrainedRanges_[0].begin = 0; + constrainedRanges_[0].end = static_cast(interpolatedArray2D_.rows()); + constrainedRanges_[1].begin = 0; + constrainedRanges_[1].end = static_cast(interpolatedArray2D_.cols()); + resultSet_ = false; + nextCoordToExtractBy_ = coordsToExtractBy_.begin(); +} + + +float DataExtractor::getResult() { + // Fetch the result + if (resultSet_) { + // This was derived from linear interpolation so return it. + resetExtract(); + return result_; + } + + int sizeDim0 = constrainedRanges_[0].end - constrainedRanges_[0].begin; + int sizeDim1 = constrainedRanges_[1].end - constrainedRanges_[1].begin; + if (sizeDim0 != 1 || sizeDim1 != 1) { + throw eckit::Exception("Previous calls to extract() have failed to identify " + "a single value to return.", Here()); + } + float res = interpolatedArray2D_(constrainedRanges_[0].begin, constrainedRanges_[1].begin); + resetExtract(); + return res; +} + + +} // namespace ufo diff --git a/src/ufo/utils/dataextractor/DataExtractor.h b/src/ufo/utils/dataextractor/DataExtractor.h new file mode 100644 index 000000000..4dc40d668 --- /dev/null +++ b/src/ufo/utils/dataextractor/DataExtractor.h @@ -0,0 +1,526 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTOR_H_ +#define UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTOR_H_ + +#include // sort +#include // greater +#include // std::numeric_limits +#include // list +#include // unique_ptr +#include // stringstream +#include +#include +#include // pair +#include + +#include "boost/variant.hpp" +#include "Eigen/Dense" // Eigen Arrays and Matrices + +#include "eckit/exception/Exceptions.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/RecursiveSplitter.h" + +namespace ioda { +class Variable; +struct Named_Variable; +} + + +namespace ufo { + +class DataExtractorBackend; + +/// \brief Method used by the DataExtractor to map the value of an ObsSpace variable to +/// a range of slices of the interpolated array along the dimension indexed by that variable. +enum class InterpMethod { + /// \brief Select slices where the indexing coordinate matches exactly the value of the + /// corresponding ObsSpace variable. + /// + /// If no match is found, an exception is thrown unless there are slices where the indexing + /// coordinate is set to the missing value placeholder; in this case these slices are selected + /// instead. This can be used to define a fallback value (used if there is no exact match). + /// + /// This is the only method that can be used for variables of type 'string'. + EXACT, + + /// \brief Select slices where the indexing coordinate is closest to the value of the + /// corresponding ObsSpace variable. + /// + /// In case of a tie (e.g. if the value of the ObsSpace variable is 3 and the coordinate contains + /// values 2 and 4, but not 3), the smaller of the candidate coordinate values is used (in this + /// example, 2). + NEAREST, + + /// \brief Select slices corresponding to the least value of the indexing coordinate + /// greater than or equal to the value of the corresponding ObsSpace variable. + LEAST_UPPER_BOUND, + + /// \brief Select slices corresponding to the greatest value of the indexing coordinate + /// less than or equal to the value of the corresponding ObsSpace variable. + GREATEST_LOWER_BOUND, + + /// \brief Perform a piecewise linear interpolation along the dimension indexed by the ObsSpace + /// variable. + /// + /// This method can only be used for the last indexing variable. + LINEAR +}; + + +/// \brief This class makes it possible to extract and interpolate data loaded from a file. +/// +/// Currently the following file formats are supported: NetCDF and CSV. See +/// DataExtractorNetCDFBackend and DataExtractorCSVBackend for more information about these +/// formats. +/// +/// In both cases, the files will contain a _payload array_ (the array from which data will be +/// extracted) and one or more _coordinate arrays_ indexing the payload array. This class makes it +/// possible to rapidly extract a value from the payload array corresponding to particular values +/// of the coordinates, or to interpolate multiple values from this array. Coordinate matching can +/// be exact or approximate (looking for the nearest match). It is also possible to perform a +/// piecewise linear interpolation of the data along one coordinate axis. (Multidimensional linear +/// interpolation is not supported.) +/// +/// Here's how to use this class: +/// +/// * Call the constructor to load data from an input file. +/// * Call scheduleSort() one or more times, each time passing to it the name of a coordinate +/// array and the matching method to be used for this coordinate. This will determine the order +/// in which coordinates will be matched. +/// * Call sort(). This will prepare internal data structures required for a rapid search for +/// matching coordinates. +/// * To extract a value from the payload array for a particular data point, pass the values of +/// successive coordinates of that point to calls to extract() (in the order matching the order of +/// the preceding calls to scheduleSort()). Then call getResult() to retrieve the extracted value. +/// +/// Here is a summary of particulars to the extraction/interpolation algorithms available: +/// - Nearest neighbour 'interpolation' chooses the **first** nearest value to be found in the case +/// of equidistant neighbours of different values. The algorithm will then return the one or more +/// locations corresponding to this nearest value. Let's illustrate by extracting the nearest +/// neighbours to 1.5: +/// +/// [1, 1, 2, 3, 4, 5] +/// +/// Both 1 and 2 are equidistant, but we take the first found equidistant neighbour (1), then +/// return all indices matching this neighbour (indices 0 and 1 in this case). +class DataExtractor +{ + public: + /// \brief Create an object that can be used to extract data loaded from a file. + /// \details This object is capable of sorting the data from this file extracting the relevant + /// values for a given observation as well as performing linear interpolation to derive the final + /// value. + /// \param[in] filepath Path to the input file. + /// \param[in] group Group containing the payload variable. + explicit DataExtractor(const std::string &filepath, const std::string &group); + + /// \brief Update the instruction on how to sort the data for the provided variable name. + /// \details This works iteratively by further splitting the RecursiveSplitter sub-groups + /// according to the variable name provided. By this, it is possible to sort the data in + /// such a way to ensure that extraction always results in 1 contiguous chunk. + /// No data or coordinates are actually physically sorted yet. Special treatment for float + /// type variables, where this is used to sort each of the sub-groups. + /// \param[in] varName is the name of the coordinate axis to sort. + /// \param[in] method is the interpolation/extraction method to use for this coordinate. + /// \internal This member function call corresponds to a RecursiveSplitter.groupBy call, useful + /// to sort according to nearesr/exact match variables. In the special case of float type, + /// RecursiveSplitter.sortGroupsBy is used. + void scheduleSort(const std::string &varName, const InterpMethod &method); + + /// \brief Finalise the sort, sorting each of the coordinates indexing the axes of the array to + /// be interpolated, as well as that array itself. + /// \details Utilising the instructions provided by the user calling the scheduleSort() member + /// function, we now physically sort the array itself along with all coordinates which + /// describe it. + /// \internal Applies the RecursiveSplitter object and necessarily creates copies to achieve + /// this sort. + void sort(); + + /// \brief Perform extract, given an observation value for the coordinate associated with this + /// extract iteration. + /// \details Calls the relevant extract methood (linear, nearest or exact), corresponding to the + /// coordinate associated with this extract iteration (along with the associated interpolation + /// method). The extract then utilises the observation value ('obVal') to perform this + /// extraction. + /// \param[in] obVal is the observation value used for the extract operation. + template + void extract(const T &obVal) { + if (nextCoordToExtractBy_ == coordsToExtractBy_.cend()) { + throw eckit::UserError("Too many extract() calls made for the expected number of variables.", + Here()); + } + + // Fetch coordinate values + const std::vector &coordValues = boost::get>(nextCoordToExtractBy_->values); + // Perform the extraction using the selected method + switch (nextCoordToExtractBy_->method) { + case InterpMethod::EXACT: + exactMatch(nextCoordToExtractBy_->name, coordValues, + nextCoordToExtractBy_->payloadDim, obVal); + break; + case InterpMethod::LINEAR: + result_ = getResult(nextCoordToExtractBy_->name, coordValues, + nextCoordToExtractBy_->payloadDim, obVal); + resultSet_ = true; + break; + case InterpMethod::NEAREST: + nearestMatch(nextCoordToExtractBy_->name, coordValues, + nextCoordToExtractBy_->payloadDim, obVal); + break; + case InterpMethod::LEAST_UPPER_BOUND: + leastUpperBoundMatch(nextCoordToExtractBy_->name, coordValues, + nextCoordToExtractBy_->payloadDim, obVal); + break; + case InterpMethod::GREATEST_LOWER_BOUND: + greatestLowerBoundMatch(nextCoordToExtractBy_->name, coordValues, + nextCoordToExtractBy_->payloadDim, obVal); + break; + default: + throw eckit::UserError("Only 'linear', 'nearest', 'exact', 'least upper bound' and " + "'greatest lower bound' interpolation methods supported.", + Here()); + } + ++nextCoordToExtractBy_; + } + + /// \brief Fetch the final interpolated value. + /// \details This will only be succesful if previous calls to extract() have produced a single + /// value to return. + float getResult(); + + private: + /// \brief Fetch the final interpolated value at the 'location' `obVal`. + /// + /// \details It is assumed that previous calls to extract() have extracted a single 1D slice of + /// the interpolated array parallel to the axis `varName`. This function returns the value + /// produced by piecewise linear interpolation of this slice at the point `obVal`. + /// + /// \param[in] varName is the name of the coordinate along which to interpolate. + /// \param[in] varValues is the vector of values of that coordinate. + /// \param[in] dimIndex is the dimension of the payload array indexed by the coordinate. + /// \param[in] obVal is the interpolation location. + template + float getResult(const std::string &varName, const std::vector &varValues, + int dimIndex, const T &obVal) { + // Sanity check constraint + int sizeDim0 = constrainedRanges_[0].end - constrainedRanges_[0].begin; + int sizeDim1 = constrainedRanges_[1].end - constrainedRanges_[1].begin; + if ((dimIndex == 1 && !(sizeDim1 > 1 && sizeDim0 == 1)) || + (dimIndex == 0 && !(sizeDim0 > 1 && sizeDim1 == 1))) { + throw eckit::Exception("Linear interpolation failed - data must be 1D.", Here()); + } + + // Constrain our index range in the relevant dimension. + const Range &range = constrainedRanges_[static_cast(dimIndex)]; + + if ((obVal > varValues[range.end - 1]) || (obVal < varValues[range.begin])) { + throw eckit::Exception("Linear interpolation failed, value is beyond grid extent." + "No extrapolation supported.", + Here()); + } + // Find first index of varValues >= obVal + int nnIndex = std::lower_bound(varValues.begin() + range.begin, + varValues.begin() + range.end, + obVal) - varValues.begin(); + + // Determine upper or lower indices from this + if (varValues[nnIndex] == obVal) { + // No interpolation required (is equal) + float res; + if (dimIndex == 1) { + res = static_cast(interpolatedArray2D_(constrainedRanges_[0].begin, nnIndex)); + } else { + res = static_cast(interpolatedArray2D_(nnIndex, constrainedRanges_[1].begin)); + } + return res; + } + // Linearly interpolate between these two indices. + auto zUpper = *(interpolatedArray2D_.data()); + auto zLower = *(interpolatedArray2D_.data()); + if (dimIndex == 1) { + zUpper = interpolatedArray2D_(constrainedRanges_[0].begin, nnIndex); + zLower = interpolatedArray2D_(constrainedRanges_[0].begin, nnIndex-1); + } else { + zUpper = interpolatedArray2D_(nnIndex, constrainedRanges_[1].begin); + zLower = interpolatedArray2D_(nnIndex-1, constrainedRanges_[1].begin); + } + float res = ((static_cast(obVal - varValues[nnIndex-1]) / + static_cast(varValues[nnIndex] - varValues[nnIndex-1])) * + (zUpper - zLower)) + zLower; + return res; + } + + float getResult(const std::string &varName, const std::vector &varValues, + int dimIndex, const std::string &obVal) { + throw eckit::UserError("VarName: " + varName + + " - linear interpolation not compatible with string type.", Here()); + } + + /// \brief Update our extract constraint based on a nearest match against the specified + /// coordinate indexing a dimension of the payload array. + /// \details + /// + /// Method: + /// - Find **first** discovered nearest value in our loop. + /// - Determine which indices match this nearest value. + /// (more than one index could have this one value). + /// + /// [1, 1, 2, 3, 4, 5] + /// + /// Nearest neighbour extraction of “1”, has more than one neighbour. + /// That is, more than one index with the same value have the same distance: + /// + /// [1, 1] i.e. range=(0, 2) + /// + /// - Note that an alternative implementation could consider equidistant + /// values, though it was decided this was not desirable behaviour: + /// + /// [1, 1, 2, 3, 4, 5] + /// + /// Nearest neighbour extraction of “1.5” could be then considered to have 3 + /// equidistant neighbours (1, 1, 2). That is, two different values with the + /// same distance. + /// + /// [1, 1, 2] i.e. range=(0, 3) + /// + /// \param[in] varName is the name of the coordinate to match against. + /// \param[in] varValues is the vector of values of that coordinate. + /// \param[in] dimIndex is the dimension of the payload array indexed by the coordinate. + /// \param[in] obVal is the value to match. + template + void nearestMatch(const std::string &varName, const std::vector &varValues, + int dimIndex, const T &obVal) { + // Constrain our index range in the relevant dimension. + Range &range = constrainedRanges_[static_cast(dimIndex)]; + + // Find first index of varValues >= obVal + int nnIndex = std::lower_bound(varValues.begin() + range.begin, + varValues.begin() + range.end, + obVal) - varValues.begin(); + if (nnIndex >= range.end) { + nnIndex = range.end - 1; + } + + // Now fetch the nearest neighbour index (lower index prioritised for different values with + // same distance) + T dist = std::abs(varValues[nnIndex] - obVal); + if ((varValues[nnIndex] > obVal) && (nnIndex > range.begin) && + (std::abs(varValues[nnIndex - 1] - obVal) <= dist)) + nnIndex--; + + // Now find **same value** equidistant neighbours + auto bounds = std::equal_range(varValues.begin() + range.begin, + varValues.begin() + range.end, + varValues[nnIndex]); + range = {static_cast(bounds.first - varValues.begin()), + static_cast(bounds.second - varValues.begin())}; + oops::Log::debug() << "Nearest match; name: " << varName << " range: " << + range.begin << "," << range.end << std::endl; + } + + void nearestMatch(const std::string &varName, const std::vector &varValues, + int dimIndex, const std::string &obVal) { + throw eckit::UserError("Nearest match not compatible with string type.", Here()); + } + + /// \brief Update our extract constraint based on an exact match against the specified coordinate + /// indexing a dimension of the payload array. + /// + /// \param[in] varName is the name of the coordinate to match against. + /// \param[in] varValues is the vector of values of that coordinate. + /// \param[in] dimIndex is the dimension of the payload array indexed by the coordinate. + /// \param[in] obVal is the value to match. + template + void exactMatch(const std::string &varName, const std::vector &varValues, + int dimIndex, const T &obVal) { + // Constrain our index range in the relevant dimension. + Range &range = constrainedRanges_[static_cast(dimIndex)]; + + // Find the first and last matching index + auto bounds = std::equal_range(varValues.begin() + range.begin, + varValues.begin() + range.end, + obVal); + if (bounds.first == bounds.second) { + // No matching coordinate found. If the coordinate contains a 'missing value' entry, + // use it as a fallback. (If it doesn't, the 'bounds' range will stay empty, so an error will + // be reported). + bounds = std::equal_range(varValues.begin() + range.begin, + varValues.begin() + range.end, + util::missingValue(obVal)); + } + + range = {static_cast(bounds.first - varValues.begin()), + static_cast(bounds.second - varValues.begin())}; + + if (range.begin == range.end) { + std::stringstream msg; + msg << "No match found for exact match extraction of value '" << obVal + << "' of the variable '" << varName << "'"; + throw eckit::Exception(msg.str(), Here()); + } + oops::Log::debug() << "Exact match; name: " << varName << " range: " << + range.begin << "," << range.end << std::endl; + } + + /// \brief Update our extract constraint based on a least-upper-bound match against the specified + /// coordinate indexing a dimension of the payload array. + /// + /// \param[in] varName is the name of the coordinate to match against. + /// \param[in] varValues is the vector of values of that coordinate. + /// \param[in] dimIndex is the dimension of the payload array indexed by the coordinate. + /// \param[in] obVal is the value to match. + template + void leastUpperBoundMatch(const std::string &varName, const std::vector &varValues, + int dimIndex, const T &obVal) { + // Constrain our index range in the relevant dimension. + Range &range = constrainedRanges_[static_cast(dimIndex)]; + + // Find index of the first varValues >= obVal + typedef typename std::vector::const_iterator It; + const It rangeBegin(varValues.begin() + range.begin); + const It rangeEnd(varValues.begin() + range.end); + + const It leastUpperBoundIt = std::lower_bound(rangeBegin, rangeEnd, obVal); + if (leastUpperBoundIt == rangeEnd) { + std::stringstream msg; + msg << "No match found for 'least upper bound' extraction of value '" << obVal + << "' of the variable '" << varName << "'"; + throw eckit::Exception(msg.str(), Here()); + } + + // Find the range of items with the same value of this coordinate + const auto bounds = std::equal_range(rangeBegin, rangeEnd, *leastUpperBoundIt); + range = {static_cast(bounds.first - varValues.begin()), + static_cast(bounds.second - varValues.begin())}; + oops::Log::debug() << "Least upper bound match; name: " << varName << " range: " + << range.begin << "," << range.end << std::endl; + } + + void leastUpperBoundMatch(const std::string &varName, const std::vector &varValues, + int dimIndex, const std::string &obVal) { + throw eckit::UserError("The 'least upper bound' method cannot be used for string variables.", + Here()); + } + + /// \brief Update our extract constraint based on a greatest-lower-bound match against the + /// specified coordinate indexing a dimension of the payload array. + /// + /// \param[in] varName is the name of the coordinate to match against. + /// \param[in] varValues is the vector of values of that coordinate. + /// \param[in] dimIndex is the dimension of the payload array indexed by the coordinate. + /// \param[in] obVal is the value to match, against the NetCDF coordinate of name 'varName'. + template + void greatestLowerBoundMatch(const std::string &varName, const std::vector &varValues, + int dimIndex, const T &obVal) { + // Constrain our index range in the relevant dimension. + Range &range = constrainedRanges_[static_cast(dimIndex)]; + + // Find index of the last varValues <= obVal + + typedef typename std::vector::const_reverse_iterator ReverseIt; + typedef std::greater Compare; + const ReverseIt reverseRangeBegin(varValues.begin() + range.end); + const ReverseIt reverseRangeEnd(varValues.begin() + range.begin); + + const ReverseIt greatestLowerBoundIt = + std::lower_bound(reverseRangeBegin, reverseRangeEnd, obVal, Compare()); + if (greatestLowerBoundIt == reverseRangeEnd) { + std::stringstream msg; + msg << "No match found for 'greatest lower bound' extraction of value '" << obVal + << "' of the variable '" << varName << "'"; + throw eckit::Exception(msg.str(), Here()); + } + + // Find the range of items with the same value of this coordinate + const auto bounds = std::equal_range(varValues.begin() + range.begin, + varValues.begin() + range.end, + *greatestLowerBoundIt); + range = {static_cast(bounds.first - varValues.begin()), + static_cast(bounds.second - varValues.begin())}; + oops::Log::debug() << "Greatest lower bound match; name: " << varName << " range: " + << range.begin << "," << range.end << std::endl; + } + + void greatestLowerBoundMatch(const std::string &varName, + const std::vector &varValues, + int dimIndex, const std::string &obVal) { + throw eckit::UserError("The 'greatest lower bound' method cannot be used for string variables.", + Here()); + } + + /// \brief Reset the extraction range for this object. + /// \details Each time an exactMatch, nearestMatch, leastUpperBoundMatch or + /// greatestLowerBoundMatch call is made for one or more variable, + /// the extraction range is further constrained to match our updated match conditions. After + /// the final 'extract' is made (i.e. an interpolated value is derived) it is desirable to reset + /// the extraction range by calling this method. + /// \internal This is called by the getObsErrorValue member functions just before returning the + /// interpolated value. + void resetExtract(); + + /// \brief Fetch the coordinate data of specified name. + /// \details We cache this data so as not to require performing multiple loads from disk. + template + T& get(const std::string &key) { + try { + return boost::get (coordsVals_[key]); + } catch (boost::bad_get) { + throw eckit::BadParameter("Unable to find coordinate with this type", Here()); + } + } + + /// \brief Load all data from the input file. + void load(const std::string &filepath, const std::string &interpolatedArrayGroup); + + /// \brief Create a backend able to read file \p filepath. + static std::unique_ptr createBackendFor(const std::string &filepath); + + // Object represent the extraction range in both dimensions. + struct Range {int begin, end;}; + std::array constrainedRanges_; + + // Container holding coordinate arrays (of all supported types) loaded from the input file. + typedef boost::variant, + std::vector, + std::vector> CoordinateValues; + std::unordered_map coordsVals_; + // The array to be interpolated (the payload array). + Eigen::ArrayXXf interpolatedArray2D_; + float result_; + bool resultSet_; + // Container for re-ordering our data + std::vector splitter_; + + /// Maps coordinate names to dimensions (0 or 1) of the payload array + std::unordered_map coord2DimMapping_; + /// Maps dimensions of the payload array (0 or 1) to coordinate names + std::vector> dim2CoordMapping_; + + /// Coordinate used for data extraction from the payload array. + struct Coordinate { + /// Coordinate name + std::string name; + /// Coordinate values + const CoordinateValues &values; + /// Extraction method to use + InterpMethod method; + /// Axis of the payload array indexed by the coordinate (0 or 1) + int payloadDim; + }; + + /// Coordinates to use in successive calls to extract(). + std::vector coordsToExtractBy_; + std::vector::const_iterator nextCoordToExtractBy_; +}; + +} // namespace ufo + +#endif // UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTOR_H_ diff --git a/src/ufo/utils/dataextractor/DataExtractorBackend.h b/src/ufo/utils/dataextractor/DataExtractorBackend.h new file mode 100644 index 000000000..c7cd2531a --- /dev/null +++ b/src/ufo/utils/dataextractor/DataExtractorBackend.h @@ -0,0 +1,37 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORBACKEND_H_ +#define UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORBACKEND_H_ + +#include + +namespace ufo +{ + +struct DataExtractorInput; + +/// \brief Provides data to the DataExtractor. +class DataExtractorBackend { + public: + virtual ~DataExtractorBackend() = default; + + /// \brief Load data for subsequent extraction. + /// + /// \param payloadGroup + /// Group (e.g. ObsBias or ErrorVariance) containing the payload variable, + /// i.e. the variable that will be interpolated. The data source + /// must contain exactly one variable from this group. + /// + /// \returns An object encapsulating the payload variable, all coordinates indexing it + /// and the mapping between dimensions of the payload array and coordinates. + virtual DataExtractorInput loadData(const std::string &payloadGroup) const = 0; +}; + +} // namespace ufo + +#endif // UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORBACKEND_H_ diff --git a/src/ufo/utils/dataextractor/DataExtractorCSVBackend.cc b/src/ufo/utils/dataextractor/DataExtractorCSVBackend.cc new file mode 100644 index 000000000..45490a18d --- /dev/null +++ b/src/ufo/utils/dataextractor/DataExtractorCSVBackend.cc @@ -0,0 +1,220 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include // for move +#include + +#include +#include "Eigen/Core" + +#include "eckit/exception/Exceptions.h" +#include "eckit/parser/CSVParser.h" +#include "eckit/utils/StringTools.h" + +#include "ioda/Misc/StringFuncs.h" // for convertV1PathToV2Path +#include "ioda/ObsSpace.h" // for ObsDtype + +#include "oops/util/Logger.h" + +#include "ufo/utils/dataextractor/DataExtractorCSVBackend.h" +#include "ufo/utils/dataextractor/DataExtractorInput.h" + +namespace ufo +{ + +namespace { + +/// Number of header rows in CSV files. +const size_t numHeaderRows = 2; + +/// Representation of missing values in CSV files (same as in NetCDF's CDL). +const char *missingValuePlaceholder = "_"; + +/// Visitor whose operator() takes a vector and appends to it the value passed to the constructor +/// (treating "_" as a placeholder for "missing value"). +class AppendValueVisitor : public boost::static_visitor { + public: + explicit AppendValueVisitor(const eckit::Value &value) : + value_(value) + {} + + void operator()(std::vector &values) const { + int value; + if (value_.as() == missingValuePlaceholder) + value = util::missingValue(value); + else + // NOLINTNEXTLINE(runtime/int): It's not our fault that eckit uses the 'long long' type... + value = static_cast(value_.as()); + values.push_back(value); + } + + void operator()(std::vector &values) const { + float value; + if (value_.as() == missingValuePlaceholder) + value = util::missingValue(value); + else + value = static_cast(value_.as()); + values.push_back(value); + } + + void operator()(std::vector &values) const { + std::string value = value_.as(); + if (value == missingValuePlaceholder) + value = util::missingValue(value); + values.push_back(value); + } + + private: + const eckit::Value &value_; +}; + +/// Visitor that converts a (numeric) std::vector to an Eigen column vector. +class ConvertToEigenArrayVisitor : public boost::static_visitor { + public: + explicit ConvertToEigenArrayVisitor(Eigen::ArrayXXf &output) : + output_(output) + {} + + template + void operator()(const std::vector &values) { + output_.resize(values.size(), 1); + for (size_t i = 0; i < values.size(); ++i) + output_(i, 0) = values[i]; + } + + void operator()(const std::vector &) { + // Should never be called + throw eckit::NotImplemented(Here()); + } + + private: + Eigen::ArrayXXf &output_; +}; + +/// \brief Find the index of the column whose name ends with `@` followed by `payloadGroup` +/// or begins with `payloadGroup` followed by `/`. +/// +/// Throw an exception if there's no such column or there's more than one. +size_t findPayloadColumn(const std::vector &columnNames, + const std::string &payloadGroup) { + const std::string prefix = payloadGroup + '/'; + const std::string suffix = '@' + payloadGroup; + auto isInPayloadGroup = [&prefix, &suffix](const std::string &name) { + return eckit::StringTools::beginsWith(name, prefix) || + eckit::StringTools::endsWith(name, suffix); + }; + auto payloadColumnIt = std::find_if(columnNames.begin(), columnNames.end(), isInPayloadGroup); + if (payloadColumnIt == columnNames.end()) + throw eckit::UserError("No payload column found: no column name begins with '" + prefix + + "' or ends with '" + suffix + "'", + Here()); + if (std::any_of(payloadColumnIt + 1, columnNames.end(), isInPayloadGroup)) + throw eckit::UserError("Multiple payload candidates found: " + "more than one column name begins with '" + prefix + + "' or ends with '" + suffix + "'", Here()); + return payloadColumnIt - columnNames.begin(); +} + +template +std::vector createColumn(size_t numValues) { + std::vector values; + values.reserve(numValues); + return values; +} + +} // namespace + +DataExtractorCSVBackend::DataExtractorCSVBackend(const std::string &filepath) + : filepath_(filepath) +{} + +DataExtractorInput DataExtractorCSVBackend::loadData( + const std::string &interpolatedArrayGroup) const { + DataExtractorInput result; + + const eckit::Value contents = eckit::CSVParser::decodeFile(filepath_, false /* hasHeader? */); + const size_t numRows = contents.size(); + // Ensure we have at least three lines: + // * column names + // * data types + // * one row of values. + if (numRows <= numHeaderRows) + throw eckit::UserError("No data could be loaded from the file '" + filepath_ + "'", Here()); + const size_t numValues = numRows - numHeaderRows; + + // Read column names from the first line + const eckit::Value nameHeader = contents[0]; + const size_t numColumns = nameHeader.size(); + std::vector columnNames(numColumns); + columnNames.reserve(numColumns); + for (size_t column = 0; column < numColumns; ++column) + columnNames[column] = nameHeader[column].as(); + + const size_t payloadColumnIndex = findPayloadColumn(columnNames, interpolatedArrayGroup); + + // Now that we won't need to include column names in any further error messages, convert + // them to the ioda-v2 convention (Group/var rather than var@Group) + for (std::string &columnName : columnNames) + columnName = ioda::convertV1PathToV2Path(columnName); + + // Read data types from the second line + const eckit::Value typeHeader = contents[1]; + if (typeHeader.size() != numColumns) + throw eckit::UserError("The number of columns in line 2 differs from that in line 1", Here()); + + // Allocate vectors for values to be loaded from subsequent lines + std::vector columns(numColumns); + for (size_t column = 0; column < numColumns; ++column) { + const std::string type = typeHeader[column]; + if (type == "string" || type == "datetime") { + if (column == payloadColumnIndex) + throw eckit::UserError("The payload column must contain numeric data", Here()); + columns[column] = createColumn(numValues); + } else if (type == "int" || type == "integer") { + columns[column] = createColumn(numValues); + } else if (type == "float") { + columns[column] = createColumn(numValues); + } else { + throw eckit::UserError("Unsupported data type '" + type + "'", Here()); + } + } + + // Load values from the rest of the CSV file + for (size_t row = numHeaderRows; row < numRows; ++row) { + const eckit::Value rowContents = contents[row]; + if (rowContents.size() == 1 && rowContents[0] == "") + continue; // empty line + if (rowContents.size() != numColumns) + throw eckit::UserError("The number of columns in line " + std::to_string(1 + row) + + " differs from that in line 1", Here()); + for (size_t column = 0; column < numColumns; ++column) + boost::apply_visitor(AppendValueVisitor(rowContents[column]), columns[column]); + } + + // Store the loaded data in the result object + const int firstDim = 0; + result.dim2CoordMapping.resize(1); + for (size_t column = 0; column < numColumns; ++column) { + if (column == payloadColumnIndex) { + ConvertToEigenArrayVisitor visitor(result.payloadArray); + boost::apply_visitor(visitor, columns[column]); + } else { + result.coordsVals[columnNames[column]] = std::move(columns[column]); + result.coord2DimMapping[columnNames[column]] = firstDim; + result.dim2CoordMapping[firstDim].push_back(columnNames[column]); + } + } + + if (result.payloadArray.rows() == 0) + throw eckit::UserError("No data could be loaded from the file '" + filepath_ + "'", Here()); + + return result; +} + +} // namespace ufo diff --git a/src/ufo/utils/dataextractor/DataExtractorCSVBackend.h b/src/ufo/utils/dataextractor/DataExtractorCSVBackend.h new file mode 100644 index 000000000..1885b7395 --- /dev/null +++ b/src/ufo/utils/dataextractor/DataExtractorCSVBackend.h @@ -0,0 +1,98 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORCSVBACKEND_H_ +#define UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORCSVBACKEND_H_ + +#include + +#include "ufo/utils/dataextractor/DataExtractorBackend.h" + +namespace ufo +{ + +/// \brief Produces input for a DataExtractor by loading data from a CSV file. +/// +/// The file should have the following structure (described in more detail in the example below): +/// * First line: comma-separated column names in ioda-v1 style (`var@Group`) or ioda-v2 style +/// (`Group/var`) +/// * Second line: comma-separated column data types (datetime, float, int or string) +/// * Further lines: comma-separated data entries +/// The number of entries in each line should be the same. +/// +/// Here's an example of a file that could be read by this backend and used for bias correction: +/// +/// station_id@MetaData,air_pressure@MetaData,air_temperature@ObsBias +/// string,float,float +/// ABC,30000,0.1 +/// ABC,60000,0.2 +/// ABC,90000,0.3 +/// XYZ,40000,0.4 +/// XYZ,80000,0.5 +/// +/// One of the columns (above, air_temperature@ObsBias) contains the values to be extracted (also +/// known as the _payload_). The payload column is identified by the group it belongs to, i.e. the +/// part of its name following the `@` sign (ioda-v1 style) or preceding the last `/` sign (ioda-v2 +/// style); this group is specified in the call to the loadData() member function. The values from +/// the other columns (_coordinates_) are compared against ObsSpace variables with the same names +/// to determine the row or rows from which the payload value should be extracted for each +/// observation. The details of this comparison (e.g. whether an exact match is required, the +/// nearest match is used, or piecewise linear interpolation is performed) depend on how the class +/// using the extracted data (e.g. the DrawValueFromFile ObsFunction) is configured. The data type +/// of each column must match the data type of the corresponding ObsSpace variable. The payload +/// column must be of type `float` or `int`. The column order does not matter. +/// +/// Notes: +/// +/// 1. A column containing channel numbers (which aren't stored in a separate ObsSpace variable) +/// should be labelled `channel_number@MetaData` or `MetaData/channel_number`. +/// +/// 2. Single underscores serve as placeholders for missing values; for example, the following row +/// +/// ABC,_,_ +/// +/// contains missing values in the second and third columns. +/// +/// To continue the example above, suppose the file shown earlier is passed to the +/// DrawValueFromFile ObsFunction configured in the following way: +/// +/// name: DrawValueFromFile@ObsFunction +/// options: +/// file: ... # path to the CSV file +/// group: ObsBias # group with the payload variable +/// interpolation: +/// - name: station_id@MetaData +/// method: exact +/// - name: air_pressure@MetaData +/// method: linear +/// +/// For an observation taken by station XYZ at pressure 60000 the function would be evaluated in the +/// following way: +/// * First, find all rows in the CSV file with a value of `XYZ` in the `station_id@MetaData` +/// column. +/// * Then take the values of the `air_pressure@MetaData` and `air_temperature@ObsBias` columns +/// in these rows and use them to construct a piecewise linear interpolant. Evaluate this +/// interpolant at pressure 60000. This produces the value of 0.45. +/// +/// Refer to the documentation of the DrawValueFromFile ObsFunction for more information about the +/// available extraction methods. +class DataExtractorCSVBackend : public DataExtractorBackend { + public: + /// \brief Create a new instance. + /// + /// \param filepath Path to the CSV file that will be read by loadData(). + explicit DataExtractorCSVBackend(const std::string &filepath); + + DataExtractorInput loadData(const std::string &payloadGroup) const override; + + private: + std::string filepath_; +}; + +} // namespace ufo + +#endif // UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORCSVBACKEND_H_ diff --git a/src/ufo/utils/dataextractor/DataExtractorInput.h b/src/ufo/utils/dataextractor/DataExtractorInput.h new file mode 100644 index 000000000..241b01018 --- /dev/null +++ b/src/ufo/utils/dataextractor/DataExtractorInput.h @@ -0,0 +1,46 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORINPUT_H_ +#define UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORINPUT_H_ + +#include +#include +#include + +#include +// cpplint misclassifies this file as a c system include +#include // NOLINT(build/include_order) + +namespace ufo +{ + +/// \brief Input data for the DataExtractor. +/// +/// Note: the names of all coordinates are expected to be of the form `Group/var` (ioda-v2 style) +/// rather than `var@Group` (ioda-v1 style). +struct DataExtractorInput { + /// Array from which values will be extracted + Eigen::ArrayXXf payloadArray; + + typedef boost::variant, + std::vector, + std::vector + > Coordinate; + typedef std::unordered_map Coordinates; + /// Coordinates indexing payloadArray + Coordinates coordsVals; + + /// Maps coordinate names to dimensions (0 or 1) of the payload array + std::unordered_map coord2DimMapping; + /// Maps dimensions of the payload array (0 or 1) to coordinate names + std::vector> dim2CoordMapping; +}; + +} // namespace ufo + +#endif // UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORINPUT_H_ diff --git a/src/ufo/utils/dataextractor/DataExtractorNetCDFBackend.cc b/src/ufo/utils/dataextractor/DataExtractorNetCDFBackend.cc new file mode 100644 index 000000000..1e4289525 --- /dev/null +++ b/src/ufo/utils/dataextractor/DataExtractorNetCDFBackend.cc @@ -0,0 +1,316 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include +#include // pair +#include + +#include +#include "Eigen/Core" +#include "unsupported/Eigen/CXX11/Tensor" + +#include "eckit/exception/Exceptions.h" +#include "eckit/utils/StringTools.h" + +#include "ioda/Engines/HH.h" +#include "ioda/Group.h" +#include "ioda/Misc/StringFuncs.h" // for convertV1PathToV2Path + +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" + +#include "ufo/utils/dataextractor/DataExtractorInput.h" +#include "ufo/utils/dataextractor/DataExtractorNetCDFBackend.h" + +namespace ufo +{ + +namespace { + +/// \brief Helper function for determining the dimension mapping names for a variable +std::vector fetchDimNameMapping( + const ioda::Variable &variable, + const std::string &varName, + const std::list &coordinates) { + std::vector dimnames; + if (variable.isDimensionScale()) { + // Variable corresponds to the dimension name so maps to itself (it's a dimension coordinate). + dimnames = {varName}; + } else { + // Lookup the dimension mapping for this variable. + // Loop over the dimension scales associated with a particular dimension. + auto dsm = variable.getDimensionScaleMappings(coordinates); + for (const auto &item : dsm) { + // There shouldn't be more than 1 dimension scale per dimension. + if (item.size() != 1) + throw eckit::Exception("Variable '" + varName + "' has a multi-dimensional coordinate. " + "This is not supported.", Here()); + dimnames.emplace_back(item[0].name); + } + } + return dimnames; +} + +/// \brief Add variable `var` of type `T` to `coordsVals` under key `key`. +template +void updateVariable(const std::string &key, ioda::Variable var, + DataExtractorInput::Coordinates &coordsVals) { + // Read the variable from the input file + std::vector values = var.readAsVector(); + + // Replace source fill values with corresponding missing marks + if (var.hasFillValue()) { + ioda::detail::FillValueData_t sourceFvData = var.getFillValue(); + const T sourceFillValue = ioda::detail::getFillValue(sourceFvData); + // TODO(someone): This won't call the correct overload for datetimes (which are treated + // as strings by ioda-engines). + const T oopsFillValue = util::missingValue(oopsFillValue); + if (oopsFillValue != sourceFillValue) + std::replace(values.begin(), values.end(), sourceFillValue, oopsFillValue); + } + + // Store the variable in coordsVals_ + coordsVals.emplace(key, std::move(values)); +} + +/// \brief Add variable `var` to `coordsVals` under key `key`. +void update(const std::string &key, ioda::Variable var, + DataExtractorInput::Coordinates &coordsVals) { + if (var.isA()) { + updateVariable(key, var, coordsVals); + } else if (var.isA()) { + updateVariable(key, var, coordsVals); + } else if (var.isA()) { + updateVariable(key, var, coordsVals); + } else { + throw eckit::Exception("Data type not yet supported.", Here()); + } +} + +/// \brief Helper function to determine dimension index from a dimension name +std::vector getDimMapping(const std::vector &dimnames, + const std::vector &interpolatedArrayDimnames) { + std::vector dimIndex; + // Loop over dimension names provided + for (const std::string & dimname : dimnames) { + int ind = -1; + for (const std::string &obsDimname : interpolatedArrayDimnames) { + ind += 1; + if (dimname == obsDimname) { + dimIndex.emplace_back(ind); + } + } + } + if (dimIndex.size() != dimnames.size()) { + oops::Log::debug() << "One or more of the dimension names provided do not map to the " + "array to be interpolated: "; + for (std::string dimname : dimnames) oops::Log::debug() << dimname << " "; + oops::Log::debug() << std::endl; + throw eckit::Exception( + "Dimension mappings not associated with the array to be interpolated are not " + "currently supported.", Here()); + } + return dimIndex; +} + +/// \brief Return the name of the unique variable whose name ends with `@` followed by +/// `payloadGroup` or begins with `payloadGroup` followed by '/'. +/// +/// Throw an exception if there's no such variable or there's more than one. +const std::string &findPayloadVariable(const std::vector &varNames, + const std::string &payloadGroup) { + const std::string prefix = payloadGroup + '/'; + const std::string suffix = '@' + payloadGroup; + auto isInPayloadGroup = [&prefix, &suffix](const std::string &name) { + return eckit::StringTools::beginsWith(name, prefix) || + eckit::StringTools::endsWith(name, suffix); + }; + auto payloadVarIt = std::find_if(varNames.begin(), varNames.end(), isInPayloadGroup); + if (payloadVarIt == varNames.end()) + throw eckit::UserError("No payload column found: no column name begins with '" + prefix + + "' or ends with '" + suffix + "'", + Here()); + if (std::any_of(payloadVarIt + 1, varNames.end(), isInPayloadGroup)) + throw eckit::UserError("Multiple payload candidates found: " + "more than one column name begins with '" + prefix + + "' or ends with '" + suffix + "'", Here()); + return *payloadVarIt; +} + +} // namespace + +DataExtractorNetCDFBackend::DataExtractorNetCDFBackend(const std::string &filepath) + : filepath_(filepath) +{} + +DataExtractorInput DataExtractorNetCDFBackend::loadData( + const std::string &interpolatedArrayGroup) const { + DataExtractorInput result; + + // Open the input file + const ioda::Group group = ioda::Engines::HH::openFile( + filepath_, ioda::Engines::BackendOpenModes::Read_Only); + + // All our coords from the file. + std::vector vars = group.listObjects(ioda::ObjectType::Variable, true) + .at(ioda::ObjectType::Variable); + + // Find the array to be interpolated + std::string interpolatedArrayName = findPayloadVariable(vars, interpolatedArrayGroup); + + // Loop over ALL coords and fetch the corresponding Variable object. + std::unordered_map coords; + std::list lcoords; + for (const auto &coord_name : vars) { + ioda::Variable cvar = group.vars[coord_name]; + coords.emplace(coord_name, cvar); + ioda::Named_Variable v{coord_name, cvar}; + lcoords.push_back(v); + } + + // Process the metadata of the array to be interpolated + // -------------------------------- + const ioda::Variable &interpolatedArrayCoord = coords[interpolatedArrayName]; + // The variable to be interpolated shouldn't be a dimension mapping + ASSERT(interpolatedArrayCoord.isDimensionScale() == false); + // Fetch the dimension mapping names for this array + std::vector interpolatedArrayDimnames = fetchDimNameMapping( + interpolatedArrayCoord, interpolatedArrayName, lcoords); + const ioda::Dimensions dimdim = interpolatedArrayCoord.getDimensions(); + + // Does the array to be interpolated represent a full covariance matrix (or a stack of them)? + // If so, we'll need to extract diagonals. + // -------------------------------- + std::string isCovariant {"false"}; + if (interpolatedArrayCoord.atts.exists("full")) { + interpolatedArrayCoord.atts["full"].read(isCovariant); + } + + // Remove the array to be interpolated from our list of coords + vars.erase(std::remove(vars.begin(), vars.end(), interpolatedArrayName), vars.end()); + + // -------------------------------- + // Fetch dimension mappings for our NetCDF variables. Store them initially in more generic + // containers than those used by result.coord2DimMapping and result.dim2CoordMapping; later + // we'll post-process them and make sure they can be stored in the corresponding member variables + // of 'result'. + + // Maps coordinate names to dimensions (0 or 1) of the payload array + std::unordered_map> coord2DimMapping; + // Maps dimensions of the payload array (0 or 1) to coordinate names + std::unordered_map> dim2CoordMapping; + // Coordinate values + DataExtractorInput::Coordinates coordsVals; + + for (const std::string& varName : vars) { + const ioda::Variable &variable = coords[varName]; + std::vector dimnames = fetchDimNameMapping(variable, varName, lcoords); + + // Load our variables data and keep hold of it along with relevant information. + update(varName, variable, coordsVals); + + // Create a mapping between name and array dimension and vice versa. + std::vector ddim = getDimMapping(dimnames, interpolatedArrayDimnames); + for (int dim : ddim) { + dim2CoordMapping[dim].emplace_back(varName); + } + coord2DimMapping[varName] = ddim; + } + + // Load the array to be interpolated + // -------------------------------- + // NOTE: + // - We might want to eventually put together a more generalised approach (a collapse + // method taking the dimension as argument) + // - We might also want to not assume that the array is ordered? - by using + // coordinate info. though I'm not sure why it wouldn't be... + if (isCovariant == "true") { + // This is a full matrix or a stack of full matrices - pull out the diagonals + // -------------------------------- + int dimCollapse = 0; // Dimension to collapse + ASSERT(dimdim.dimsCur[0] == dimdim.dimsCur[1]); // Sanity check the matrix is square. + + if (dimdim.dimensionality == 3) { + // ioda::Variable requires us to define the size of the tensor beforehand + // We use row major ordering so that it resemblence handling the NetCDF data structure. + Eigen::Tensor data(dimdim.dimsCur[0], dimdim.dimsCur[1], + dimdim.dimsCur[2]); + interpolatedArrayCoord.readWithEigenTensor(data); + result.payloadArray.resize(dimdim.dimsCur[1], dimdim.dimsCur[2]); + + for (int i = 0; i < data.dimension(0); i++) { + for (int k = 0; k < data.dimension(2); k++) { + result.payloadArray(i, k) = data(i, i, k); + } + } + } else if (dimdim.dimensionality == 2) { + Eigen::ArrayXXf data; + interpolatedArrayCoord.readWithEigenRegular(data); + result.payloadArray.resize(dimdim.dimsCur[1], 1); + + for (int i = 0; i < data.rows(); i++) { + result.payloadArray(i, 0) = data(i, i); + } + } else { + throw eckit::Exception("Expecting 3D or 2D array for error covariance.", Here()); + } + + // Update our dimension mapping after collapsing a dimension. + // -------------------------------- + // Remove the outermost dimension mapping of our data array. + interpolatedArrayDimnames.erase(interpolatedArrayDimnames.begin()); + // Now update all affected coordinates (those who vary with dim0). + for (const std::string &coord : dim2CoordMapping.at(dimCollapse)) + coord2DimMapping.erase(coord); + dim2CoordMapping.clear(); + for (auto &coord : coord2DimMapping) { + // Shift remaining dimension mappings since the collapse + for (int &dim : coord.second) { + dim--; + dim2CoordMapping[dim].emplace_back(coord.first); // update our reverse lookup + } + } + } else if ((dimdim.dimensionality == 2) || (dimdim.dimensionality == 1)) { + // This is just the diagonals - read directly into our container + // -------------------------------- + interpolatedArrayCoord.readWithEigenRegular(result.payloadArray); + } else { + throw eckit::Exception("The array to be interpolated has an unsupported number of dimensions.", + Here()); + } + + // Store the coordinate values and dimension mappings in 'result', converting any ioda-v1-style + // variable names (var@Group) to ioda-v2 style (Group/var) + + for (auto &coordAndVals : coordsVals) + result.coordsVals[ioda::convertV1PathToV2Path(coordAndVals.first)] = coordAndVals.second; + + for (const auto &coordAndDims : coord2DimMapping) { + if (coordAndDims.second.size() != 1) + throw eckit::Exception("Coordinate '" + coordAndDims.first + + "'is used to index multiple dimensions of the payload array", Here()); + result.coord2DimMapping[ioda::convertV1PathToV2Path(coordAndDims.first)] = + coordAndDims.second.front(); + } + + if (!dim2CoordMapping.empty()) { + const int maxDimIndex = std::max_element(dim2CoordMapping.begin(), + dim2CoordMapping.end())->first; + result.dim2CoordMapping.resize(maxDimIndex + 1); + for (const auto &dimAndCoords : dim2CoordMapping) { + result.dim2CoordMapping[dimAndCoords.first] = dimAndCoords.second; + for (std::string &varName : result.dim2CoordMapping[dimAndCoords.first]) + varName = ioda::convertV1PathToV2Path(varName); + } + } + + return result; +} + +} // namespace ufo diff --git a/src/ufo/utils/dataextractor/DataExtractorNetCDFBackend.h b/src/ufo/utils/dataextractor/DataExtractorNetCDFBackend.h new file mode 100644 index 000000000..9b7f1321e --- /dev/null +++ b/src/ufo/utils/dataextractor/DataExtractorNetCDFBackend.h @@ -0,0 +1,128 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORNETCDFBACKEND_H_ +#define UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORNETCDFBACKEND_H_ + +#include + +#include "ufo/utils/dataextractor/DataExtractorBackend.h" + +namespace ufo +{ + +/// \brief Produces input for a DataExtractor by loading data from a NetCDF file. +/// +/// ioda-v1 and ioda-v2-style NetCDF files are supported. ioda-v1-style files should have the +/// following structure: +/// - It should contain exactly one variable whose name ends with the `@` suffix, where +/// is the value of the `payloadGroup` parameter passed to the loadData() method. +/// This is the variable containing the values to be extracted (also known as the _payload_). +/// It should be of type `float`. +/// - For most types of data, the above array should be 1D or 2D. As a special case, +/// if this class is used to extract variances, the file may contain a full 2D covariance +/// matrix or a stack of such matrices stored as a 3D array, with variances located on the +/// diagonal of each matrix. In that case, the array should be equipped with a `full` attribute +/// of type `string` set to `"true"`. Only the diagonal elements (`A[i, i]` for a 2D array and +/// `A[i, i, k]` for a 3D array) will be used during the data extraction process. +/// - 1D coordinates must be defined for each dimension of the payload array. +/// Coordinates can be of type `float`, `int` or `string`. Datetimes should be represented +/// as ISO 8601 strings. Auxiliary coordinates are supported, i.e. there can be more than one +/// coordinate per dimension. Coordinate names should correspond to names of ObsSpace variables. +/// Use the name `channel_number@MetaData` for channel numbers (for which there's no dedicated +/// ObsSpace variable). +/// +/// ioda-v2-style files are similar except that +/// - The payload variable should be placed in the NetCDF group specified by the `payloadGroup` +/// parameter passed to loadData(). +/// - Coordinate variables should be placed in appropriate groups, e.g. `MetaData`. Because +/// of the limitations of the NetCDF file format, these variables can only be used as auxiliary +/// coordinates of the payload variable (listed in its `coordinates` attribute). +/// +/// Example 1: the following NetCDF metadata describe a ioda-v1-style file encoding the dependence +/// of the diagonal elements of a covariance matrix (with rows and columns corresponding to certain +/// air pressures) on the observation type: +/// +/// \code +/// dimensions: +/// air_pressure@MetaData = 10 ; +/// index = 5 ; +/// variables: +/// int air_temperature@ErrorVariance(air_pressure@MetaData, index) ; +/// air_temperature@ErrorVariance:coordinates = "observation_type@MetaData index" ; +/// int air_pressure@MetaData(air_pressure@MetaData) ; +/// int index(index) ; +/// int observation_type@MetaData(index) ; +/// \endcode +/// +/// Example 2: the following NetCDF metadata describe a ioda-v1-style file encoding the dependence +/// of a full covariance matrix (with rows and columns corresponding to channel numbers) on +/// multiple variables (`latitude_band@MetaData`, `processing_center@MetaData` and +/// `satellite_id@MetaData(index)`): +/// +/// \code +/// dimensions: +/// channel_number = 10 ; +/// channel_number@MetaData = 10 ; +/// index = 8 ; +/// variables: +/// float air_temperature@ErrorVariance(channel_number, channel_number@MetaData, index) ; +/// air_temperature@ErrorVariance:coordinates = "latitude_band@MetaData \ +/// processing_center@MetaData satellite_id@MetaData" ; +/// string air_temperature@ErrorVariance:full = "true" ; +/// int index(index) ; +/// int channel_number(channel_number) ; +/// int channel_number@MetaData(channel_number@MetaData) ; +/// int latitude_band@MetaData(index) ; +/// int processing_center@MetaData(index) ; +/// int satellite_id@MetaData(index) ; +/// \endcode +/// +/// Notice how a channel_number describes the outermost two dimensions. The very outermost +/// dimension is collapsed and discarded after extracting the diagonal elements of the covariance +/// matrices, so its name can be arbitrary. (This lets us stay conforming to the CF convention, +/// which forbids multiple axes of an array to be indexed by the same coordinate.) +/// +/// Example 3: the following NetCDF metadata describe a ioda-v2-style file equivalent to the one +/// from Example 1: +/// +/// \code +/// dimensions: +/// rows = 10 ; +/// index = 5 ; +/// variables: +/// int rows(rows) ; +/// int index(index) ; +/// +/// group: MetaData { +/// variables: +/// int air_pressure(rows) ; +/// int observation_type(index) ; +/// } +/// +/// group: ErrorVariance { +/// variables: +/// float air_temperature(rows, index) ; +/// air_temperature:coordinates = "/MetaData/air_pressure /MetaData/observation_type" ; +/// } +/// \endcode +class DataExtractorNetCDFBackend : public DataExtractorBackend { + public: + /// \brief Create a new instance. + /// + /// \param filepath Path to the NetCDF file that will be read by loadData(). + explicit DataExtractorNetCDFBackend(const std::string &filepath); + + DataExtractorInput loadData(const std::string &payloadGroup) const override; + + private: + std::string filepath_; +}; + +} // namespace ufo + +#endif // UFO_UTILS_DATAEXTRACTOR_DATAEXTRACTORNETCDFBACKEND_H_ diff --git a/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.cc b/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.cc new file mode 100644 index 000000000..e54c1045b --- /dev/null +++ b/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.cc @@ -0,0 +1,75 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/utils/metoffice/MetOfficeBMatrixStatic.h" +#include "oops/util/Logger.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +/// \brief Constructor +MetOfficeBMatrixStatic::MetOfficeBMatrixStatic(const eckit::Configuration & config): + nbands_(0), nelements_(0), southlimits_(), northlimits_(), elements_() +{ + oops::Log::trace() << "MetOfficeBMatrixStatic constructor starting" << std::endl; + + // Read bmatrix file into Fortran object + ufo_metoffice_bmatrixstatic_setup_f90(keyMetOfficeBMatrixStatic_, config, nbands_, nelements_); + + // Map Fortran data to c++ + northlimits_.resize(nbands_); + southlimits_.resize(nbands_); + size_t ntotal = nelements_ * nelements_ * nbands_; + std::vector Btotal(ntotal); + ufo_metoffice_bmatrixstatic_getelements_f90(keyMetOfficeBMatrixStatic_, + nelements_, nbands_, southlimits_.data(), + northlimits_.data(), Btotal.data()); + + // For each of nbands, store B matrix in container + for (size_t i = 0; i < nbands_; ++i) { + Eigen::Map bmap(Btotal.data()+i*nelements_*nelements_, + nelements_, nelements_); + elements_.push_back(bmap); + } + + // Remove the Fortran object because it is no longer needed + ufo_metoffice_bmatrixstatic_delete_f90(keyMetOfficeBMatrixStatic_); + + oops::Log::trace() << "MetOfficeBMatrixStatic constructor end" << std::endl; +} +// ----------------------------------------------------------------------------- +/// \brief Return bmatrix size (number of rows or columns of square matrix) +size_t MetOfficeBMatrixStatic::getsize(void) const { + return nelements_; +} +// ----------------------------------------------------------------------------- +/// \brief Find bmatrix band index for a given latitude +size_t MetOfficeBMatrixStatic::getindex(const float latitude) const { + auto lower = std::lower_bound(northlimits_.begin(), northlimits_.end(), latitude); + return std::distance(northlimits_.begin(), lower); +} +// ----------------------------------------------------------------------------- +/// \brief Multiply input matrix by bmatrix array based on latitude +void MetOfficeBMatrixStatic::multiply(const float lat, + const Eigen::MatrixXf & in, + Eigen::MatrixXf & out) const { + size_t index = this->getindex(lat); + out = elements_[index] * in; +} + +// ----------------------------------------------------------------------------- +/// \brief Print +void MetOfficeBMatrixStatic::print(std::ostream & os) const { + os << "MetOfficeBMatrixStatic: start print" << std::endl; + os << "nbands_ = " << nbands_ << std::endl; + os << "nelements_ = " << nelements_ << std::endl; + os << "southlimits_[0] = " << southlimits_[0] << std::endl; + os << "northlimits_[0] = " << northlimits_[0] << std::endl; + os << "MetOfficeBMatrixStatic: end print" << std::endl; +} +// ----------------------------------------------------------------------------- +} // namespace ufo diff --git a/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.h b/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.h new file mode 100644 index 000000000..d5f58f258 --- /dev/null +++ b/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.h @@ -0,0 +1,58 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_METOFFICE_METOFFICEBMATRIXSTATIC_H_ +#define UFO_UTILS_METOFFICE_METOFFICEBMATRIXSTATIC_H_ + +#include + +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "oops/util/Printable.h" +#include "ufo/utils/metoffice/MetOfficeBMatrixStatic.interface.h" + +namespace eckit { + class Configuration; +} + +namespace ufo { + +// ----------------------------------------------------------------------------- +/// MetOfficeBMatrixStatic: Met Office static model covariance +/// This class provides access to the static b matrix used for radiance +/// processing by the Met Office. The objects main method is to multiply +/// an eigen matrix by the bmatrix +// ----------------------------------------------------------------------------- + +class MetOfficeBMatrixStatic : public util::Printable, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::MetOfficeBMatrixStatic";} + + explicit MetOfficeBMatrixStatic(const eckit::Configuration &); + + size_t getindex(const float) const; + size_t getsize(void) const; + void multiply(const float, const Eigen::MatrixXf &, Eigen::MatrixXf &) const; + + private: + void print(std::ostream &) const override; + F90obfilter keyMetOfficeBMatrixStatic_; // key to Fortran for B + size_t nbands_; // number of latitude bands for B + size_t nelements_; // number of elements in each dimension of B + std::vector southlimits_; // southern latitude limit per band + std::vector northlimits_; // northern latitude limit per band + std::vector elements_; // container for B contents +}; + +} // namespace ufo + +#endif // UFO_UTILS_METOFFICE_METOFFICEBMATRIXSTATIC_H_ diff --git a/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.interface.F90 b/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.interface.F90 new file mode 100644 index 000000000..7804b2320 --- /dev/null +++ b/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.interface.F90 @@ -0,0 +1,121 @@ +!------------------------------------------------------------------------------- +! (C) Crown Copyright 2021 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +module ufo_metoffice_bmatrixstatic_mod_c + +use fckit_configuration_module, only: fckit_configuration +use iso_c_binding +use kinds +use oops_variables_mod +use ufo_metoffice_bmatrixstatic_mod +use ufo_vars_mod + +implicit none + +private + +#define LISTED_TYPE ufo_metoffice_bmatrixstatic + +!> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + +!> Global registry +type(registry_t) :: ufo_metoffice_bmatrixstatic_registry + +contains + +!> Linked list implementation +#include "oops/util/linkedList_c.f" + +!------------------------------------------------------------------------------- +subroutine ufo_metoffice_bmatrixstatic_setup_c(c_self, c_conf, & + nbands, nelements) & + bind(c, name='ufo_metoffice_bmatrixstatic_setup_f90') + +implicit none +integer(c_int), intent(inout) :: c_self +type(c_ptr), value, intent(in) :: c_conf +integer(c_size_t), intent(inout) :: nbands ! number of latitude bands in B-matrix file +integer(c_size_t), intent(inout) :: nelements ! number of elements per B-matrix dimension + +type(ufo_metoffice_bmatrixstatic), pointer :: self +type(fckit_configuration) :: f_conf +character(len=:), allocatable :: str +character(len=:), allocatable :: str_array(:) +logical :: qtotal_flag +character(len=200) :: filepath +integer :: varsize +character(len=200), allocatable :: background_fields(:) + +! Interface and setup +call ufo_metoffice_bmatrixstatic_registry % setup(c_self, self) + +! Get filepath from configuration +f_conf = fckit_configuration(c_conf) +call f_conf % get_or_die("BMatrix", str) +filepath = str + +! Get variables from configuration +varsize = f_conf % get_size("background fields") +allocate(background_fields(varsize)) +call f_conf % get_or_die("background fields", str_array) +background_fields(1:varsize) = str_array + +! Get qtotal from configuration +call f_conf % get_or_die("qtotal", qtotal_flag) + +! Call Fortran +call self % setup(background_fields, trim(filepath), qtotal_flag) + +! B-matrix has dimensions (nelements, nelements, nbands) +nbands = self % nbands +nelements = size(self % store, 1) + +end subroutine ufo_metoffice_bmatrixstatic_setup_c + +!------------------------------------------------------------------------------- +subroutine ufo_metoffice_bmatrixstatic_delete_c(c_self) & + bind(c, name='ufo_metoffice_bmatrixstatic_delete_f90') + +implicit none +integer(c_int), intent(inout) :: c_self + +! Interface and setup +type(ufo_metoffice_bmatrixstatic), pointer :: self +call ufo_metoffice_bmatrixstatic_registry % get(c_self, self) + +! Delete +call self % delete() +call ufo_metoffice_bmatrixstatic_registry % delete(c_self, self) + +end subroutine ufo_metoffice_bmatrixstatic_delete_c + +!------------------------------------------------------------------------------- +! Extract elements of B-matrix given its dimensions +subroutine ufo_metoffice_bmatrixstatic_getelements_c(c_self, nelements, nbands, south, north, & + bmatrix_store) bind(C, name='ufo_metoffice_bmatrixstatic_getelements_f90') + +implicit none +integer(c_int), intent(inout) :: c_self +integer(c_size_t), intent(in) :: nelements +integer(c_size_t), intent(in) :: nbands +real(c_float), intent(inout) :: south(nbands) +real(c_float), intent(inout) :: north(nbands) +real(c_float), intent(inout) :: bmatrix_store(nelements,nelements,nbands) + +type(ufo_metoffice_bmatrixstatic), pointer :: self +call ufo_metoffice_bmatrixstatic_registry % get(c_self, self) + +south = self % south(1:nbands) +north = self % north(1:nbands) +bmatrix_store = real(self % store, kind=kind_single) + +end subroutine ufo_metoffice_bmatrixstatic_getelements_c + +!------------------------------------------------------------------------------- + +end module ufo_metoffice_bmatrixstatic_mod_c diff --git a/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.interface.h b/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.interface.h new file mode 100644 index 000000000..7fcd4aac4 --- /dev/null +++ b/src/ufo/utils/metoffice/MetOfficeBMatrixStatic.interface.h @@ -0,0 +1,32 @@ +/* + * (C) Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_METOFFICE_METOFFICEBMATRIXSTATIC_INTERFACE_H_ +#define UFO_UTILS_METOFFICE_METOFFICEBMATRIXSTATIC_INTERFACE_H_ + +#include "oops/base/Variables.h" +#include "ufo/Fortran.h" + +// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ufo { + +/// Interface to Fortran routines +extern "C" { + void ufo_metoffice_bmatrixstatic_setup_f90(F90obfilter &, const eckit::Configuration &, + size_t &, size_t &); + void ufo_metoffice_bmatrixstatic_delete_f90(F90obfilter &); + void ufo_metoffice_bmatrixstatic_getelements_f90(F90obfilter &, const size_t &, + const size_t &, float *, float *, float *); +} // extern C + +} // namespace ufo + +#endif // UFO_UTILS_METOFFICE_METOFFICEBMATRIXSTATIC_INTERFACE_H_ diff --git a/src/ufo/utils/metoffice/MetOfficeQCFlags.h b/src/ufo/utils/metoffice/MetOfficeQCFlags.h index 731e2eb99..986c1c490 100644 --- a/src/ufo/utils/metoffice/MetOfficeQCFlags.h +++ b/src/ufo/utils/metoffice/MetOfficeQCFlags.h @@ -124,6 +124,25 @@ namespace MetOfficeQCFlags { DiurnalWarmFlag = 1 << 13 ///< Indicates a likely diurnal warming component in signal }; + // Profile flags which depend on sounding data type + enum Sounding { + // TEMP and PILOT + TEMPSigWind = 1 << 1, ///< Significant wind level + TEMPSigTemp = 1 << 2, ///< Significant temperature level + TEMPMaxWind = 1 << 3, ///< Maximum wind level + TEMPTropopause = 1 << 4, ///< Tropopause level + TEMPStandard = 1 << 5, ///< Standard level + TEMPSurface = 1 << 6, ///< Surface level + TEMPStandardX = 1 << 7, ///< Semi-standard level + // BUFR + BUFRSigWind = 1 << 11, ///< Significant wind level + BUFRSigTemp = 1 << 13, ///< Significant temperature level + BUFRMaxWind = 1 << 14, ///< Maximum wind level + BUFRTropopause = 1 << 15, ///< Tropopause level + BUFRStandard = 1 << 16, ///< Standard level + BUFRSurface = 1 << 17, ///< Surface level + BUFRStandardX = 1 << 16 ///< Semi-standard level, grouped with standard levels in this case + }; } // namespace MetOfficeQCFlags } // namespace ufo diff --git a/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.cc b/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.cc new file mode 100644 index 000000000..a737c9521 --- /dev/null +++ b/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.cc @@ -0,0 +1,97 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include + +#include "oops/util/abor1_cpp.h" +#include "oops/util/Logger.h" +#include "ufo/utils/metoffice/MetOfficeRMatrixRadiance.h" + +namespace ufo { + +// ----------------------------------------------------------------------------- +/// \brief Constructor +MetOfficeRMatrixRadiance::MetOfficeRMatrixRadiance(const eckit::Configuration & config): + nchans_(0), wmoid_(0), rtype_(0), channels_(), errors_() +{ + oops::Log::trace() << "MetOfficeRMatrixRadiance constructor starting" << std::endl; + + // Read rmatrix file into Fortran object + ufo_metoffice_rmatrixradiance_setup_f90(keyMetOfficeRMatrixRadiance_, config, + nchans_, wmoid_, rtype_); + + // Only diagonal setup at the moment + // OPS 1=full; 2=diagonal; 3=band diagonal + if (rtype_ != 2) { + ABORT("R-matrix type not currently in use - only diagonal"); + } + + // Map Fortran data to c++ + std::vector chans_data(nchans_); + std::vector elements_data(nchans_); + ufo_metoffice_rmatrixradiance_getelements_f90(keyMetOfficeRMatrixRadiance_, nchans_, + chans_data.data(), elements_data.data()); + channels_ = chans_data; + errors_ = elements_data; + + // Remove Fortran object as no longer needed + ufo_metoffice_rmatrixradiance_delete_f90(keyMetOfficeRMatrixRadiance_); + + oops::Log::trace() << "MetOfficeRMatrixRadiance constructor end" << std::endl; +} +// ----------------------------------------------------------------------------- +/// \brief Add r matrix variance onto input array +void MetOfficeRMatrixRadiance::add(const std::vector & chans_used, + const Eigen::MatrixXf & in, + Eigen::MatrixXf & out) const { + int matrows = in.rows(); + int matcols = in.cols(); + oops::Log::debug() << "matrows, matcols = " << matrows << " " << matcols << std::endl; + assert(matrows == chans_used.size()); + assert(matcols == chans_used.size()); + out = in; + if (rtype_ == 2) { + for (size_t ichan = 0; ichan < chans_used.size(); ++ichan) { + auto it = std::find(channels_.begin(), channels_.end(), chans_used[ichan]); + if (it == channels_.end()) { + oops::Log::error() << "Channel not found in R-matrix: " + << chans_used[ichan] << std::endl; + ABORT("Invalid channel specified for R-matrix"); + } else { + size_t index = it - channels_.begin(); + out(ichan, ichan) += errors_[index] * errors_[index]; + } + } + } else { + ABORT("R-matrix type not currently in use - only diagonal"); + } +} +// ----------------------------------------------------------------------------- +/// \brief Print +void MetOfficeRMatrixRadiance::print(std::ostream & os) const { + os << "MetOfficeRMatrixRadiance: print starting" << std::endl; + os << "nchans_ = " << nchans_ << std::endl; + os << "wmoid_ = " << wmoid_ << std::endl; + os << "rtype_ = " << rtype_ << std::endl; + if (nchans_ < 30) { + os << "channels = "; + for (std::vector::const_iterator i = channels_.begin(); i != channels_.end(); ++i) + os << *i << ' '; + os << std::endl; + os << "errors (stdevs) = "; + for (std::vector::const_iterator i = errors_.begin(); i != errors_.end(); ++i) + os << *i << ' '; + os << std::endl; + } else { + os << "channels[0] = " << channels_[0] << std::endl; + os << "errors[0] (stdev) = " << errors_[0] << std::endl; + } + os << "MetOfficeRMatrixRadiance: print end" << std::endl; +} +// ----------------------------------------------------------------------------- +} // namespace ufo diff --git a/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.h b/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.h new file mode 100644 index 000000000..a31a9a075 --- /dev/null +++ b/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.h @@ -0,0 +1,53 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_METOFFICE_METOFFICERMATRIXRADIANCE_H_ +#define UFO_UTILS_METOFFICE_METOFFICERMATRIXRADIANCE_H_ + +#include +#include +#include + +#include "oops/base/Variables.h" +#include "oops/util/ObjectCounter.h" +#include "oops/util/Printable.h" +#include "ufo/utils/metoffice/MetOfficeRMatrixRadiance.interface.h" + +namespace eckit { + class Configuration; +} + +namespace ufo { + +// ----------------------------------------------------------------------------- +/// MetOfficeRMatrixStatic: Met Office static model covariance +/// This class provides access to the static r matrix used for radiance +/// processing by the Met Office. +// ----------------------------------------------------------------------------- + +class MetOfficeRMatrixRadiance : public util::Printable, + private util::ObjectCounter { + public: + static const std::string classname() {return "ufo::MetOfficeRMatrixRadiance";} + + explicit MetOfficeRMatrixRadiance(const eckit::Configuration &); + + void add(const std::vector &, const Eigen::MatrixXf &, Eigen::MatrixXf &) const; + + private: + void print(std::ostream &) const override; + F90obfilter keyMetOfficeRMatrixRadiance_; + size_t nchans_; + size_t wmoid_; + size_t rtype_; + std::vector channels_; + std::vector errors_; +}; + +} // namespace ufo + +#endif // UFO_UTILS_METOFFICE_METOFFICERMATRIXRADIANCE_H_ diff --git a/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.interface.F90 b/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.interface.F90 new file mode 100644 index 000000000..bc576827b --- /dev/null +++ b/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.interface.F90 @@ -0,0 +1,119 @@ +!------------------------------------------------------------------------------- +! (C) Crown Copyright 2021 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +module ufo_metoffice_rmatrixradiance_mod_c + +use fckit_configuration_module, only: fckit_configuration +use iso_c_binding +use kinds +use ufo_metoffice_rmatrixradiance_mod + +implicit none + +private + +#define LISTED_TYPE ufo_metoffice_rmatrixradiance + +!> Linked list interface - defines registry_t type +#include "oops/util/linkedList_i.f" + +!> Global registry +type(registry_t) :: ufo_metoffice_rmatrixradiance_registry + +contains + +!> Linked list implementation +#include "oops/util/linkedList_c.f" + +!------------------------------------------------------------------------------- +subroutine ufo_metoffice_rmatrixradiance_setup_c(c_self, c_conf, nchans, wmoid, rtype) & + bind(c, name='ufo_metoffice_rmatrixradiance_setup_f90') + +implicit none +integer(c_int), intent(inout) :: c_self +type(c_ptr), value, intent(in) :: c_conf +integer(c_size_t), intent(inout) :: nchans +integer(c_size_t), intent(inout) :: wmoid +integer(c_size_t), intent(inout) :: rtype + +type(ufo_metoffice_rmatrixradiance), pointer :: self +type(fckit_configuration) :: f_conf +character(len=:), allocatable :: str +character(len=200) :: filepath + +! Interface and setup +call ufo_metoffice_rmatrixradiance_registry % setup(c_self, self) + +! Get filepath from configuration +f_conf = fckit_configuration(c_conf) +call f_conf % get_or_die("RMatrix", str) +filepath = str + +! Call Fortran +call self % setup(trim(filepath)) + +! Pass back dimension and wmoid +nchans = self % nchans +wmoid = self % wmo_id +rtype = self % rtype + +end subroutine ufo_metoffice_rmatrixradiance_setup_c + +!------------------------------------------------------------------------------- +subroutine ufo_metoffice_rmatrixradiance_delete_c(c_self) & + bind(c, name='ufo_metoffice_rmatrixradiance_delete_f90') + +implicit none +integer(c_int), intent(inout) :: c_self + +! Interface and setup +type(ufo_metoffice_rmatrixradiance), pointer :: self +call ufo_metoffice_rmatrixradiance_registry % get(c_self, self) + +! Delete +call self % delete() +call ufo_metoffice_rmatrixradiance_registry % delete(c_self, self) + +end subroutine ufo_metoffice_rmatrixradiance_delete_c + +!------------------------------------------------------------------------------- +subroutine ufo_metoffice_rmatrixradiance_getelements_c(c_self, nchans, channels, obs_error) & + bind(C, name='ufo_metoffice_rmatrixradiance_getelements_f90') + +implicit none +integer(c_int), intent(inout) :: c_self +integer(c_size_t), intent(in) :: nchans +integer(c_int), intent(inout) :: channels(nchans) +real(c_float), intent(inout) :: obs_error(nchans) + +type(ufo_metoffice_rmatrixradiance), pointer :: self + +call ufo_metoffice_rmatrixradiance_registry%get(c_self, self) + +channels = self % channels(1:nchans) +obs_error = self % errors(1:nchans) + +end subroutine ufo_metoffice_rmatrixradiance_getelements_c + +!------------------------------------------------------------------------------- +subroutine ufo_metoffice_rmatrixradiance_print_c(c_self) & + bind(c, name='ufo_metoffice_rmatrixradiance_print_f90') + +implicit none +integer(c_int), intent(inout) :: c_self + +! Interface and setup +type(ufo_metoffice_rmatrixradiance), pointer :: self +call ufo_metoffice_rmatrixradiance_registry % get(c_self, self) + +! Print information about object +call self % print() + +end subroutine ufo_metoffice_rmatrixradiance_print_c + +!------------------------------------------------------------------------------- +end module ufo_metoffice_rmatrixradiance_mod_c diff --git a/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.interface.h b/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.interface.h new file mode 100644 index 000000000..30bb45ddd --- /dev/null +++ b/src/ufo/utils/metoffice/MetOfficeRMatrixRadiance.interface.h @@ -0,0 +1,33 @@ +/* + * (C) Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_METOFFICE_METOFFICERMATRIXRADIANCE_INTERFACE_H_ +#define UFO_UTILS_METOFFICE_METOFFICERMATRIXRADIANCE_INTERFACE_H_ + +#include "oops/base/Variables.h" +#include "ufo/Fortran.h" + +// Forward declarations +namespace eckit { + class Configuration; +} + +namespace ufo { + +// Interface to Fortran routines +extern "C" { + void ufo_metoffice_rmatrixradiance_setup_f90(const F90obfilter &, const eckit::Configuration &, + size_t &, size_t &, size_t &); + void ufo_metoffice_rmatrixradiance_delete_f90(const F90obfilter &); + void ufo_metoffice_rmatrixradiance_getelements_f90(const F90obfilter &, + const size_t &, int *, float *); + void ufo_metoffice_rmatrixradiance_print_f90(const F90obfilter &); +} // extern C + +} // namespace ufo + +#endif // UFO_UTILS_METOFFICE_METOFFICERMATRIXRADIANCE_INTERFACE_H_ diff --git a/src/ufo/utils/metoffice/ufo_metoffice_bmatrixstatic_mod.f90 b/src/ufo/utils/metoffice/ufo_metoffice_bmatrixstatic_mod.f90 new file mode 100644 index 000000000..1a9c88fed --- /dev/null +++ b/src/ufo/utils/metoffice/ufo_metoffice_bmatrixstatic_mod.f90 @@ -0,0 +1,750 @@ +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran module containing the full b-matrix data type and methods for the 1D-Var. + +module ufo_metoffice_bmatrixstatic_mod + +use fckit_log_module, only : fckit_log +use kinds +use ufo_constants_mod, only: zero, one +use ufo_utils_mod, only : ufo_utils_iogetfreeunit, InvertMatrix +use ufo_vars_mod + +implicit none +private + +type, public :: ufo_metoffice_bmatrixstatic + logical :: status !< status indicator + integer :: nbands !< number of latitude bands + integer :: nsurf !< number of surface type variations + integer :: nfields !< number of fields + integer, pointer :: fields(:,:) !< fieldtypes and no. elements in each + real(kind=kind_real), pointer :: store(:,:,:) !< original b-matrices read from the file + real(kind=kind_real), pointer :: inverse(:,:,:) !< inverse of above + real(kind=kind_real), pointer :: sigma(:,:) !< diagonal elements + real(kind=kind_real), pointer :: proxy(:,:) !< copy of original for manipulation + real(kind=kind_real), pointer :: inv_proxy(:,:) !< copy of inverse + real(kind=kind_real), pointer :: sigma_proxy(:,:) !< copy of diagonal + real(kind=kind_real), pointer :: south(:) !< s limit of each latitude band + real(kind=kind_real), pointer :: north(:) !< n limit of each latitude band +contains + procedure :: setup => ufo_metoffice_bmatrixstatic_setup + procedure :: delete => ufo_metoffice_bmatrixstatic_delete + procedure :: reset => ufo_metoffice_bmatrixstatic_reset +end type ufo_metoffice_bmatrixstatic + +character(len=200) :: message + +!----------------------------------------------------------------------------- +! 1. 1d-var profile elements +!----------------------------------------------------------------------------- + +! define id codes for 1d-var retrieval fields. +! a list of these fieldtype codes is always present in the header of the bmatrix +! file and it's that list which decides the form of the retrieval vector. +! +! new definitions should be made in conjunction with the profileinfo_type +! structure found in ufo_rttovonedvarcheck_profindex_mod.F90. + +integer, parameter, public :: nfieldtypes_ukmo = 19 !< number of fieldtypes +integer, parameter, public :: & + ufo_metoffice_fieldtype_t = 1, & !< temperature + ufo_metoffice_fieldtype_q = 2, & !< specific humidity profile + ufo_metoffice_fieldtype_t2 = 3, & !< surface air temperature + ufo_metoffice_fieldtype_q2 = 4, & !< surface spec humidity + ufo_metoffice_fieldtype_tstar = 5, & !< surface skin temperature + ufo_metoffice_fieldtype_pstar = 6, & !< surface pressure + ufo_metoffice_fieldtype_o3total = 7, & !< total column ozone - not currently setup + ufo_metoffice_fieldtype_not_used = 8, & !< not currently in use - not currently setup + ufo_metoffice_fieldtype_ql = 9, & !< liquid water profile - not currently setup + ufo_metoffice_fieldtype_qt = 10, & !< total water profile + ufo_metoffice_fieldtype_windspeed = 11, & !< surface wind speed + ufo_metoffice_fieldtype_o3profile = 12, & !< ozone - not currently setup + ufo_metoffice_fieldtype_lwp = 13, & !< liquid water path - not currently setup + ufo_metoffice_fieldtype_mwemiss = 14, & !< microwave emissivity - not currently setup + ufo_metoffice_fieldtype_qi = 15, & !< ice profile - not currently setup + ufo_metoffice_fieldtype_cloudtopp = 16, & !< single-level cloud top pressure + ufo_metoffice_fieldtype_cloudfrac = 17, & !< effective cloud fraction + ufo_metoffice_fieldtype_emisspc = 18, & !< emissivity prinipal components - not currently setup + ufo_metoffice_fieldtype_cf = 19 !< cloud fraction profile - not currently setup + +character(len=*), parameter, public :: ufo_metoffice_fieldtype_text(nfieldtypes_ukmo) = & + (/ var_ts, & + var_q, & + var_sfc_t2m, & + var_sfc_q2m, & + var_sfc_tskin, & + var_sfc_p2m, & + 'ozone (total column)', & + '[unused field type] ', & + var_clw, & + 'q total ', & + var_sfc_wspeed, & + 'ozone (profile) ', & + 'liquid water path ', & + var_sfc_emiss, & + var_cli, & + 'cloud top pressure ', & + 'cloud fraction ', & + 'emissivity pcs ', & + var_cldfrac /) + +contains + +! ------------------------------------------------------------------------------------------------ +!> Routine to read and setup the 1D-Var B-matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_metoffice_bmatrixstatic_setup(self, variables, filepath, qtotal_flag) + +implicit none +class(ufo_metoffice_bmatrixstatic), intent(inout) :: self !< B-matrix Covariance +character(len=*), intent(in) :: variables(:) !< Model variables in B matrix +character(len=*), intent(in) :: filepath !< Path to B matrix file +logical, intent(in) :: qtotal_flag !< Flag for qtotal + +logical :: file_exists ! Check if a file exists logical +integer :: fileunit ! Unit number for reading in files +integer, allocatable :: fields_in(:) ! Fields_in used to subset b-matrix for testing. +real(kind=kind_real) :: t1,t2 ! Time values for logging +character(len=:), allocatable :: str +logical :: testing = .false. +integer :: ii, jj +logical :: match + +call fckit_log % info("ufo_metoffice_bmatrixstatic_setup start") + +call cpu_time(t1) + +! Open file and read in b-matrix +inquire(file=trim(filepath), exist=file_exists) +if (file_exists) then + fileunit = ufo_utils_iogetfreeunit() + open(unit = fileunit, file = trim(filepath)) + call rttovonedvarcheck_covariance_InitBmatrix(self) + call rttovonedvarcheck_create_fields_in(fields_in, variables, qtotal_flag) + if (testing) then + call rttovonedvarcheck_covariance_GetBmatrix(self, fileunit, fieldlist=fields_in) + else + call rttovonedvarcheck_covariance_GetBmatrix(self, fileunit) + end if + close(unit = fileunit) + call fckit_log % info("rttovonedvarcheck bmatrix file exists and read in") +else + call abor1_ftn("rttovonedvarcheck bmatrix file not found") +end if + +call cpu_time(t2) + +! Check the yaml input contains all required b-matrix elements +do ii = 1, size(self % fields(:,1)) ! loop over b-matrix elements + match = .false. + do jj = 1, size(fields_in) ! loop over array generated from yaml + if (self % fields(ii,1) == fields_in(jj)) match = .true. + end do + if (.not. match) then + write(*,*) "input model variables do not have ",ufo_metoffice_fieldtype_text(self % fields(ii,1)) + call abor1_ftn("rttovonedvarcheck not all the model data is available for the b-matrix") + end if +end do + +write(message,*) "ufo_metoffice_bmatrixstatic_setup cpu time = ",(t2-t1) +call fckit_log % info(message) + +end subroutine ufo_metoffice_bmatrixstatic_setup + +! ------------------------------------------------------------------------------------------------ +!> Routine to delete and squash the 1D-Var B-matrix +!! +!! \details Met Office OPS Heritage: Ops_SatRad_SquashBmatrix.f90 +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine ufo_metoffice_bmatrixstatic_delete(self) + +implicit none +class(ufo_metoffice_bmatrixstatic), intent(inout) :: self !< B-matrix Covariance + +character(len=*), parameter :: RoutineName = "ufo_metoffice_bmatrixstatic_delete" + +self % status = .false. +self % nbands = 0 +self % nsurf = 0 +if ( associated(self % fields) ) deallocate( self % fields ) +if ( associated(self % store) ) deallocate( self % store ) +if ( associated(self % inverse) ) deallocate( self % inverse ) +if ( associated(self % sigma) ) deallocate( self % sigma ) +if ( associated(self % proxy) ) deallocate( self % proxy ) +if ( associated(self % inv_proxy) ) deallocate( self % inv_proxy ) +if ( associated(self % sigma_proxy) ) deallocate( self % sigma_proxy ) +if ( associated(self % south) ) deallocate( self % south ) +if ( associated(self % north) ) deallocate( self % north ) + +end subroutine ufo_metoffice_bmatrixstatic_delete + +! ------------------------------------------------------------------------------------------------ +!> \brief Routine to initialize the 1D-Var B-matrix +!! +!! \details Met Office OPS Heritage: Ops_SatRad_InitBmatrix.f90 +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rttovonedvarcheck_covariance_InitBmatrix(self) + +implicit none + +! subroutine arguments: +type(ufo_metoffice_bmatrixstatic), intent(out) :: self !< B-matrix Covariance + +character(len=*), parameter :: routinename = "rttovonedvarcheck_covariance_InitBmatrix" + +self % status = .false. +self % nbands = 0 +self % nsurf = 0 +nullify( self % fields ) +nullify( self % store ) +nullify( self % inverse ) +nullify( self % sigma ) +nullify( self % proxy ) +nullify( self % inv_proxy ) +nullify( self % sigma_proxy ) +nullify( self % south ) +nullify( self % north ) + +end subroutine rttovonedvarcheck_covariance_InitBmatrix + +! ------------------------------------------------------------------------------------------------ +!> Routine to initialize the 1D-Var B-matrix +!! +!! \details Met Office OPS Heritage: Ops_SatRad_GetBmatrix.f90 +!! +!! read the input file and allocate and fill in all the components of the bmatrix +!! structure, with the exception of proxy variables. +!! +!! notes: +!! +!! it is assumed a valid bmatrix file is available and has been opened ready for +!! reading, accessed via the input unit number. +!! +!! two optional arguments are provided to allow a submatrix to be formed from the +!! original file, either depending on a list of fields to be used for retrieval, +!! or a list of specific element numbers to be retained. +!! +!! the file header may begin with any number of comment lines, which are defined +!! as those using either # or ! as the first non-blank character. +!! +!! immediately following any comments, the header should then contain information +!! on the size of the matrix to be read in, and a description of each matrix +!! element. memory will be allocated depending on these matrix specifications. +!! +!! each matrix should then have a one line header containing the latitude bounds +!! and a latitude band id. band id numbers should be sequential in latitude. i.e. +!! band 1 will begin at -90.0, band 2 from -60.0 ... (or however wide we choose +!! the bands). +!! +!! we also calculate an inverse of each b matrix, used in the current 1d-var for +!! cost function monitoring only but it may be required for other minimization +!! methods at some point. +!! +!! standard deviations are also stored in a separate vector. +!! +!! proxy variables are provided to allow manipulation of the chosen matrix during +!! processing without affecting the original. we might wish to do this, for +!! example, to take account of the different surface temperature errors for land +!! and sea. this routine makes no assumption about the use of these variables, +!! hence no space is allocated. nullification of unused pointers should take +!! place outside (use the ops_satrad_initbmatrix routine). +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rttovonedvarcheck_covariance_GetBmatrix (self, & + fileunit, & + b_elementsused, & + fieldlist) + +implicit none + +! subroutine arguments: +type (ufo_metoffice_bmatrixstatic), intent(inout) :: self !< B-matrix covariance +integer, intent(in) :: fileunit !< free file unit number +integer, optional, intent(in) :: b_elementsused(:) !< optional: list of elements used +integer, optional, intent(inout) :: fieldlist(:) !< optional: list of fields used + +! local declarations: +character(len=*), parameter :: routinename = "rttovonedvarcheck_covariance_GetBmatrix" +integer :: i +integer :: j +integer :: k +integer :: readstatus +integer :: status +integer :: nbands +integer :: matrixsize +integer :: band +integer :: nmatrix +integer :: nbfields +integer :: nelements +integer :: nelements_total +real(kind=kind_real) :: southlimit +real(kind=kind_real) :: northlimit +character(len=80) :: line +character(len=3) :: fieldtype +real(kind=kind_real), allocatable :: bfromfile(:,:) +logical, allocatable :: list(:) +integer, allocatable :: bfields(:,:) +integer, allocatable :: elementsused(:) + +!-------------------------- +! 1. read header information +!-------------------------- + +!---- +! 1.1) header comments +!---- + +! read individual lines until one is found where the first non-blank character +! is not # or ! + +do + read (fileunit, '(a)') line + line = adjustl (line) + if (verify (line, '#!') == 1) then + backspace (fileunit) + exit + end if +end do + +!---- +! 1.2) matrix information +!---- + +nmatrix = 0 +nbfields = 0 +read (fileunit, *) matrixsize, nbands, nbfields ! matrix size, no. of latitude +if (nbfields > 0) then ! bands, no. of fields + allocate (bfields(nbfields,2)) + do i = 1, nbfields + read (fileunit, *) bfields(i,1), bfields(i,2) ! field id, number of elements + end do +end if + +!---- +! 1.3) output initial messages +!---- + +call fckit_log % debug('reading b matrix file:') +write (message, '(a,i0)') 'number of latitude bands = ', nbands +call fckit_log % debug(message) +write (message, '(a,i0)') 'matrix size = ', matrixsize +call fckit_log % debug(message) +if (nbfields > 0) then + call fckit_log % debug('order of fields and number of elements in each:') + do i = 1, nbfields + write (message, '(i0,a)') bfields(i,2), ' x ' // ufo_metoffice_fieldtype_text(bfields(i,1)) + call fckit_log % debug(message) + end do +end if + +!------------------------------------------------------- +! 2. read matrix field mappings and generate element list +!------------------------------------------------------- + +! each field in the bmatrix has a designated id number (defined in +! opsmod_satrad_info) that is included in the file header. under certain +! circumstances there may be a requirement to exclude some of the fields from +! the matrix and this can be achieved by including the fieldlist argument which +! should contain the required id values. +! this section checks the fields in fieldlist against those in the file and makes +! up a final list of those common to both. subsequently, this is translated to +! the exact element numbers that we wish to keep. note that an alternative to +! this method is to provide the element numbers explicitly in the argument +! b_elementsused. + +allocate (self % fields(nfieldtypes_ukmo,2)) +self % fields(:,:) = 0 +self % status = .true. +nelements = 0 +nelements_total = 0 +self % nfields = 0 + +if (present (fieldlist)) then + + !---- + ! 2.1) use retrieval field list if optional argument fieldlist present + !---- + + allocate (elementsused(matrixsize)) + blist: do j = 1, nbfields + do i = 1, size (fieldlist) + if (fieldlist(i) == 0) cycle + if (bfields(j,1) == fieldlist(i)) then + do k = 1, bfields(j,2) + elementsused(nelements + k) = nelements_total + k + end do + nelements_total = nelements_total + bfields(j,2) + nelements = nelements + bfields(j,2) + fieldlist(i) = 0 ! store only fields that are not used in here + cycle blist + end if + end do + bfields(j,1) = 0 ! store only fields that are used in here + nelements_total = nelements_total + bfields(j,2) + end do blist + + nbfields = count (bfields(:,1) /= 0) + bfields(1:nbfields,2) = pack (bfields(:,2), bfields(:,1) /= 0) + bfields(1:nbfields,1) = pack (bfields(:,1), bfields(:,1) /= 0) + + ! 2.1.1) write messages + + if (any (fieldlist /= 0)) then + call fckit_log % debug('the following requested retrieval fields are not in the b matrix:') + do i = 1, size (fieldlist) + if (fieldlist(i) /= 0) then + write (fieldtype, '(i0)') fieldlist(i) + if (fieldlist(i) > 0 .and. fieldlist(i) <= nfieldtypes_ukmo) then + write (message, '(a)') trim (ufo_metoffice_fieldtype_text(fieldlist(i))) // & + ' (fieldtype ' // trim (adjustl (fieldtype)) // ')' + call fckit_log % debug(message) + else + write (message, '(a)') 'fieldtype ' // trim (adjustl (fieldtype)) // & + ' which is invalid' + call fckit_log % debug(message) + end if + end if + end do + end if + + call fckit_log % debug('b matrix fields used to define the retrieval profile vector:') + do i = 1, nbfields + write (message, '(a)') ufo_metoffice_fieldtype_text(bfields(i,1)) + call fckit_log % debug(message) + end do + + ! 2.1.2) reset fieldlist so that only used fields are now stored + + fieldlist(1:nbfields) = bfields(1:nbfields,1) + if (nbfields < size (fieldlist)) fieldlist(nbfields + 1:) = 0 + +else if (present (b_elementsused)) then + + !---- + ! 2.2) or use exact element numbers if optional argument b_elementsused present + !---- + + allocate (elementsused(size (b_elementsused))) + if (any (b_elementsused < 0 .and. b_elementsused > matrixsize)) then + write(*,*) routinename // ' : invalid b matrix elements present in input list' + end if + nelements = count (b_elementsused > 0 .and. b_elementsused <= matrixsize) + elementsused(1:nelements) = pack (b_elementsused, b_elementsused > 0 .and. b_elementsused <= matrixsize) + +else + + !---- + ! 2.3) or use everything by default + !---- + + nelements = matrixsize + +end if + +!--------------------------------------------------------- +! 3. read the file and store in bmatrix structure variables +!--------------------------------------------------------- + +! set field list in structure variable + +if (nbfields > 0) self % fields(1:nbfields,:) = bfields(1:nbfields,:) +self % nfields = nbfields + +! initialize the rest + +self % nbands = nbands +allocate (self % store(nelements,nelements,nbands)) +allocate (self % south(nbands)) +allocate (self % north(nbands)) +self % store(:,:,:) = zero + +! allocate dummy variables for file input + +allocate (list(nbands)) +allocate (bfromfile(matrixsize,matrixsize)) +list(:) = .false. + +readallb : do + + !---- + ! 3.1) read matrix + !---- + + ! latitude band information + + read (fileunit, '(i3,2f8.2)', iostat = readstatus) band, southlimit, northlimit + if (readstatus < 0) exit + + ! matrix data + + do j = 1, matrixsize + read (fileunit, '(5e16.8)' ) (bfromfile(i,j), i = 1, matrixsize) + end do + + write (message, '(a,i0,a,2f8.2)') & + 'band no.', band, ' has southern and northern latitude limits of', & + southlimit, northlimit + call fckit_log % debug(message) + + !---- + ! 3.2) transfer into 1d-var arrays + !---- + + if (band > 0 .and. band <= nbands) then + list(band) = .true. + nmatrix = nmatrix + 1 + if (nelements < matrixsize) then + self % store(:,:,band) = & + bfromfile(elementsused(1:nelements),elementsused(1:nelements)) + else + self % store(:,:,band) = bfromfile(:,:) + end if + self % south(band) = southlimit + self % north(band) = northlimit + else + write (message, '(a,i0)') 'skipped matrix with band number ', band + call fckit_log % debug(message) + end if + +end do readallb + +!--------------------- +! 4. store error vector +!--------------------- + +allocate (self % sigma(nelements,nbands)) +do i = 1, nelements + self % sigma(i,:) = sqrt (self % store(i,i,:)) +end do + +!---------------- +! 5. invert matrix +!---------------- + +allocate (self % inverse(nelements,nelements,nbands)) +self % inverse(:,:,:) = self % store(:,:,:) + +do k = 1, nbands + call InvertMatrix (nelements, & ! in + nelements, & ! in + self % inverse(:,:,k), & ! inout + status) ! out + if (status /= 0) then + call abor1_ftn("rttovonedvarcheck: bmatrix is not invertible") + end if +end do + +!---------- +! 6. tidy up +!---------- + +if (nmatrix < nbands) then + call abor1_ftn("rttovonedvarcheck: too few b matrices found in input file") +end if + +end subroutine rttovonedvarcheck_covariance_GetBmatrix + +! ------------------------------------------------------------------------------------------------ +!> Routine to create error covariances for a single observation +!! +!! \details Met Office OPS Heritage: Ops_SatRad_ResetCovariances.f90 +!! +!! \author Met Office +!! +!! \date 08/09/2020: Created +!! +subroutine ufo_metoffice_bmatrixstatic_reset(self, latitude, & ! in + b_matrix, b_inverse, b_sigma ) ! out + +implicit none + +! Subroutine arguments +class(ufo_metoffice_bmatrixstatic), intent(in) :: self !< B-matrix covariance +real(kind_real), intent(in) :: latitude +real(kind_real), intent(out) :: b_matrix(:,:) +real(kind_real), intent(out) :: b_inverse(:,:) +real(kind_real), intent(out) :: b_sigma(:) + +! Local Variables +integer :: band, i + +! select appropriate b matrix for latitude of observation +b_matrix(:,:) = zero +b_inverse(:,:) = zero +b_sigma(:) = zero +do band = 1, self % nbands + if (latitude < self % north(band)) exit +end do +b_matrix(:,:) = self % store(:,:,band) +b_inverse(:,:) = self % inverse(:,:,band) +b_sigma(:) = self % sigma(:,band) + +! This has been left in for future development +!! Use errors associated with microwave emissivity atlas +!if (profindex % mwemiss(1) > 0) then +! +! ! Atlas uncertainty stored in Ob % MwEmErrAtlas, use this to scale each +! ! row/column of the B matrix block. +! ! The default B matrix, see e.g. file ATOVS_Bmatrix_43, contains error +! ! covariances representing a global average. Here, those elements are scaled +! ! by a factor MwEmissError/SQRT(diag(B_matrix)) for each channel. +! +! do i = profindex % mwemiss(1), profindex % mwemiss(2) +! MwEmissError = Ob % MwEmErrAtlas(i - profindex % mwemiss(1) + 1) +! if (MwEmissError > 1.0E-4 .and. MwEmissError < 1.0) then +! bscale = MwEmissError / sqrt (B_matrix(i,i)) +! B_matrix(:,i) = B_matrix(:,i) * bscale +! B_matrix(i,:) = B_matrix(i,:) * bscale +! B_inverse(:,i) = B_inverse(:,i) / bscale +! B_inverse(i,:) = B_inverse(i,:) / bscale +! B_sigma(i) = B_sigma(i) * bscale +! end if +! end do +! +!end if +! +!! Scale the background skin temperature error covariances over land +!if (Ob % Surface == RTland .and. SkinTempErrorLand >= 0.0) then +! +! bscale = SkinTempErrorLand / sqrt (B_matrix(profindex % tstar,profindex % tstar)) +! B_matrix(:,profindex % tstar) = B_matrix(:,profindex % tstar) * bscale +! B_matrix(profindex % tstar,:) = B_matrix(profindex % tstar,:) * bscale +! B_inverse(:,profindex % tstar) = B_inverse(:,profindex % tstar) / bscale +! B_inverse(profindex % tstar,:) = B_inverse(profindex % tstar,:) / bscale +! B_sigma(profindex % tstar) = B_sigma(profindex % tstar) * bscale +! +!end if + +end subroutine ufo_metoffice_bmatrixstatic_reset + +! ------------------------------------------------------------------------------------------------ +!> Create a subset of the b-matrix. Used for testing. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rttovonedvarcheck_create_fields_in(fields_in, variables, qtotal_flag) + +implicit none + +integer, allocatable, intent(inout) :: fields_in(:) !< Array to specify fields used +character(len=*), intent(in) :: variables(:) !< Model variables in B matrix +logical, intent(in) :: qtotal_flag !< Flag for qtotal + +character(len=MAXVARLEN) :: varname +integer :: jvar +integer :: nmvars, counter +character(len=200) :: message +logical :: clw_present = .false. +logical :: ciw_present = .false. + +call fckit_log % info("rttovonedvarcheck_create_fields_in: starting") + +! Which fields are being used from b matrix file - temporary until rttov can handle all fields +nmvars = size(variables) +allocate(fields_in(nmvars)) +fields_in(:) = 0 + +counter = 0 +do jvar = 1, nmvars + + counter = counter + 1 + varname = variables(jvar) + + select case (trim(varname)) + + case (var_ts) + fields_in(counter) = 1 ! air_temperature + + case (var_q) + if (qtotal_flag) then + fields_in(counter) = 10 ! total water profile + else + fields_in(counter) = 2 ! water profile + end if + + case(var_sfc_t2m) + fields_in(counter) = 3 ! 2m air_temperature + + case(var_sfc_q2m) + fields_in(counter) = 4 ! 2m specific_humidity + + case(var_sfc_tskin) + fields_in(counter) = 5 ! surface skin temperature + + case(var_sfc_p2m) + fields_in(counter) = 6 ! surface air pressure + + ! 7 - o3total is not implmented yet + ! 8 - not used is not implmented yet + + case (var_clw) + clw_present = .true. + if (.NOT. qtotal_flag) then + call abor1_ftn("rttovonedvarcheck not setup for independent clw yet") + end if + + case (var_u, var_v) + fields_in(counter) = 11 ! surface wind speed + + ! 12 - o3profile is not implmented yet + ! 13 - lwp (liquid water path) is not implmented yet + + case ("surface_emissivity") ! microwave emissivity + call abor1_ftn("rttovonedvarcheck not setup for surface surface_emissivity yet") + + case (var_cli) + ciw_present = .true. + if (.NOT. qtotal_flag) then + call abor1_ftn("rttovonedvarcheck not setup for independent ciw yet") + end if + + case ("cloud_top_pressure") + call abor1_ftn("rttovonedvarcheck not setup for cloud retrievals yet") + + case ("effective_cloud_fraction") ! effective cloud fraction + call abor1_ftn("rttovonedvarcheck not setup for cloud retrievals yet") + + case ("emissivity_pc") ! emissivity prinipal components + call abor1_ftn("rttovonedvarcheck not setup for pc emissivity yet") + + ! 19 cloud fraction profile - not currently used + + case default + write(message,*) trim(varname)," not implemented yet in rttovonedvarcheck Covariance" + call abor1_ftn(message) + + end select + +end do + +! Check clw and ciw are both in the list of required variables +if (qtotal_flag .and. ((.not. clw_present) .or. (.not. ciw_present))) then + call abor1_ftn("rttovonedvarcheck using qtotal but clw or ciw not in variables") +end if + +end subroutine rttovonedvarcheck_create_fields_in + +! ------------------------------------------------------------------------------------------------ + +end module ufo_metoffice_bmatrixstatic_mod diff --git a/src/ufo/utils/metoffice/ufo_metoffice_rmatrixradiance_mod.f90 b/src/ufo/utils/metoffice/ufo_metoffice_rmatrixradiance_mod.f90 new file mode 100644 index 000000000..bbc9ecd8e --- /dev/null +++ b/src/ufo/utils/metoffice/ufo_metoffice_rmatrixradiance_mod.f90 @@ -0,0 +1,131 @@ +! (C) British Crown Copyright 2017-2018 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +!> Fortran derived type to hold data for the observation covariance + +module ufo_metoffice_rmatrixradiance_mod + +use kinds +use netcdf + +implicit none +private + +type, public :: ufo_metoffice_rmatrixradiance + integer, allocatable :: channels(:) !< array with instruemnt channel numbers + integer :: wmo_id !< wmo id for satellite + integer :: rtype !< type of r-matrix (1=full; 2=diagonal) + integer :: nchans !< number of channels in rmatrix + real(kind_real), allocatable :: errors(:) !< vector of errors + +contains + procedure :: setup => rmatrix_setup + procedure :: delete => rmatrix_delete + procedure :: print => rmatrix_print + +end type ufo_metoffice_rmatrixradiance + +! ------------------------------------------------------------------------------ +contains +! ------------------------------------------------------------------------------ +!> Setup for the full r_matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rmatrix_setup(self, filename) + +implicit none +class(ufo_metoffice_rmatrixradiance), intent(inout) :: self !< Full R matrix structure +character(len=*), intent(in) :: filename !< Path to input filename + +integer :: error ! error code for read +integer :: lun ! value for identifying file +integer :: dimid ! value for dimension id +integer :: varid ! value for variable id +integer :: nchans ! number of channels + +! defaults +self % nchans = 0 + +! Open netcdf file +error = nf90_open(trim(filename), nf90_nowrite, lun) +if (error /= 0) call abor1_ftn("error in opening file") + +! Read wmo_id +error = nf90_inq_varid(lun, "wmo_id", varid) +error = nf90_get_var(lun, varid, self % wmo_id) +if (error /= 0) call abor1_ftn("error in reading the WMO ID") + +! Read rtype +error = nf90_inq_varid(lun, "r_type", varid) +error = nf90_get_var(lun, varid, self % rtype) +if (error /= 0) call abor1_ftn("error in reading the rmatrix type") + +! Get dimensions of arrays +error = nf90_inq_dimid(lun, "nchans", dimid) +error = nf90_inquire_dimension(lun, dimid, len=self % nchans) +if (error /= 0) call abor1_ftn("error in reading the dimensions from numchans") + +! Allocate arrays +allocate(self % channels(self % nchans)) +allocate(self % errors(self % nchans)) + +! Read channels from files +error = nf90_inq_varid(lun, "channels", varid) +error = nf90_get_var(lun, varid, self % channels) +if (error /= 0) call abor1_ftn("error in reading the rmatrix channels") + +! Read channels from files +error = nf90_inq_varid(lun, "obs_error", varid) +error = nf90_get_var(lun, varid, self % errors) +if (error /= 0) call abor1_ftn("error in reading the rmatrix errors") + +! Close netcdf file +error = nf90_close(lun) +if (error /= 0) call abor1_ftn("error in closing the rmatrix") + +write(*,*) "Successfully opened, read and closed r matrix netcdf file" + +end subroutine rmatrix_setup + +! ------------------------------------------------------------------------------ +!> Delete method for the full r_matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rmatrix_delete(self) + +implicit none +class(ufo_metoffice_rmatrixradiance), intent(inout) :: self !< Full R matrix structure + +if (allocated(self % channels)) deallocate(self % channels) +if (allocated(self % errors)) deallocate(self % errors) + +end subroutine rmatrix_delete + +! ------------------------------------------------------------------------------ +!> Print method for the full r_matrix +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine rmatrix_print(self) + +implicit none +class(ufo_metoffice_rmatrixradiance), intent(inout) :: self !< Full R matrix structure + +write(*,*) "wmo_id = ", self % wmo_id +write(*,*) "rtype = ", self % rtype +write(*,*) "channels = ", self % channels(:) +write(*,*) "errors = ", self % errors(:) + +end subroutine rmatrix_print + +end module ufo_metoffice_rmatrixradiance_mod diff --git a/src/ufo/utils/parameters/ParameterTraitsVariable.cc b/src/ufo/utils/parameters/ParameterTraitsVariable.cc new file mode 100644 index 000000000..d5b56f193 --- /dev/null +++ b/src/ufo/utils/parameters/ParameterTraitsVariable.cc @@ -0,0 +1,113 @@ +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/utils/parameters/ParameterTraitsVariable.h" + +#include +#include +#include + +#include "eckit/exception/Exceptions.h" +#include "eckit/utils/StringTools.h" +#include "oops/util/CompositePath.h" +#include "oops/util/LocalEnvironment.h" +#include "oops/util/parameters/ParameterTraits.h" +#include "oops/util/stringFunctions.h" +#include "ufo/filters/Variable.h" + +namespace oops { + +boost::optional ParameterTraits::get( + util::CompositePath &path, const eckit::Configuration &config, const std::string& name) { + if (config.has(name)) { + std::list messages; + + { + // Within this block, set the ECKIT_EXCEPTION_IS_SILENT environment variable to 1 + // to prevent eckit exceptions (which will be caught) from printing unnerving messages + // to the error log. + util::LocalEnvironment localEnv; + localEnv.set("ECKIT_EXCEPTION_IS_SILENT", "1"); + + // Handle the following YAML structure: + // + // : + // name: somevar@SomeGroup + // channels: ... # optional + // options: # optional + // ... + try { + eckit::LocalConfiguration varConf(config, name); + return ufo::Variable(varConf); + } catch (eckit::Exception &e) { + // The YAML doesn't have this structure. + messages.emplace_back(e.what()); + } + + // Handle the following YAML structure: + // + // : somevar@SomeGroup + + try { + std::string varAndGroup = config.getString(name); + return ufo::Variable(varAndGroup); + } catch (eckit::Exception &e) { + // The YAML doesn't have this structure. + messages.emplace_back(e.what()); + } + + // ECKIT_EXCEPTION_IS_SILENT will be unset or restored to its previous value + // when localEnv goes of of scope at the end of this block. + } + + messages.push_front("The key '" + name + + "' is set to neither a string nor a map with the correct keys."); + throw eckit::Exception(eckit::StringTools::join("\n", messages.begin(), messages.end()), + Here()); + } else { + return boost::none; + } +} + +void ParameterTraits::set(eckit::LocalConfiguration &config, + const std::string &name, + const ufo::Variable &value) { + eckit::LocalConfiguration subConfig; + subConfig.set("name", value.variable() + "@" + value.group()); + const std::vector &channels = value.channels(); + if (!channels.empty()) { + const std::string channelsAsString = util::stringfunctions::join( + ",", channels.begin(), channels.end(), [](int n) { return std::to_string(n); }); + subConfig.set("channels", channelsAsString); + } + if (!value.options().keys().empty()) + subConfig.set("options", value.options()); + config.set(name, subConfig); +} + +ObjectJsonSchema ParameterTraits::jsonSchema(const std::string &name) { + std::stringstream oneOf; + { + eckit::Channel ch; + ch.setStream(oneOf); + ch << "[\n"; + { + eckit::AutoIndent indent(ch); + ObjectJsonSchema simpleSchema = ParameterTraits::jsonSchema(""); + ObjectJsonSchema completeSchema({{"name", {{"type", "\"string\""}}}, + {"options", {{"type", "\"object\""}}}, + {"channels", {{"type", "[\"string\", \"integer\"]"}}}}); + ch << toString(simpleSchema.properties().at("")) << ",\n"; + ch << completeSchema.toString() << '\n'; + } + ch << "]"; + } + + return ObjectJsonSchema({{name, {{"oneOf", oneOf.str()}}}}); +} + +} // namespace oops diff --git a/src/ufo/utils/parameters/ParameterTraitsVariable.h b/src/ufo/utils/parameters/ParameterTraitsVariable.h index d916201b7..1b695ce78 100644 --- a/src/ufo/utils/parameters/ParameterTraitsVariable.h +++ b/src/ufo/utils/parameters/ParameterTraitsVariable.h @@ -9,12 +9,8 @@ #define UFO_UTILS_PARAMETERS_PARAMETERTRAITSVARIABLE_H_ #include -#include -#include "eckit/exception/Exceptions.h" -#include "oops/util/CompositePath.h" #include "oops/util/parameters/ParameterTraits.h" -#include "oops/util/stringFunctions.h" #include "ufo/filters/Variable.h" /// \file ParameterTraitsVariable.h @@ -23,46 +19,39 @@ namespace oops { +/// Parameters representing variables can be set in YAML files in two ways: +/// +/// * by setting a key to the variable name (a string), e.g. +/// +/// selected variable: air_temperature@ObsValue +/// +/// * by setting a key to a dictionary containing a key "name" set to the variable name +/// and optionally extra keys called "channels" and "options", e.g. +/// +/// selected variable: +/// name: brightness_temperature@ObsValue +/// channels: 1,5,7-10,13 +/// +/// or +/// +/// selected variable: +/// name: SCATRetMW@ObsFunction +/// options: +/// scatret_ch238: 1 +/// scatret_ch314: 2 +/// scatret_ch890: 15 +/// scatret_types: [ObsValue] template <> struct ParameterTraits { static boost::optional get(util::CompositePath &path, const eckit::Configuration &config, - const std::string& name) { - if (config.has(name)) { - eckit::LocalConfiguration varConf(config, name); - if (!varConf.has("name")) { - // TODO(wsmigaj): shouldn't ufo::Variable itself throw an exception if - // the 'name' property is not specified? - throw eckit::BadParameter(path.path() + ": No variable name specified", Here()); - } - return ufo::Variable(varConf); - } else { - return boost::none; - } - } + const std::string& name); static void set(eckit::LocalConfiguration &config, const std::string &name, - const ufo::Variable &value) { - eckit::LocalConfiguration subConfig; - subConfig.set("name", value.variable() + "@" + value.group()); - const std::vector &channels = value.channels(); - if (!channels.empty()) { - const std::string channelsAsString = util::stringfunctions::join( - ",", channels.begin(), channels.end(), [](int n) { return std::to_string(n); }); - subConfig.set("channels", channelsAsString); - } - if (!value.options().keys().empty()) - subConfig.set("options", value.options()); - config.set(name, subConfig); - } + const ufo::Variable &value); - static ObjectJsonSchema jsonSchema(const std::string &name) { - ObjectJsonSchema nestedSchema({{"name", {{"type", "\"string\""}}}, - {"options", {{"type", "\"object\""}}}, - {"channels", {{"type", "[\"string\", \"integer\"]"}}}}); - return ObjectJsonSchema({{name, nestedSchema.toPropertyJsonSchema()}}); - } + static ObjectJsonSchema jsonSchema(const std::string &name); }; } // namespace oops diff --git a/src/ufo/utils/ufo_utils.interface.F90 b/src/ufo/utils/ufo_utils.interface.F90 new file mode 100644 index 000000000..73f911e06 --- /dev/null +++ b/src/ufo/utils/ufo_utils.interface.F90 @@ -0,0 +1,75 @@ +!------------------------------------------------------------------------------- +! (C) Crown Copyright 2021 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +module ufo_cloudcostfunction_mod_c + +use iso_c_binding +use kinds +use ufo_utils_mod, only: Ops_SatRad_Qsplit, Ops_QsatWat + +implicit none + +private + +contains + +!------------------------------------------------------------------------------- +! Met Office Qsplit routine +! output_type = 1: Partition total humidity into water vapour, liquid water and ice +! output_type != 1: Compute derivatives dq/dqtotal, dql/dqtotal, dqi/dqtotal +subroutine ufo_ops_satrad_qsplit_c(output_type, nvals, p_in, t_in, qtotal_in, & + q_out, ql_out, qi_out, use_qt_split_rain) & + bind(C, name='ufo_ops_satrad_qsplit_f90') + +implicit none +integer(c_int), intent(in) :: output_type +integer(c_int), intent(in) :: nvals +real(c_float), intent(in) :: p_in(nvals) +real(c_float), intent(in) :: t_in(nvals) +real(c_float), intent(in) :: qtotal_in(nvals) +real(c_float), intent(out) :: q_out(nvals) +real(c_float), intent(out) :: ql_out(nvals) +real(c_float), intent(out) :: qi_out(nvals) +logical(c_bool), intent(in) :: use_qt_split_rain + +real(kind_real) :: q_out_kind_real(nvals) +real(kind_real) :: ql_out_kind_real(nvals) +real(kind_real) :: qi_out_kind_real(nvals) + +call Ops_SatRad_Qsplit(output_type, dble(p_in), dble(t_in), dble(qtotal_in), & + q_out_kind_real, ql_out_kind_real, qi_out_kind_real, & + logical(use_qt_split_rain)) + +q_out = real(q_out_kind_real) +ql_out = real(ql_out_kind_real) +qi_out = real(qi_out_kind_real) + +end subroutine ufo_ops_satrad_qsplit_c + +!------------------------------------------------------------------------------- +! Met Office Ops_QsatWat routine +! Returns a saturation mixing ratio (kg/kg) given a temperature and pressure +! using saturation vapour pressure calculated using Goff-Gratch formulae +subroutine ufo_ops_satrad_qsatwat(qs_out, t_in, p_in, nvals) & + bind(C, name='ufo_ops_satrad_qsatwat_f90') + +implicit none +real(c_float), intent(out) :: qs_out(nvals) ! saturation mixing ratio (kg/kg) +real(c_float), intent(in) :: t_in(nvals) ! temperature (K) +real(c_float), intent(in) :: p_in(nvals) ! pressure (Pa) +integer(c_int), intent(in) :: nvals + +real(kind_real) :: qs_out_kind_real(nvals) + +call Ops_QsatWat(qs_out_kind_real, dble(t_in), dble(p_in), nvals) + +qs_out = real(qs_out_kind_real) + +end subroutine ufo_ops_satrad_qsatwat + +!------------------------------------------------------------------------------- +end module ufo_cloudcostfunction_mod_c diff --git a/src/ufo/utils/ufo_utils.interface.h b/src/ufo/utils/ufo_utils.interface.h new file mode 100644 index 000000000..6a36810d5 --- /dev/null +++ b/src/ufo/utils/ufo_utils.interface.h @@ -0,0 +1,22 @@ + +/* + * (C) Crown Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_UTILS_UFO_UTILS_INTERFACE_H_ +#define UFO_UTILS_UFO_UTILS_INTERFACE_H_ + +namespace ufo { + +extern "C" { + void ufo_ops_satrad_qsplit_f90(const int &, const int &, const float *, const float *, + const float *, float *, float *, float *, const bool &); + void ufo_ops_satrad_qsatwat_f90(float *, const float *, const float *, const int &); +} // extern C + +} // namespace ufo + +#endif // UFO_UTILS_UFO_UTILS_INTERFACE_H_ diff --git a/src/ufo/utils/ufo_utils_mod.F90 b/src/ufo/utils/ufo_utils_mod.F90 index 8eb851a58..2e1d5367c 100644 --- a/src/ufo/utils/ufo_utils_mod.F90 +++ b/src/ufo/utils/ufo_utils_mod.F90 @@ -1,432 +1,1415 @@ +!------------------------------------------------------------------------------- +! (C) British Crown Copyright 2020 Met Office +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +!------------------------------------------------------------------------------- + +!> Fortran module with various useful routines + module ufo_utils_mod -use ufo_constants_mod +use fckit_log_module, only : fckit_log +use kinds, only: kind_real +use missing_values_mod, only: missing_value +use ufo_constants_mod, only : zero, half, one, two, & + t0c, zerodegc, min_q, epsilon + +implicit none +private public Ops_Qsat +public Ops_QsatWat +public Ops_SatRad_Qsplit +public Ops_Cholesky +public ufo_utils_iogetfreeunit +public InvertMatrix +public cmp_strings contains - !----------------------------------------------------------------------- - ! (C) Crown copyright Met Office. All rights reserved. - ! Refer to COPYRIGHT.txt of this distribution for details. - !----------------------------------------------------------------------- - ! Saturation Specific Humidity Scheme (Qsat): Vapour to Liquid/Ice. - ! - ! Purpose: - ! - ! Returns a saturation mixing ratio given a temperature and pressure - ! using saturation vapour pressures caluclated using the Goff-Gratch - ! formulae, adopted by the WMO as taken from Landolt-Bornstein, 1987 - ! Numerical Data and Functional relationships in Science and - ! Technology. Group V/Vol 4B Meteorology. Physical and Chemical - ! properties of Air, P35. - ! - ! Value in the lookup table are over water above 0 degrees C and over - ! ice below this temperatures. +!------------------------------------------------------------------------------- +!> Calculate the Saturation Specific Humidity Scheme (Qsat): Vapour to Liquid/Ice. +!! +!! \details Heritage: Ops_Qsat.inc +!! +!! Returns a saturation mixing ratio given a temperature and pressure +!! using saturation vapour pressures caluclated using the Goff-Gratch +!! formulae, adopted by the WMO as taken from Landolt-Bornstein, 1987 +!! Numerical data and Functional relationships in Science and +!! Technology. Group V/Vol 4B Meteorology. Physical and Chemical +!! properties of Air, P35. +!! +!! Value in the lookup table are over water above 0 degrees C and over +!! ice below this temperatures. +!! +!! Method:
+!! uses lookup tables to find eSAT, calculates qSAT directly from that. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine Ops_Qsat (QS, & + T, & + P, & + npnts) + +implicit none + +! subroutine arguments: +integer, intent(in) :: npnts !< Points being processed by qSAT scheme. +real(kind=kind_real), intent(in) :: T(npnts) !< Temperature (K) +real(kind=kind_real), intent(in) :: P(npnts) !< Pressure (Pa). +real(kind=kind_real), intent(inout) :: QS(npnts) !< Saturation mixing ratio (KG/KG) + +! Local declarations: +real(kind=kind_real), parameter :: one_minus_epsilon = one - epsilon +real(kind=kind_real), parameter :: T_low = 183.15_kind_real ! Lowest temperature for which look-up table is valid +real(kind=kind_real), parameter :: T_high = 338.15_kind_real ! Highest temperature for which look-up table is valid +real(kind=kind_real), parameter :: delta_T = 0.1_kind_real ! Temperature increment of look-up table +integer, parameter :: N = ((T_high - T_low + (delta_T * half)) / delta_T) + one ! Size of lookup-table (gives 1551) +integer :: ITABLE +real(kind=kind_real) :: ATABLE +real(kind=kind_real) :: FSUBW ! Converts from sat vapour pressure in pure water to pressure in air +real(kind=kind_real) :: TT +integer :: I +integer :: IES +real(kind=kind_real) :: ES(0:N + 1) ! Table of saturation water vapour pressure (PA) +character(len=*), parameter :: RoutineName = "Ops_Qsat" + +! Note: 0 element is a repeat of 1st element to cater for special case +! of low temperatures (.LE.T_low) for which the array index is +! rounded down due to machine precision. + +data (ES(IES), IES= 0, 95) / 0.966483D-02, & +0.966483D-02,0.984279D-02,0.100240D-01,0.102082D-01,0.103957D-01, & +0.105865D-01,0.107803D-01,0.109777D-01,0.111784D-01,0.113825D-01, & +0.115902D-01,0.118016D-01,0.120164D-01,0.122348D-01,0.124572D-01, & +0.126831D-01,0.129132D-01,0.131470D-01,0.133846D-01,0.136264D-01, & +0.138724D-01,0.141225D-01,0.143771D-01,0.146356D-01,0.148985D-01, & +0.151661D-01,0.154379D-01,0.157145D-01,0.159958D-01,0.162817D-01, & +0.165725D-01,0.168680D-01,0.171684D-01,0.174742D-01,0.177847D-01, & +0.181008D-01,0.184216D-01,0.187481D-01,0.190801D-01,0.194175D-01, & +0.197608D-01,0.201094D-01,0.204637D-01,0.208242D-01,0.211906D-01, & +0.215631D-01,0.219416D-01,0.223263D-01,0.227172D-01,0.231146D-01, & +0.235188D-01,0.239296D-01,0.243465D-01,0.247708D-01,0.252019D-01, & +0.256405D-01,0.260857D-01,0.265385D-01,0.269979D-01,0.274656D-01, & +0.279405D-01,0.284232D-01,0.289142D-01,0.294124D-01,0.299192D-01, & +0.304341D-01,0.309571D-01,0.314886D-01,0.320285D-01,0.325769D-01, & +0.331348D-01,0.337014D-01,0.342771D-01,0.348618D-01,0.354557D-01, & +0.360598D-01,0.366727D-01,0.372958D-01,0.379289D-01,0.385717D-01, & +0.392248D-01,0.398889D-01,0.405633D-01,0.412474D-01,0.419430D-01, & +0.426505D-01,0.433678D-01,0.440974D-01,0.448374D-01,0.455896D-01, & +0.463545D-01,0.471303D-01,0.479191D-01,0.487190D-01,0.495322D-01/ + data (ES(IES),IES= 96,190) / & +0.503591D-01,0.511977D-01,0.520490D-01,0.529145D-01,0.537931D-01, & +0.546854D-01,0.555924D-01,0.565119D-01,0.574467D-01,0.583959D-01, & +0.593592D-01,0.603387D-01,0.613316D-01,0.623409D-01,0.633655D-01, & +0.644053D-01,0.654624D-01,0.665358D-01,0.676233D-01,0.687302D-01, & +0.698524D-01,0.709929D-01,0.721490D-01,0.733238D-01,0.745180D-01, & +0.757281D-01,0.769578D-01,0.782061D-01,0.794728D-01,0.807583D-01, & +0.820647D-01,0.833905D-01,0.847358D-01,0.861028D-01,0.874882D-01, & +0.888957D-01,0.903243D-01,0.917736D-01,0.932464D-01,0.947407D-01, & +0.962571D-01,0.977955D-01,0.993584D-01,0.100942D+00,0.102551D+00, & +0.104186D+00,0.105842D+00,0.107524D+00,0.109231D+00,0.110963D+00, & +0.112722D+00,0.114506D+00,0.116317D+00,0.118153D+00,0.120019D+00, & +0.121911D+00,0.123831D+00,0.125778D+00,0.127755D+00,0.129761D+00, & +0.131796D+00,0.133863D+00,0.135956D+00,0.138082D+00,0.140241D+00, & +0.142428D+00,0.144649D+00,0.146902D+00,0.149190D+00,0.151506D+00, & +0.153859D+00,0.156245D+00,0.158669D+00,0.161126D+00,0.163618D+00, & +0.166145D+00,0.168711D+00,0.171313D+00,0.173951D+00,0.176626D+00, & +0.179342D+00,0.182096D+00,0.184893D+00,0.187724D+00,0.190600D+00, & +0.193518D+00,0.196473D+00,0.199474D+00,0.202516D+00,0.205604D+00, & +0.208730D+00,0.211905D+00,0.215127D+00,0.218389D+00,0.221701D+00/ + data (ES(IES),IES=191,285) / & +0.225063D+00,0.228466D+00,0.231920D+00,0.235421D+00,0.238976D+00, & +0.242580D+00,0.246232D+00,0.249933D+00,0.253691D+00,0.257499D+00, & +0.261359D+00,0.265278D+00,0.269249D+00,0.273274D+00,0.277358D+00, & +0.281498D+00,0.285694D+00,0.289952D+00,0.294268D+00,0.298641D+00, & +0.303078D+00,0.307577D+00,0.312135D+00,0.316753D+00,0.321440D+00, & +0.326196D+00,0.331009D+00,0.335893D+00,0.340842D+00,0.345863D+00, & +0.350951D+00,0.356106D+00,0.361337D+00,0.366636D+00,0.372006D+00, & +0.377447D+00,0.382966D+00,0.388567D+00,0.394233D+00,0.399981D+00, & +0.405806D+00,0.411714D+00,0.417699D+00,0.423772D+00,0.429914D+00, & +0.436145D+00,0.442468D+00,0.448862D+00,0.455359D+00,0.461930D+00, & +0.468596D+00,0.475348D+00,0.482186D+00,0.489124D+00,0.496160D+00, & +0.503278D+00,0.510497D+00,0.517808D+00,0.525224D+00,0.532737D+00, & +0.540355D+00,0.548059D+00,0.555886D+00,0.563797D+00,0.571825D+00, & +0.579952D+00,0.588198D+00,0.596545D+00,0.605000D+00,0.613572D+00, & +0.622255D+00,0.631059D+00,0.639962D+00,0.649003D+00,0.658144D+00, & +0.667414D+00,0.676815D+00,0.686317D+00,0.695956D+00,0.705728D+00, & +0.715622D+00,0.725641D+00,0.735799D+00,0.746082D+00,0.756495D+00, & +0.767052D+00,0.777741D+00,0.788576D+00,0.799549D+00,0.810656D+00, & +0.821914D+00,0.833314D+00,0.844854D+00,0.856555D+00,0.868415D+00/ + data (ES(IES),IES=286,380) / & +0.880404D+00,0.892575D+00,0.904877D+00,0.917350D+00,0.929974D+00, & +0.942771D+00,0.955724D+00,0.968837D+00,0.982127D+00,0.995600D+00, & +0.100921D+01,0.102304D+01,0.103700D+01,0.105116D+01,0.106549D+01, & +0.108002D+01,0.109471D+01,0.110962D+01,0.112469D+01,0.113995D+01, & +0.115542D+01,0.117107D+01,0.118693D+01,0.120298D+01,0.121923D+01, & +0.123569D+01,0.125234D+01,0.126923D+01,0.128631D+01,0.130362D+01, & +0.132114D+01,0.133887D+01,0.135683D+01,0.137500D+01,0.139342D+01, & +0.141205D+01,0.143091D+01,0.145000D+01,0.146933D+01,0.148892D+01, & +0.150874D+01,0.152881D+01,0.154912D+01,0.156970D+01,0.159049D+01, & +0.161159D+01,0.163293D+01,0.165452D+01,0.167640D+01,0.169852D+01, & +0.172091D+01,0.174359D+01,0.176653D+01,0.178977D+01,0.181332D+01, & +0.183709D+01,0.186119D+01,0.188559D+01,0.191028D+01,0.193524D+01, & +0.196054D+01,0.198616D+01,0.201208D+01,0.203829D+01,0.206485D+01, & +0.209170D+01,0.211885D+01,0.214637D+01,0.217424D+01,0.220242D+01, & +0.223092D+01,0.225979D+01,0.228899D+01,0.231855D+01,0.234845D+01, & +0.237874D+01,0.240937D+01,0.244040D+01,0.247176D+01,0.250349D+01, & +0.253560D+01,0.256814D+01,0.260099D+01,0.263431D+01,0.266800D+01, & +0.270207D+01,0.273656D+01,0.277145D+01,0.280671D+01,0.284248D+01, & +0.287859D+01,0.291516D+01,0.295219D+01,0.298962D+01,0.302746D+01/ + data (ES(IES),IES=381,475) / & +0.306579D+01,0.310454D+01,0.314377D+01,0.318351D+01,0.322360D+01, & +0.326427D+01,0.330538D+01,0.334694D+01,0.338894D+01,0.343155D+01, & +0.347456D+01,0.351809D+01,0.356216D+01,0.360673D+01,0.365184D+01, & +0.369744D+01,0.374352D+01,0.379018D+01,0.383743D+01,0.388518D+01, & +0.393344D+01,0.398230D+01,0.403177D+01,0.408175D+01,0.413229D+01, & +0.418343D+01,0.423514D+01,0.428746D+01,0.434034D+01,0.439389D+01, & +0.444808D+01,0.450276D+01,0.455820D+01,0.461423D+01,0.467084D+01, & +0.472816D+01,0.478607D+01,0.484468D+01,0.490393D+01,0.496389D+01, & +0.502446D+01,0.508580D+01,0.514776D+01,0.521047D+01,0.527385D+01, & +0.533798D+01,0.540279D+01,0.546838D+01,0.553466D+01,0.560173D+01, & +0.566949D+01,0.573807D+01,0.580750D+01,0.587749D+01,0.594846D+01, & +0.602017D+01,0.609260D+01,0.616591D+01,0.623995D+01,0.631490D+01, & +0.639061D+01,0.646723D+01,0.654477D+01,0.662293D+01,0.670220D+01, & +0.678227D+01,0.686313D+01,0.694495D+01,0.702777D+01,0.711142D+01, & +0.719592D+01,0.728140D+01,0.736790D+01,0.745527D+01,0.754352D+01, & +0.763298D+01,0.772316D+01,0.781442D+01,0.790676D+01,0.800001D+01, & +0.809435D+01,0.818967D+01,0.828606D+01,0.838343D+01,0.848194D+01, & +0.858144D+01,0.868207D+01,0.878392D+01,0.888673D+01,0.899060D+01, & +0.909567D+01,0.920172D+01,0.930909D+01,0.941765D+01,0.952730D+01/ + data (ES(IES),IES=476,570) / & +0.963821D+01,0.975022D+01,0.986352D+01,0.997793D+01,0.100937D+02, & +0.102105D+02,0.103287D+02,0.104481D+02,0.105688D+02,0.106909D+02, & +0.108143D+02,0.109387D+02,0.110647D+02,0.111921D+02,0.113207D+02, & +0.114508D+02,0.115821D+02,0.117149D+02,0.118490D+02,0.119847D+02, & +0.121216D+02,0.122601D+02,0.124002D+02,0.125416D+02,0.126846D+02, & +0.128290D+02,0.129747D+02,0.131224D+02,0.132712D+02,0.134220D+02, & +0.135742D+02,0.137278D+02,0.138831D+02,0.140403D+02,0.141989D+02, & +0.143589D+02,0.145211D+02,0.146845D+02,0.148501D+02,0.150172D+02, & +0.151858D+02,0.153564D+02,0.155288D+02,0.157029D+02,0.158786D+02, & +0.160562D+02,0.162358D+02,0.164174D+02,0.166004D+02,0.167858D+02, & +0.169728D+02,0.171620D+02,0.173528D+02,0.175455D+02,0.177406D+02, & +0.179372D+02,0.181363D+02,0.183372D+02,0.185400D+02,0.187453D+02, & +0.189523D+02,0.191613D+02,0.193728D+02,0.195866D+02,0.198024D+02, & +0.200200D+02,0.202401D+02,0.204626D+02,0.206871D+02,0.209140D+02, & +0.211430D+02,0.213744D+02,0.216085D+02,0.218446D+02,0.220828D+02, & +0.223241D+02,0.225671D+02,0.228132D+02,0.230615D+02,0.233120D+02, & +0.235651D+02,0.238211D+02,0.240794D+02,0.243404D+02,0.246042D+02, & +0.248704D+02,0.251390D+02,0.254109D+02,0.256847D+02,0.259620D+02, & +0.262418D+02,0.265240D+02,0.268092D+02,0.270975D+02,0.273883D+02/ + data (ES(IES),IES=571,665) / & +0.276822D+02,0.279792D+02,0.282789D+02,0.285812D+02,0.288867D+02, & +0.291954D+02,0.295075D+02,0.298222D+02,0.301398D+02,0.304606D+02, & +0.307848D+02,0.311119D+02,0.314424D+02,0.317763D+02,0.321133D+02, & +0.324536D+02,0.327971D+02,0.331440D+02,0.334940D+02,0.338475D+02, & +0.342050D+02,0.345654D+02,0.349295D+02,0.352975D+02,0.356687D+02, & +0.360430D+02,0.364221D+02,0.368042D+02,0.371896D+02,0.375790D+02, & +0.379725D+02,0.383692D+02,0.387702D+02,0.391744D+02,0.395839D+02, & +0.399958D+02,0.404118D+02,0.408325D+02,0.412574D+02,0.416858D+02, & +0.421188D+02,0.425551D+02,0.429962D+02,0.434407D+02,0.438910D+02, & +0.443439D+02,0.448024D+02,0.452648D+02,0.457308D+02,0.462018D+02, & +0.466775D+02,0.471582D+02,0.476428D+02,0.481313D+02,0.486249D+02, & +0.491235D+02,0.496272D+02,0.501349D+02,0.506479D+02,0.511652D+02, & +0.516876D+02,0.522142D+02,0.527474D+02,0.532836D+02,0.538266D+02, & +0.543737D+02,0.549254D+02,0.554839D+02,0.560456D+02,0.566142D+02, & +0.571872D+02,0.577662D+02,0.583498D+02,0.589392D+02,0.595347D+02, & +0.601346D+02,0.607410D+02,0.613519D+02,0.619689D+02,0.625922D+02, & +0.632204D+02,0.638550D+02,0.644959D+02,0.651418D+02,0.657942D+02, & +0.664516D+02,0.671158D+02,0.677864D+02,0.684624D+02,0.691451D+02, & +0.698345D+02,0.705293D+02,0.712312D+02,0.719398D+02,0.726542D+02/ + data (ES(IES),IES=666,760) / & +0.733754D+02,0.741022D+02,0.748363D+02,0.755777D+02,0.763247D+02, & +0.770791D+02,0.778394D+02,0.786088D+02,0.793824D+02,0.801653D+02, & +0.809542D+02,0.817509D+02,0.825536D+02,0.833643D+02,0.841828D+02, & +0.850076D+02,0.858405D+02,0.866797D+02,0.875289D+02,0.883827D+02, & +0.892467D+02,0.901172D+02,0.909962D+02,0.918818D+02,0.927760D+02, & +0.936790D+02,0.945887D+02,0.955071D+02,0.964346D+02,0.973689D+02, & +0.983123D+02,0.992648D+02,0.100224D+03,0.101193D+03,0.102169D+03, & +0.103155D+03,0.104150D+03,0.105152D+03,0.106164D+03,0.107186D+03, & +0.108217D+03,0.109256D+03,0.110303D+03,0.111362D+03,0.112429D+03, & +0.113503D+03,0.114588D+03,0.115684D+03,0.116789D+03,0.117903D+03, & +0.119028D+03,0.120160D+03,0.121306D+03,0.122460D+03,0.123623D+03, & +0.124796D+03,0.125981D+03,0.127174D+03,0.128381D+03,0.129594D+03, & +0.130822D+03,0.132058D+03,0.133306D+03,0.134563D+03,0.135828D+03, & +0.137109D+03,0.138402D+03,0.139700D+03,0.141017D+03,0.142338D+03, & +0.143676D+03,0.145025D+03,0.146382D+03,0.147753D+03,0.149133D+03, & +0.150529D+03,0.151935D+03,0.153351D+03,0.154783D+03,0.156222D+03, & +0.157678D+03,0.159148D+03,0.160624D+03,0.162117D+03,0.163621D+03, & +0.165142D+03,0.166674D+03,0.168212D+03,0.169772D+03,0.171340D+03, & +0.172921D+03,0.174522D+03,0.176129D+03,0.177755D+03,0.179388D+03/ + data (ES(IES),IES=761,855) / & +0.181040D+03,0.182707D+03,0.184382D+03,0.186076D+03,0.187782D+03, & +0.189503D+03,0.191240D+03,0.192989D+03,0.194758D+03,0.196535D+03, & +0.198332D+03,0.200141D+03,0.201963D+03,0.203805D+03,0.205656D+03, & +0.207532D+03,0.209416D+03,0.211317D+03,0.213236D+03,0.215167D+03, & +0.217121D+03,0.219087D+03,0.221067D+03,0.223064D+03,0.225080D+03, & +0.227113D+03,0.229160D+03,0.231221D+03,0.233305D+03,0.235403D+03, & +0.237520D+03,0.239655D+03,0.241805D+03,0.243979D+03,0.246163D+03, & +0.248365D+03,0.250593D+03,0.252830D+03,0.255093D+03,0.257364D+03, & +0.259667D+03,0.261979D+03,0.264312D+03,0.266666D+03,0.269034D+03, & +0.271430D+03,0.273841D+03,0.276268D+03,0.278722D+03,0.281185D+03, & +0.283677D+03,0.286190D+03,0.288714D+03,0.291266D+03,0.293834D+03, & +0.296431D+03,0.299045D+03,0.301676D+03,0.304329D+03,0.307006D+03, & +0.309706D+03,0.312423D+03,0.315165D+03,0.317930D+03,0.320705D+03, & +0.323519D+03,0.326350D+03,0.329199D+03,0.332073D+03,0.334973D+03, & +0.337897D+03,0.340839D+03,0.343800D+03,0.346794D+03,0.349806D+03, & +0.352845D+03,0.355918D+03,0.358994D+03,0.362112D+03,0.365242D+03, & +0.368407D+03,0.371599D+03,0.374802D+03,0.378042D+03,0.381293D+03, & +0.384588D+03,0.387904D+03,0.391239D+03,0.394604D+03,0.397988D+03, & +0.401411D+03,0.404862D+03,0.408326D+03,0.411829D+03,0.415352D+03/ + data (ES(IES),IES=856,950) / & +0.418906D+03,0.422490D+03,0.426095D+03,0.429740D+03,0.433398D+03, & +0.437097D+03,0.440827D+03,0.444570D+03,0.448354D+03,0.452160D+03, & +0.455999D+03,0.459870D+03,0.463765D+03,0.467702D+03,0.471652D+03, & +0.475646D+03,0.479674D+03,0.483715D+03,0.487811D+03,0.491911D+03, & +0.496065D+03,0.500244D+03,0.504448D+03,0.508698D+03,0.512961D+03, & +0.517282D+03,0.521617D+03,0.525989D+03,0.530397D+03,0.534831D+03, & +0.539313D+03,0.543821D+03,0.548355D+03,0.552938D+03,0.557549D+03, & +0.562197D+03,0.566884D+03,0.571598D+03,0.576351D+03,0.581131D+03, & +0.585963D+03,0.590835D+03,0.595722D+03,0.600663D+03,0.605631D+03, & +0.610641D+03,0.615151D+03,0.619625D+03,0.624140D+03,0.628671D+03, & +0.633243D+03,0.637845D+03,0.642465D+03,0.647126D+03,0.651806D+03, & +0.656527D+03,0.661279D+03,0.666049D+03,0.670861D+03,0.675692D+03, & +0.680566D+03,0.685471D+03,0.690396D+03,0.695363D+03,0.700350D+03, & +0.705381D+03,0.710444D+03,0.715527D+03,0.720654D+03,0.725801D+03, & +0.730994D+03,0.736219D+03,0.741465D+03,0.746756D+03,0.752068D+03, & +0.757426D+03,0.762819D+03,0.768231D+03,0.773692D+03,0.779172D+03, & +0.784701D+03,0.790265D+03,0.795849D+03,0.801483D+03,0.807137D+03, & +0.812842D+03,0.818582D+03,0.824343D+03,0.830153D+03,0.835987D+03, & +0.841871D+03,0.847791D+03,0.853733D+03,0.859727D+03,0.865743D+03/ + data (ES(IES),IES=951,1045) / & +0.871812D+03,0.877918D+03,0.884046D+03,0.890228D+03,0.896433D+03, & +0.902690D+03,0.908987D+03,0.915307D+03,0.921681D+03,0.928078D+03, & +0.934531D+03,0.941023D+03,0.947539D+03,0.954112D+03,0.960708D+03, & +0.967361D+03,0.974053D+03,0.980771D+03,0.987545D+03,0.994345D+03, & +0.100120D+04,0.100810D+04,0.101502D+04,0.102201D+04,0.102902D+04, & +0.103608D+04,0.104320D+04,0.105033D+04,0.105753D+04,0.106475D+04, & +0.107204D+04,0.107936D+04,0.108672D+04,0.109414D+04,0.110158D+04, & +0.110908D+04,0.111663D+04,0.112421D+04,0.113185D+04,0.113952D+04, & +0.114725D+04,0.115503D+04,0.116284D+04,0.117071D+04,0.117861D+04, & +0.118658D+04,0.119459D+04,0.120264D+04,0.121074D+04,0.121888D+04, & +0.122709D+04,0.123534D+04,0.124362D+04,0.125198D+04,0.126036D+04, & +0.126881D+04,0.127731D+04,0.128584D+04,0.129444D+04,0.130307D+04, & +0.131177D+04,0.132053D+04,0.132931D+04,0.133817D+04,0.134705D+04, & +0.135602D+04,0.136503D+04,0.137407D+04,0.138319D+04,0.139234D+04, & +0.140156D+04,0.141084D+04,0.142015D+04,0.142954D+04,0.143896D+04, & +0.144845D+04,0.145800D+04,0.146759D+04,0.147725D+04,0.148694D+04, & +0.149672D+04,0.150655D+04,0.151641D+04,0.152635D+04,0.153633D+04, & +0.154639D+04,0.155650D+04,0.156665D+04,0.157688D+04,0.158715D+04, & +0.159750D+04,0.160791D+04,0.161836D+04,0.162888D+04,0.163945D+04/ + data (ES(IES),IES=1046,1140) / & +0.165010D+04,0.166081D+04,0.167155D+04,0.168238D+04,0.169325D+04, & +0.170420D+04,0.171522D+04,0.172627D+04,0.173741D+04,0.174859D+04, & +0.175986D+04,0.177119D+04,0.178256D+04,0.179402D+04,0.180552D+04, & +0.181711D+04,0.182877D+04,0.184046D+04,0.185224D+04,0.186407D+04, & +0.187599D+04,0.188797D+04,0.190000D+04,0.191212D+04,0.192428D+04, & +0.193653D+04,0.194886D+04,0.196122D+04,0.197368D+04,0.198618D+04, & +0.199878D+04,0.201145D+04,0.202416D+04,0.203698D+04,0.204983D+04, & +0.206278D+04,0.207580D+04,0.208887D+04,0.210204D+04,0.211525D+04, & +0.212856D+04,0.214195D+04,0.215538D+04,0.216892D+04,0.218249D+04, & +0.219618D+04,0.220994D+04,0.222375D+04,0.223766D+04,0.225161D+04, & +0.226567D+04,0.227981D+04,0.229399D+04,0.230829D+04,0.232263D+04, & +0.233708D+04,0.235161D+04,0.236618D+04,0.238087D+04,0.239560D+04, & +0.241044D+04,0.242538D+04,0.244035D+04,0.245544D+04,0.247057D+04, & +0.248583D+04,0.250116D+04,0.251654D+04,0.253204D+04,0.254759D+04, & +0.256325D+04,0.257901D+04,0.259480D+04,0.261073D+04,0.262670D+04, & +0.264279D+04,0.265896D+04,0.267519D+04,0.269154D+04,0.270794D+04, & +0.272447D+04,0.274108D+04,0.275774D+04,0.277453D+04,0.279137D+04, & +0.280834D+04,0.282540D+04,0.284251D+04,0.285975D+04,0.287704D+04, & +0.289446D+04,0.291198D+04,0.292954D+04,0.294725D+04,0.296499D+04/ + data (ES(IES),IES=1141,1235) / & +0.298288D+04,0.300087D+04,0.301890D+04,0.303707D+04,0.305529D+04, & +0.307365D+04,0.309211D+04,0.311062D+04,0.312927D+04,0.314798D+04, & +0.316682D+04,0.318577D+04,0.320477D+04,0.322391D+04,0.324310D+04, & +0.326245D+04,0.328189D+04,0.330138D+04,0.332103D+04,0.334073D+04, & +0.336058D+04,0.338053D+04,0.340054D+04,0.342069D+04,0.344090D+04, & +0.346127D+04,0.348174D+04,0.350227D+04,0.352295D+04,0.354369D+04, & +0.356458D+04,0.358559D+04,0.360664D+04,0.362787D+04,0.364914D+04, & +0.367058D+04,0.369212D+04,0.371373D+04,0.373548D+04,0.375731D+04, & +0.377929D+04,0.380139D+04,0.382355D+04,0.384588D+04,0.386826D+04, & +0.389081D+04,0.391348D+04,0.393620D+04,0.395910D+04,0.398205D+04, & +0.400518D+04,0.402843D+04,0.405173D+04,0.407520D+04,0.409875D+04, & +0.412246D+04,0.414630D+04,0.417019D+04,0.419427D+04,0.421840D+04, & +0.424272D+04,0.426715D+04,0.429165D+04,0.431634D+04,0.434108D+04, & +0.436602D+04,0.439107D+04,0.441618D+04,0.444149D+04,0.446685D+04, & +0.449241D+04,0.451810D+04,0.454385D+04,0.456977D+04,0.459578D+04, & +0.462197D+04,0.464830D+04,0.467468D+04,0.470127D+04,0.472792D+04, & +0.475477D+04,0.478175D+04,0.480880D+04,0.483605D+04,0.486336D+04, & +0.489087D+04,0.491853D+04,0.494623D+04,0.497415D+04,0.500215D+04, & +0.503034D+04,0.505867D+04,0.508707D+04,0.511568D+04,0.514436D+04/ + data (ES(IES),IES=1236,1330) / & +0.517325D+04,0.520227D+04,0.523137D+04,0.526068D+04,0.529005D+04, & +0.531965D+04,0.534939D+04,0.537921D+04,0.540923D+04,0.543932D+04, & +0.546965D+04,0.550011D+04,0.553064D+04,0.556139D+04,0.559223D+04, & +0.562329D+04,0.565449D+04,0.568577D+04,0.571727D+04,0.574884D+04, & +0.578064D+04,0.581261D+04,0.584464D+04,0.587692D+04,0.590924D+04, & +0.594182D+04,0.597455D+04,0.600736D+04,0.604039D+04,0.607350D+04, & +0.610685D+04,0.614036D+04,0.617394D+04,0.620777D+04,0.624169D+04, & +0.627584D+04,0.631014D+04,0.634454D+04,0.637918D+04,0.641390D+04, & +0.644887D+04,0.648400D+04,0.651919D+04,0.655467D+04,0.659021D+04, & +0.662599D+04,0.666197D+04,0.669800D+04,0.673429D+04,0.677069D+04, & +0.680735D+04,0.684415D+04,0.688104D+04,0.691819D+04,0.695543D+04, & +0.699292D+04,0.703061D+04,0.706837D+04,0.710639D+04,0.714451D+04, & +0.718289D+04,0.722143D+04,0.726009D+04,0.729903D+04,0.733802D+04, & +0.737729D+04,0.741676D+04,0.745631D+04,0.749612D+04,0.753602D+04, & +0.757622D+04,0.761659D+04,0.765705D+04,0.769780D+04,0.773863D+04, & +0.777975D+04,0.782106D+04,0.786246D+04,0.790412D+04,0.794593D+04, & +0.798802D+04,0.803028D+04,0.807259D+04,0.811525D+04,0.815798D+04, & +0.820102D+04,0.824427D+04,0.828757D+04,0.833120D+04,0.837493D+04, & +0.841895D+04,0.846313D+04,0.850744D+04,0.855208D+04,0.859678D+04/ + data (ES(IES),IES=1331,1425) / & +0.864179D+04,0.868705D+04,0.873237D+04,0.877800D+04,0.882374D+04, & +0.886979D+04,0.891603D+04,0.896237D+04,0.900904D+04,0.905579D+04, & +0.910288D+04,0.915018D+04,0.919758D+04,0.924529D+04,0.929310D+04, & +0.934122D+04,0.938959D+04,0.943804D+04,0.948687D+04,0.953575D+04, & +0.958494D+04,0.963442D+04,0.968395D+04,0.973384D+04,0.978383D+04, & +0.983412D+04,0.988468D+04,0.993534D+04,0.998630D+04,0.100374D+05, & +0.100888D+05,0.101406D+05,0.101923D+05,0.102444D+05,0.102966D+05, & +0.103492D+05,0.104020D+05,0.104550D+05,0.105082D+05,0.105616D+05, & +0.106153D+05,0.106693D+05,0.107234D+05,0.107779D+05,0.108325D+05, & +0.108874D+05,0.109425D+05,0.109978D+05,0.110535D+05,0.111092D+05, & +0.111653D+05,0.112217D+05,0.112782D+05,0.113350D+05,0.113920D+05, & +0.114493D+05,0.115070D+05,0.115646D+05,0.116228D+05,0.116809D+05, & +0.117396D+05,0.117984D+05,0.118574D+05,0.119167D+05,0.119762D+05, & +0.120360D+05,0.120962D+05,0.121564D+05,0.122170D+05,0.122778D+05, & +0.123389D+05,0.124004D+05,0.124619D+05,0.125238D+05,0.125859D+05, & +0.126484D+05,0.127111D+05,0.127739D+05,0.128372D+05,0.129006D+05, & +0.129644D+05,0.130285D+05,0.130927D+05,0.131573D+05,0.132220D+05, & +0.132872D+05,0.133526D+05,0.134182D+05,0.134842D+05,0.135503D+05, & +0.136168D+05,0.136836D+05,0.137505D+05,0.138180D+05,0.138854D+05/ + data (ES(IES),IES=1426,1520) / & +0.139534D+05,0.140216D+05,0.140900D+05,0.141588D+05,0.142277D+05, & +0.142971D+05,0.143668D+05,0.144366D+05,0.145069D+05,0.145773D+05, & +0.146481D+05,0.147192D+05,0.147905D+05,0.148622D+05,0.149341D+05, & +0.150064D+05,0.150790D+05,0.151517D+05,0.152250D+05,0.152983D+05, & +0.153721D+05,0.154462D+05,0.155205D+05,0.155952D+05,0.156701D+05, & +0.157454D+05,0.158211D+05,0.158969D+05,0.159732D+05,0.160496D+05, & +0.161265D+05,0.162037D+05,0.162811D+05,0.163589D+05,0.164369D+05, & +0.165154D+05,0.165942D+05,0.166732D+05,0.167526D+05,0.168322D+05, & +0.169123D+05,0.169927D+05,0.170733D+05,0.171543D+05,0.172356D+05, & +0.173173D+05,0.173993D+05,0.174815D+05,0.175643D+05,0.176471D+05, & +0.177305D+05,0.178143D+05,0.178981D+05,0.179826D+05,0.180671D+05, & +0.181522D+05,0.182377D+05,0.183232D+05,0.184093D+05,0.184955D+05, & +0.185823D+05,0.186695D+05,0.187568D+05,0.188447D+05,0.189326D+05, & +0.190212D+05,0.191101D+05,0.191991D+05,0.192887D+05,0.193785D+05, & +0.194688D+05,0.195595D+05,0.196503D+05,0.197417D+05,0.198332D+05, & +0.199253D+05,0.200178D+05,0.201105D+05,0.202036D+05,0.202971D+05, & +0.203910D+05,0.204853D+05,0.205798D+05,0.206749D+05,0.207701D+05, & +0.208659D+05,0.209621D+05,0.210584D+05,0.211554D+05,0.212524D+05, & +0.213501D+05,0.214482D+05,0.215465D+05,0.216452D+05,0.217442D+05/ + data (ES(IES),IES=1521,1552) / & +0.218439D+05,0.219439D+05,0.220440D+05,0.221449D+05,0.222457D+05, & +0.223473D+05,0.224494D+05,0.225514D+05,0.226542D+05,0.227571D+05, & +0.228606D+05,0.229646D+05,0.230687D+05,0.231734D+05,0.232783D+05, & +0.233839D+05,0.234898D+05,0.235960D+05,0.237027D+05,0.238097D+05, & +0.239173D+05,0.240254D+05,0.241335D+05,0.242424D+05,0.243514D+05, & +0.244611D+05,0.245712D+05,0.246814D+05,0.247923D+05,0.249034D+05, & +0.250152D+05,0.250152D+05/ + +do I = 1, npnts + + ! Compute the factor that converts from sat vapour pressure in a + ! pure water system to sat vapour pressure in air, FSUBW. This formula + ! is taken from equation A4.7 of Adrian Gill's book: Atmosphere-Ocean + ! Dynamics. Note that his formula works in terms of pressure in MB and + ! temperature in Celsius, so conversion of units leads to the slightly + ! different equation used here. + + FSUBW = one + 1.0E-8_kind_real * P(I) * & + (4.5_kind_real+ 6.0E-4_kind_real * (T(I) - t0c) * (T(I) - t0c)) + + ! use the lookup table to find saturated vapour pressure, and stored it in QS. + + TT = max (T_low, T(I)) + TT = min (T_high, TT) + + ATABLE = (TT - T_low + delta_T) / delta_T + ITABLE = ATABLE + ATABLE = ATABLE - ITABLE + + QS(I) = (one - ATABLE) * ES(ITABLE) + ATABLE * ES(ITABLE + 1) + + ! Multiply by FSUBW to convert to saturated vapour pressure in air (equation A4.6 + ! of Adrian Gill's book) + + QS(I) = QS(I) * FSUBW + + ! Now form the accurate expression for QS, which is a rearranged + ! version of equation A4.3 of Gill's book. + + ! Note that at very low pressures we apply a fix, to prevent a + ! singularity (Qsat tends to 1.0 kg/kg). + + QS(I) = (epsilon * QS(I)) / (max (P(I), QS(I)) - one_minus_epsilon * QS(I)) + +end do + +end subroutine Ops_Qsat + +!------------------------------------------------------------------------------- +!> Saturation Specific Humidity Scheme: Vapour to Liquid. +!! +!! \details Heritage: Ops_QsatWat.inc +!! +!! Returns a saturation mixing ratio given a temperature and pressure +!! using saturation vapour pressure calculated using the Goff-Gratch +!! Formulaie, adopted by the WMO as taken from Landolt-Bornstein, 1987 +!! Numerical Data and Functional Relationships in Science and +!! Technology. Group V/Vol 4B Meteorology. Physical and Chemical +!! Properties of Air, P35 +!! +!! Values in the lookup table are over water above and below 0 degrees C. +!! +!! Note: For vapour pressure oever water this formula is valid for +!! temperatures between 373K and 223K. The values for saturated vapour +!! over water in the lookup table below are out of the lower end of +!! this range. However it is standard WMO practice to use the formula +!! below its accepted range for use with the calculation of dew points +!! in the upper atmosphere +!! +!! Method:
+!! uses lookup tables to find eSAT, calculates qSAT directly from that. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine Ops_QsatWat (QS, & + T, & + P, & + npnts) + +implicit none + +! subroutine arguments: +integer :: npnts ! Points (=horizontal dimensions) being processed by qSAT scheme. +real(kind=kind_real) :: T(npnts) ! Temperature (K). +real(kind=kind_real) :: P(npnts) ! Pressure (Pa). +real(kind=kind_real) :: QS(npnts) ! Saturation mixing ratio at temperature T and pressure P (KG/KG) + +! Local declarations: +real(kind=kind_real), parameter :: one_minus_epsilon = one - epsilon +real(kind=kind_real), parameter :: T_low = 183.15_kind_real +real(kind=kind_real), parameter :: T_high = 338.15_kind_real +real(kind=kind_real), parameter :: delta_T = 0.1_kind_real +integer, parameter :: N = ((T_high - T_low + (delta_T * half)) / delta_T) + one ! gives N = 1551 +integer :: ITABLE +real(kind=kind_real) :: ATABLE +real(kind=kind_real) :: FSUBW +real(kind=kind_real) :: TT +integer :: I +integer :: IES +real(kind=kind_real) :: ES(0:N + 1) ! Table of saturation water vapour pressure (PA) +character(len=*), parameter :: RoutineName = "Ops_QsatWat" + +! Note: 0 element is a repeat of 1st element to cater for special case +! of low temperatures (.LE.T_low) for which the array index is +! rounded down due to machine precision. + +data (ES(IES),IES= 0, 95) / 0.186905D-01, & +0.186905D-01,0.190449D-01,0.194059D-01,0.197727D-01,0.201462D-01, & +0.205261D-01,0.209122D-01,0.213052D-01,0.217050D-01,0.221116D-01, & +0.225252D-01,0.229463D-01,0.233740D-01,0.238090D-01,0.242518D-01, & +0.247017D-01,0.251595D-01,0.256252D-01,0.260981D-01,0.265795D-01, & +0.270691D-01,0.275667D-01,0.280733D-01,0.285876D-01,0.291105D-01, & +0.296429D-01,0.301835D-01,0.307336D-01,0.312927D-01,0.318611D-01, & +0.324390D-01,0.330262D-01,0.336232D-01,0.342306D-01,0.348472D-01, & +0.354748D-01,0.361117D-01,0.367599D-01,0.374185D-01,0.380879D-01, & +0.387689D-01,0.394602D-01,0.401626D-01,0.408771D-01,0.416033D-01, & +0.423411D-01,0.430908D-01,0.438524D-01,0.446263D-01,0.454124D-01, & +0.462122D-01,0.470247D-01,0.478491D-01,0.486874D-01,0.495393D-01, & +0.504057D-01,0.512847D-01,0.521784D-01,0.530853D-01,0.540076D-01, & +0.549444D-01,0.558959D-01,0.568633D-01,0.578448D-01,0.588428D-01, & +0.598566D-01,0.608858D-01,0.619313D-01,0.629926D-01,0.640706D-01, & +0.651665D-01,0.662795D-01,0.674095D-01,0.685570D-01,0.697219D-01, & +0.709063D-01,0.721076D-01,0.733284D-01,0.745679D-01,0.758265D-01, & +0.771039D-01,0.784026D-01,0.797212D-01,0.810577D-01,0.824164D-01, & +0.837971D-01,0.851970D-01,0.866198D-01,0.880620D-01,0.895281D-01, & +0.910178D-01,0.925278D-01,0.940622D-01,0.956177D-01,0.971984D-01/ +data (ES(IES),IES= 96,190) / & +0.988051D-01,0.100433D+00,0.102085D+00,0.103764D+00,0.105467D+00, & +0.107196D+00,0.108953D+00,0.110732D+00,0.112541D+00,0.114376D+00, & +0.116238D+00,0.118130D+00,0.120046D+00,0.121993D+00,0.123969D+00, & +0.125973D+00,0.128009D+00,0.130075D+00,0.132167D+00,0.134296D+00, & +0.136452D+00,0.138642D+00,0.140861D+00,0.143115D+00,0.145404D+00, & +0.147723D+00,0.150078D+00,0.152466D+00,0.154889D+00,0.157346D+00, & +0.159841D+00,0.162372D+00,0.164939D+00,0.167545D+00,0.170185D+00, & +0.172866D+00,0.175584D+00,0.178340D+00,0.181139D+00,0.183977D+00, & +0.186855D+00,0.189773D+00,0.192737D+00,0.195736D+00,0.198783D+00, & +0.201875D+00,0.205007D+00,0.208186D+00,0.211409D+00,0.214676D+00, & +0.217993D+00,0.221355D+00,0.224764D+00,0.228220D+00,0.231728D+00, & +0.235284D+00,0.238888D+00,0.242542D+00,0.246251D+00,0.250010D+00, & +0.253821D+00,0.257688D+00,0.261602D+00,0.265575D+00,0.269607D+00, & +0.273689D+00,0.277830D+00,0.282027D+00,0.286287D+00,0.290598D+00, & +0.294972D+00,0.299405D+00,0.303904D+00,0.308462D+00,0.313082D+00, & +0.317763D+00,0.322512D+00,0.327324D+00,0.332201D+00,0.337141D+00, & +0.342154D+00,0.347234D+00,0.352387D+00,0.357601D+00,0.362889D+00, & +0.368257D+00,0.373685D+00,0.379194D+00,0.384773D+00,0.390433D+00, & +0.396159D+00,0.401968D+00,0.407861D+00,0.413820D+00,0.419866D+00/ +data (ES(IES),IES=191,285) / & +0.425999D+00,0.432203D+00,0.438494D+00,0.444867D+00,0.451332D+00, & +0.457879D+00,0.464510D+00,0.471226D+00,0.478037D+00,0.484935D+00, & +0.491920D+00,0.499005D+00,0.506181D+00,0.513447D+00,0.520816D+00, & +0.528279D+00,0.535835D+00,0.543497D+00,0.551256D+00,0.559113D+00, & +0.567081D+00,0.575147D+00,0.583315D+00,0.591585D+00,0.599970D+00, & +0.608472D+00,0.617069D+00,0.625785D+00,0.634609D+00,0.643556D+00, & +0.652611D+00,0.661782D+00,0.671077D+00,0.680487D+00,0.690015D+00, & +0.699656D+00,0.709433D+00,0.719344D+00,0.729363D+00,0.739518D+00, & +0.749795D+00,0.760217D+00,0.770763D+00,0.781454D+00,0.792258D+00, & +0.803208D+00,0.814309D+00,0.825528D+00,0.836914D+00,0.848422D+00, & +0.860086D+00,0.871891D+00,0.883837D+00,0.895944D+00,0.908214D+00, & +0.920611D+00,0.933175D+00,0.945890D+00,0.958776D+00,0.971812D+00, & +0.985027D+00,0.998379D+00,0.101193D+01,0.102561D+01,0.103949D+01, & +0.105352D+01,0.106774D+01,0.108213D+01,0.109669D+01,0.111144D+01, & +0.112636D+01,0.114148D+01,0.115676D+01,0.117226D+01,0.118791D+01, & +0.120377D+01,0.121984D+01,0.123608D+01,0.125252D+01,0.126919D+01, & +0.128604D+01,0.130309D+01,0.132036D+01,0.133782D+01,0.135549D+01, & +0.137339D+01,0.139150D+01,0.140984D+01,0.142839D+01,0.144715D+01, & +0.146616D+01,0.148538D+01,0.150482D+01,0.152450D+01,0.154445D+01/ +data (ES(IES),IES=286,380) / & +0.156459D+01,0.158502D+01,0.160564D+01,0.162654D+01,0.164766D+01, & +0.166906D+01,0.169070D+01,0.171257D+01,0.173473D+01,0.175718D+01, & +0.177984D+01,0.180282D+01,0.182602D+01,0.184951D+01,0.187327D+01, & +0.189733D+01,0.192165D+01,0.194629D+01,0.197118D+01,0.199636D+01, & +0.202185D+01,0.204762D+01,0.207372D+01,0.210010D+01,0.212678D+01, & +0.215379D+01,0.218109D+01,0.220873D+01,0.223668D+01,0.226497D+01, & +0.229357D+01,0.232249D+01,0.235176D+01,0.238134D+01,0.241129D+01, & +0.244157D+01,0.247217D+01,0.250316D+01,0.253447D+01,0.256617D+01, & +0.259821D+01,0.263064D+01,0.266341D+01,0.269661D+01,0.273009D+01, & +0.276403D+01,0.279834D+01,0.283302D+01,0.286811D+01,0.290358D+01, & +0.293943D+01,0.297571D+01,0.301236D+01,0.304946D+01,0.308702D+01, & +0.312491D+01,0.316326D+01,0.320208D+01,0.324130D+01,0.328092D+01, & +0.332102D+01,0.336162D+01,0.340264D+01,0.344407D+01,0.348601D+01, & +0.352838D+01,0.357118D+01,0.361449D+01,0.365834D+01,0.370264D+01, & +0.374737D+01,0.379265D+01,0.383839D+01,0.388469D+01,0.393144D+01, & +0.397876D+01,0.402656D+01,0.407492D+01,0.412378D+01,0.417313D+01, & +0.422306D+01,0.427359D+01,0.432454D+01,0.437617D+01,0.442834D+01, & +0.448102D+01,0.453433D+01,0.458816D+01,0.464253D+01,0.469764D+01, & +0.475321D+01,0.480942D+01,0.486629D+01,0.492372D+01,0.498173D+01/ +data (ES(IES),IES=381,475) / & +0.504041D+01,0.509967D+01,0.515962D+01,0.522029D+01,0.528142D+01, & +0.534337D+01,0.540595D+01,0.546912D+01,0.553292D+01,0.559757D+01, & +0.566273D+01,0.572864D+01,0.579532D+01,0.586266D+01,0.593075D+01, & +0.599952D+01,0.606895D+01,0.613918D+01,0.621021D+01,0.628191D+01, & +0.635433D+01,0.642755D+01,0.650162D+01,0.657639D+01,0.665188D+01, & +0.672823D+01,0.680532D+01,0.688329D+01,0.696198D+01,0.704157D+01, & +0.712206D+01,0.720319D+01,0.728534D+01,0.736829D+01,0.745204D+01, & +0.753671D+01,0.762218D+01,0.770860D+01,0.779588D+01,0.788408D+01, & +0.797314D+01,0.806318D+01,0.815408D+01,0.824599D+01,0.833874D+01, & +0.843254D+01,0.852721D+01,0.862293D+01,0.871954D+01,0.881724D+01, & +0.891579D+01,0.901547D+01,0.911624D+01,0.921778D+01,0.932061D+01, & +0.942438D+01,0.952910D+01,0.963497D+01,0.974181D+01,0.984982D+01, & +0.995887D+01,0.100690D+02,0.101804D+02,0.102926D+02,0.104063D+02, & +0.105210D+02,0.106367D+02,0.107536D+02,0.108719D+02,0.109912D+02, & +0.111116D+02,0.112333D+02,0.113563D+02,0.114804D+02,0.116056D+02, & +0.117325D+02,0.118602D+02,0.119892D+02,0.121197D+02,0.122513D+02, & +0.123844D+02,0.125186D+02,0.126543D+02,0.127912D+02,0.129295D+02, & +0.130691D+02,0.132101D+02,0.133527D+02,0.134965D+02,0.136415D+02, & +0.137882D+02,0.139361D+02,0.140855D+02,0.142366D+02,0.143889D+02/ +data (ES(IES),IES=476,570) / & +0.145429D+02,0.146982D+02,0.148552D+02,0.150135D+02,0.151735D+02, & +0.153349D+02,0.154979D+02,0.156624D+02,0.158286D+02,0.159965D+02, & +0.161659D+02,0.163367D+02,0.165094D+02,0.166838D+02,0.168597D+02, & +0.170375D+02,0.172168D+02,0.173979D+02,0.175806D+02,0.177651D+02, & +0.179513D+02,0.181394D+02,0.183293D+02,0.185210D+02,0.187146D+02, & +0.189098D+02,0.191066D+02,0.193059D+02,0.195065D+02,0.197095D+02, & +0.199142D+02,0.201206D+02,0.203291D+02,0.205397D+02,0.207522D+02, & +0.209664D+02,0.211831D+02,0.214013D+02,0.216221D+02,0.218448D+02, & +0.220692D+02,0.222959D+02,0.225250D+02,0.227559D+02,0.229887D+02, & +0.232239D+02,0.234614D+02,0.237014D+02,0.239428D+02,0.241872D+02, & +0.244335D+02,0.246824D+02,0.249332D+02,0.251860D+02,0.254419D+02, & +0.256993D+02,0.259600D+02,0.262225D+02,0.264873D+02,0.267552D+02, & +0.270248D+02,0.272970D+02,0.275719D+02,0.278497D+02,0.281295D+02, & +0.284117D+02,0.286965D+02,0.289843D+02,0.292743D+02,0.295671D+02, & +0.298624D+02,0.301605D+02,0.304616D+02,0.307650D+02,0.310708D+02, & +0.313803D+02,0.316915D+02,0.320064D+02,0.323238D+02,0.326437D+02, & +0.329666D+02,0.332928D+02,0.336215D+02,0.339534D+02,0.342885D+02, & +0.346263D+02,0.349666D+02,0.353109D+02,0.356572D+02,0.360076D+02, & +0.363606D+02,0.367164D+02,0.370757D+02,0.374383D+02,0.378038D+02/ +data (ES(IES),IES=571,665) / & +0.381727D+02,0.385453D+02,0.389206D+02,0.392989D+02,0.396807D+02, & +0.400663D+02,0.404555D+02,0.408478D+02,0.412428D+02,0.416417D+02, & +0.420445D+02,0.424502D+02,0.428600D+02,0.432733D+02,0.436900D+02, & +0.441106D+02,0.445343D+02,0.449620D+02,0.453930D+02,0.458280D+02, & +0.462672D+02,0.467096D+02,0.471561D+02,0.476070D+02,0.480610D+02, & +0.485186D+02,0.489813D+02,0.494474D+02,0.499170D+02,0.503909D+02, & +0.508693D+02,0.513511D+02,0.518376D+02,0.523277D+02,0.528232D+02, & +0.533213D+02,0.538240D+02,0.543315D+02,0.548437D+02,0.553596D+02, & +0.558802D+02,0.564046D+02,0.569340D+02,0.574672D+02,0.580061D+02, & +0.585481D+02,0.590963D+02,0.596482D+02,0.602041D+02,0.607649D+02, & +0.613311D+02,0.619025D+02,0.624779D+02,0.630574D+02,0.636422D+02, & +0.642324D+02,0.648280D+02,0.654278D+02,0.660332D+02,0.666426D+02, & +0.672577D+02,0.678771D+02,0.685034D+02,0.691328D+02,0.697694D+02, & +0.704103D+02,0.710556D+02,0.717081D+02,0.723639D+02,0.730269D+02, & +0.736945D+02,0.743681D+02,0.750463D+02,0.757309D+02,0.764214D+02, & +0.771167D+02,0.778182D+02,0.785246D+02,0.792373D+02,0.799564D+02, & +0.806804D+02,0.814109D+02,0.821479D+02,0.828898D+02,0.836384D+02, & +0.843922D+02,0.851525D+02,0.859198D+02,0.866920D+02,0.874712D+02, & +0.882574D+02,0.890486D+02,0.898470D+02,0.906525D+02,0.914634D+02/ +data (ES(IES),IES=666,760) / & +0.922814D+02,0.931048D+02,0.939356D+02,0.947736D+02,0.956171D+02, & +0.964681D+02,0.973246D+02,0.981907D+02,0.990605D+02,0.999399D+02, & +0.100825D+03,0.101718D+03,0.102617D+03,0.103523D+03,0.104438D+03, & +0.105358D+03,0.106287D+03,0.107221D+03,0.108166D+03,0.109115D+03, & +0.110074D+03,0.111039D+03,0.112012D+03,0.112992D+03,0.113981D+03, & +0.114978D+03,0.115981D+03,0.116993D+03,0.118013D+03,0.119041D+03, & +0.120077D+03,0.121122D+03,0.122173D+03,0.123234D+03,0.124301D+03, & +0.125377D+03,0.126463D+03,0.127556D+03,0.128657D+03,0.129769D+03, & +0.130889D+03,0.132017D+03,0.133152D+03,0.134299D+03,0.135453D+03, & +0.136614D+03,0.137786D+03,0.138967D+03,0.140158D+03,0.141356D+03, & +0.142565D+03,0.143781D+03,0.145010D+03,0.146247D+03,0.147491D+03, & +0.148746D+03,0.150011D+03,0.151284D+03,0.152571D+03,0.153862D+03, & +0.155168D+03,0.156481D+03,0.157805D+03,0.159137D+03,0.160478D+03, & +0.161832D+03,0.163198D+03,0.164569D+03,0.165958D+03,0.167348D+03, & +0.168757D+03,0.170174D+03,0.171599D+03,0.173037D+03,0.174483D+03, & +0.175944D+03,0.177414D+03,0.178892D+03,0.180387D+03,0.181886D+03, & +0.183402D+03,0.184930D+03,0.186463D+03,0.188012D+03,0.189571D+03, & +0.191146D+03,0.192730D+03,0.194320D+03,0.195930D+03,0.197546D+03, & +0.199175D+03,0.200821D+03,0.202473D+03,0.204142D+03,0.205817D+03/ +data (ES(IES),IES=761,855) / & +0.207510D+03,0.209216D+03,0.210928D+03,0.212658D+03,0.214398D+03, & +0.216152D+03,0.217920D+03,0.219698D+03,0.221495D+03,0.223297D+03, & +0.225119D+03,0.226951D+03,0.228793D+03,0.230654D+03,0.232522D+03, & +0.234413D+03,0.236311D+03,0.238223D+03,0.240151D+03,0.242090D+03, & +0.244049D+03,0.246019D+03,0.248000D+03,0.249996D+03,0.252009D+03, & +0.254037D+03,0.256077D+03,0.258128D+03,0.260200D+03,0.262284D+03, & +0.264384D+03,0.266500D+03,0.268629D+03,0.270779D+03,0.272936D+03, & +0.275110D+03,0.277306D+03,0.279509D+03,0.281734D+03,0.283966D+03, & +0.286227D+03,0.288494D+03,0.290780D+03,0.293083D+03,0.295398D+03, & +0.297737D+03,0.300089D+03,0.302453D+03,0.304841D+03,0.307237D+03, & +0.309656D+03,0.312095D+03,0.314541D+03,0.317012D+03,0.319496D+03, & +0.322005D+03,0.324527D+03,0.327063D+03,0.329618D+03,0.332193D+03, & +0.334788D+03,0.337396D+03,0.340025D+03,0.342673D+03,0.345329D+03, & +0.348019D+03,0.350722D+03,0.353440D+03,0.356178D+03,0.358938D+03, & +0.361718D+03,0.364513D+03,0.367322D+03,0.370160D+03,0.373012D+03, & +0.375885D+03,0.378788D+03,0.381691D+03,0.384631D+03,0.387579D+03, & +0.390556D+03,0.393556D+03,0.396563D+03,0.399601D+03,0.402646D+03, & +0.405730D+03,0.408829D+03,0.411944D+03,0.415083D+03,0.418236D+03, & +0.421422D+03,0.424632D+03,0.427849D+03,0.431099D+03,0.434365D+03/ +data (ES(IES),IES=856,950) / & +0.437655D+03,0.440970D+03,0.444301D+03,0.447666D+03,0.451038D+03, & +0.454445D+03,0.457876D+03,0.461316D+03,0.464790D+03,0.468281D+03, & +0.471798D+03,0.475342D+03,0.478902D+03,0.482497D+03,0.486101D+03, & +0.489741D+03,0.493408D+03,0.497083D+03,0.500804D+03,0.504524D+03, & +0.508290D+03,0.512074D+03,0.515877D+03,0.519717D+03,0.523566D+03, & +0.527462D+03,0.531367D+03,0.535301D+03,0.539264D+03,0.543245D+03, & +0.547265D+03,0.551305D+03,0.555363D+03,0.559462D+03,0.563579D+03, & +0.567727D+03,0.571905D+03,0.576102D+03,0.580329D+03,0.584576D+03, & +0.588865D+03,0.593185D+03,0.597514D+03,0.601885D+03,0.606276D+03, & +0.610699D+03,0.615151D+03,0.619625D+03,0.624140D+03,0.628671D+03, & +0.633243D+03,0.637845D+03,0.642465D+03,0.647126D+03,0.651806D+03, & +0.656527D+03,0.661279D+03,0.666049D+03,0.670861D+03,0.675692D+03, & +0.680566D+03,0.685471D+03,0.690396D+03,0.695363D+03,0.700350D+03, & +0.705381D+03,0.710444D+03,0.715527D+03,0.720654D+03,0.725801D+03, & +0.730994D+03,0.736219D+03,0.741465D+03,0.746756D+03,0.752068D+03, & +0.757426D+03,0.762819D+03,0.768231D+03,0.773692D+03,0.779172D+03, & +0.784701D+03,0.790265D+03,0.795849D+03,0.801483D+03,0.807137D+03, & +0.812842D+03,0.818582D+03,0.824343D+03,0.830153D+03,0.835987D+03, & +0.841871D+03,0.847791D+03,0.853733D+03,0.859727D+03,0.865743D+03/ +data (ES(IES),IES=951,1045) / & +0.871812D+03,0.877918D+03,0.884046D+03,0.890228D+03,0.896433D+03, & +0.902690D+03,0.908987D+03,0.915307D+03,0.921681D+03,0.928078D+03, & +0.934531D+03,0.941023D+03,0.947539D+03,0.954112D+03,0.960708D+03, & +0.967361D+03,0.974053D+03,0.980771D+03,0.987545D+03,0.994345D+03, & +0.100120D+04,0.100810D+04,0.101502D+04,0.102201D+04,0.102902D+04, & +0.103608D+04,0.104320D+04,0.105033D+04,0.105753D+04,0.106475D+04, & +0.107204D+04,0.107936D+04,0.108672D+04,0.109414D+04,0.110158D+04, & +0.110908D+04,0.111663D+04,0.112421D+04,0.113185D+04,0.113952D+04, & +0.114725D+04,0.115503D+04,0.116284D+04,0.117071D+04,0.117861D+04, & +0.118658D+04,0.119459D+04,0.120264D+04,0.121074D+04,0.121888D+04, & +0.122709D+04,0.123534D+04,0.124362D+04,0.125198D+04,0.126036D+04, & +0.126881D+04,0.127731D+04,0.128584D+04,0.129444D+04,0.130307D+04, & +0.131177D+04,0.132053D+04,0.132931D+04,0.133817D+04,0.134705D+04, & +0.135602D+04,0.136503D+04,0.137407D+04,0.138319D+04,0.139234D+04, & +0.140156D+04,0.141084D+04,0.142015D+04,0.142954D+04,0.143896D+04, & +0.144845D+04,0.145800D+04,0.146759D+04,0.147725D+04,0.148694D+04, & +0.149672D+04,0.150655D+04,0.151641D+04,0.152635D+04,0.153633D+04, & +0.154639D+04,0.155650D+04,0.156665D+04,0.157688D+04,0.158715D+04, & +0.159750D+04,0.160791D+04,0.161836D+04,0.162888D+04,0.163945D+04/ +data (ES(IES),IES=1046,1140) / & +0.165010D+04,0.166081D+04,0.167155D+04,0.168238D+04,0.169325D+04, & +0.170420D+04,0.171522D+04,0.172627D+04,0.173741D+04,0.174859D+04, & +0.175986D+04,0.177119D+04,0.178256D+04,0.179402D+04,0.180552D+04, & +0.181711D+04,0.182877D+04,0.184046D+04,0.185224D+04,0.186407D+04, & +0.187599D+04,0.188797D+04,0.190000D+04,0.191212D+04,0.192428D+04, & +0.193653D+04,0.194886D+04,0.196122D+04,0.197368D+04,0.198618D+04, & +0.199878D+04,0.201145D+04,0.202416D+04,0.203698D+04,0.204983D+04, & +0.206278D+04,0.207580D+04,0.208887D+04,0.210204D+04,0.211525D+04, & +0.212856D+04,0.214195D+04,0.215538D+04,0.216892D+04,0.218249D+04, & +0.219618D+04,0.220994D+04,0.222375D+04,0.223766D+04,0.225161D+04, & +0.226567D+04,0.227981D+04,0.229399D+04,0.230829D+04,0.232263D+04, & +0.233708D+04,0.235161D+04,0.236618D+04,0.238087D+04,0.239560D+04, & +0.241044D+04,0.242538D+04,0.244035D+04,0.245544D+04,0.247057D+04, & +0.248583D+04,0.250116D+04,0.251654D+04,0.253204D+04,0.254759D+04, & +0.256325D+04,0.257901D+04,0.259480D+04,0.261073D+04,0.262670D+04, & +0.264279D+04,0.265896D+04,0.267519D+04,0.269154D+04,0.270794D+04, & +0.272447D+04,0.274108D+04,0.275774D+04,0.277453D+04,0.279137D+04, & +0.280834D+04,0.282540D+04,0.284251D+04,0.285975D+04,0.287704D+04, & +0.289446D+04,0.291198D+04,0.292954D+04,0.294725D+04,0.296499D+04/ +data (ES(IES),IES=1141,1235) / & +0.298288D+04,0.300087D+04,0.301890D+04,0.303707D+04,0.305529D+04, & +0.307365D+04,0.309211D+04,0.311062D+04,0.312927D+04,0.314798D+04, & +0.316682D+04,0.318577D+04,0.320477D+04,0.322391D+04,0.324310D+04, & +0.326245D+04,0.328189D+04,0.330138D+04,0.332103D+04,0.334073D+04, & +0.336058D+04,0.338053D+04,0.340054D+04,0.342069D+04,0.344090D+04, & +0.346127D+04,0.348174D+04,0.350227D+04,0.352295D+04,0.354369D+04, & +0.356458D+04,0.358559D+04,0.360664D+04,0.362787D+04,0.364914D+04, & +0.367058D+04,0.369212D+04,0.371373D+04,0.373548D+04,0.375731D+04, & +0.377929D+04,0.380139D+04,0.382355D+04,0.384588D+04,0.386826D+04, & +0.389081D+04,0.391348D+04,0.393620D+04,0.395910D+04,0.398205D+04, & +0.400518D+04,0.402843D+04,0.405173D+04,0.407520D+04,0.409875D+04, & +0.412246D+04,0.414630D+04,0.417019D+04,0.419427D+04,0.421840D+04, & +0.424272D+04,0.426715D+04,0.429165D+04,0.431634D+04,0.434108D+04, & +0.436602D+04,0.439107D+04,0.441618D+04,0.444149D+04,0.446685D+04, & +0.449241D+04,0.451810D+04,0.454385D+04,0.456977D+04,0.459578D+04, & +0.462197D+04,0.464830D+04,0.467468D+04,0.470127D+04,0.472792D+04, & +0.475477D+04,0.478175D+04,0.480880D+04,0.483605D+04,0.486336D+04, & +0.489087D+04,0.491853D+04,0.494623D+04,0.497415D+04,0.500215D+04, & +0.503034D+04,0.505867D+04,0.508707D+04,0.511568D+04,0.514436D+04/ +data (ES(IES),IES=1236,1330) / & +0.517325D+04,0.520227D+04,0.523137D+04,0.526068D+04,0.529005D+04, & +0.531965D+04,0.534939D+04,0.537921D+04,0.540923D+04,0.543932D+04, & +0.546965D+04,0.550011D+04,0.553064D+04,0.556139D+04,0.559223D+04, & +0.562329D+04,0.565449D+04,0.568577D+04,0.571727D+04,0.574884D+04, & +0.578064D+04,0.581261D+04,0.584464D+04,0.587692D+04,0.590924D+04, & +0.594182D+04,0.597455D+04,0.600736D+04,0.604039D+04,0.607350D+04, & +0.610685D+04,0.614036D+04,0.617394D+04,0.620777D+04,0.624169D+04, & +0.627584D+04,0.631014D+04,0.634454D+04,0.637918D+04,0.641390D+04, & +0.644887D+04,0.648400D+04,0.651919D+04,0.655467D+04,0.659021D+04, & +0.662599D+04,0.666197D+04,0.669800D+04,0.673429D+04,0.677069D+04, & +0.680735D+04,0.684415D+04,0.688104D+04,0.691819D+04,0.695543D+04, & +0.699292D+04,0.703061D+04,0.706837D+04,0.710639D+04,0.714451D+04, & +0.718289D+04,0.722143D+04,0.726009D+04,0.729903D+04,0.733802D+04, & +0.737729D+04,0.741676D+04,0.745631D+04,0.749612D+04,0.753602D+04, & +0.757622D+04,0.761659D+04,0.765705D+04,0.769780D+04,0.773863D+04, & +0.777975D+04,0.782106D+04,0.786246D+04,0.790412D+04,0.794593D+04, & +0.798802D+04,0.803028D+04,0.807259D+04,0.811525D+04,0.815798D+04, & +0.820102D+04,0.824427D+04,0.828757D+04,0.833120D+04,0.837493D+04, & +0.841895D+04,0.846313D+04,0.850744D+04,0.855208D+04,0.859678D+04/ +data (ES(IES),IES=1331,1425) / & +0.864179D+04,0.868705D+04,0.873237D+04,0.877800D+04,0.882374D+04, & +0.886979D+04,0.891603D+04,0.896237D+04,0.900904D+04,0.905579D+04, & +0.910288D+04,0.915018D+04,0.919758D+04,0.924529D+04,0.929310D+04, & +0.934122D+04,0.938959D+04,0.943804D+04,0.948687D+04,0.953575D+04, & +0.958494D+04,0.963442D+04,0.968395D+04,0.973384D+04,0.978383D+04, & +0.983412D+04,0.988468D+04,0.993534D+04,0.998630D+04,0.100374D+05, & +0.100888D+05,0.101406D+05,0.101923D+05,0.102444D+05,0.102966D+05, & +0.103492D+05,0.104020D+05,0.104550D+05,0.105082D+05,0.105616D+05, & +0.106153D+05,0.106693D+05,0.107234D+05,0.107779D+05,0.108325D+05, & +0.108874D+05,0.109425D+05,0.109978D+05,0.110535D+05,0.111092D+05, & +0.111653D+05,0.112217D+05,0.112782D+05,0.113350D+05,0.113920D+05, & +0.114493D+05,0.115070D+05,0.115646D+05,0.116228D+05,0.116809D+05, & +0.117396D+05,0.117984D+05,0.118574D+05,0.119167D+05,0.119762D+05, & +0.120360D+05,0.120962D+05,0.121564D+05,0.122170D+05,0.122778D+05, & +0.123389D+05,0.124004D+05,0.124619D+05,0.125238D+05,0.125859D+05, & +0.126484D+05,0.127111D+05,0.127739D+05,0.128372D+05,0.129006D+05, & +0.129644D+05,0.130285D+05,0.130927D+05,0.131573D+05,0.132220D+05, & +0.132872D+05,0.133526D+05,0.134182D+05,0.134842D+05,0.135503D+05, & +0.136168D+05,0.136836D+05,0.137505D+05,0.138180D+05,0.138854D+05/ +data (ES(IES),IES=1426,1520) / & +0.139534D+05,0.140216D+05,0.140900D+05,0.141588D+05,0.142277D+05, & +0.142971D+05,0.143668D+05,0.144366D+05,0.145069D+05,0.145773D+05, & +0.146481D+05,0.147192D+05,0.147905D+05,0.148622D+05,0.149341D+05, & +0.150064D+05,0.150790D+05,0.151517D+05,0.152250D+05,0.152983D+05, & +0.153721D+05,0.154462D+05,0.155205D+05,0.155952D+05,0.156701D+05, & +0.157454D+05,0.158211D+05,0.158969D+05,0.159732D+05,0.160496D+05, & +0.161265D+05,0.162037D+05,0.162811D+05,0.163589D+05,0.164369D+05, & +0.165154D+05,0.165942D+05,0.166732D+05,0.167526D+05,0.168322D+05, & +0.169123D+05,0.169927D+05,0.170733D+05,0.171543D+05,0.172356D+05, & +0.173173D+05,0.173993D+05,0.174815D+05,0.175643D+05,0.176471D+05, & +0.177305D+05,0.178143D+05,0.178981D+05,0.179826D+05,0.180671D+05, & +0.181522D+05,0.182377D+05,0.183232D+05,0.184093D+05,0.184955D+05, & +0.185823D+05,0.186695D+05,0.187568D+05,0.188447D+05,0.189326D+05, & +0.190212D+05,0.191101D+05,0.191991D+05,0.192887D+05,0.193785D+05, & +0.194688D+05,0.195595D+05,0.196503D+05,0.197417D+05,0.198332D+05, & +0.199253D+05,0.200178D+05,0.201105D+05,0.202036D+05,0.202971D+05, & +0.203910D+05,0.204853D+05,0.205798D+05,0.206749D+05,0.207701D+05, & +0.208659D+05,0.209621D+05,0.210584D+05,0.211554D+05,0.212524D+05, & +0.213501D+05,0.214482D+05,0.215465D+05,0.216452D+05,0.217442D+05/ +data (ES(IES),IES=1521,1552) / & +0.218439D+05,0.219439D+05,0.220440D+05,0.221449D+05,0.222457D+05, & +0.223473D+05,0.224494D+05,0.225514D+05,0.226542D+05,0.227571D+05, & +0.228606D+05,0.229646D+05,0.230687D+05,0.231734D+05,0.232783D+05, & +0.233839D+05,0.234898D+05,0.235960D+05,0.237027D+05,0.238097D+05, & +0.239173D+05,0.240254D+05,0.241335D+05,0.242424D+05,0.243514D+05, & +0.244611D+05,0.245712D+05,0.246814D+05,0.247923D+05,0.249034D+05, & +0.250152D+05,0.250152D+05/ + +do I = 1, npnts + + ! Compute the factor that converts from sat vapour pressure in a + ! pure water system to sat vapour pressure in air, FSUBW. This formula + ! is taken from equation A4.7 of Adrian Gill's Book: Atmosphere-Ocean + ! Dynamics. Note that his formula works in terms of pressure in MB and + ! temperature in Celsius, so conversion of units leads to the slightly + ! different equation used here. + + FSUBW = one + 1.0E-8_kind_real * P(I) & + * (4.5_kind_real + 6.0E-4_kind_real * (T(I) - t0c) * (T(I) - t0c)) + + ! use the lookup table to find saturated vapour pressure, and store it in QS + + TT = max (T_low, T(I)) + TT = min (T_high, TT) + + ATABLE = (TT - T_low + delta_T) / delta_T + ITABLE = ATABLE + ATABLE = ATABLE - ITABLE + + QS(I) = (one - ATABLE) * ES(ITABLE) + ATABLE * ES(ITABLE + 1) + + ! Multiple by FSUBW to convert to saturated vapour pressure in air + ! (equation A4.6 of Adrian Gill's book) + + QS(I) = QS(I) * FSUBW + + ! Now form the accurate expression for QS, which is a rearranged version of + ! equation A4.3 of Gill's book. ! - ! Method: - ! Uses lookup tables to find eSAT, calculates qSAT directly from that. - !----------------------------------------------------------------------- - - subroutine Ops_Qsat ( & - QS, & - T, & - P, & - NPNTS) - - implicit none - - ! Subroutine arguments: - integer, intent(in) :: NPNTS ! Points being processed by qSAT scheme. - real(kind_real), intent(in) :: T(NPNTS) ! Temperature (K) - real(kind_real), intent(in) :: P(NPNTS) ! Pressure (Pa). - real(kind_real), intent(inout) :: QS(NPNTS) ! Saturation mixing ratio (KG/KG) - - ! Local declarations: - real(kind_real), parameter :: ONE_MINUS_EPSILON = 1.0 - Epsilon - real(kind_real), parameter :: T_LOW = 183.15 ! Lowest temperature for which look-up table is valid - real(kind_real), parameter :: T_HIGH = 338.15 ! Highest temperature for which look-up table is valid - real(kind_real), parameter :: DELTA_T = 0.1 ! Temperature increment of look-up table - integer, parameter :: N = ((T_HIGH - T_LOW + (DELTA_T * 0.5)) / DELTA_T) + 1.0 ! Size of lookup-table (gives 1551) - integer :: ITABLE - real(kind_real) :: ATABLE - real(kind_real) :: FSUBW ! Converts from sat vapour pressure in pure water to pressure in air - real(kind_real) :: TT - integer :: I - integer :: IES - real(kind_real) :: ES(0:N + 1) ! Table of saturation water vapour pressure (PA) - character(len=*), parameter :: RoutineName = "Ops_Qsat" - - ! Note: 0 element is a repeat of 1st element to cater for special case - ! of low temperatures (.LE.T_LOW) for which the array index is - ! rounded down due to machine precision. - - data (ES(IES), IES= 0, 95) / 0.966483E-02, & - 0.966483E-02,0.984279E-02,0.100240E-01,0.102082E-01,0.103957E-01, & - 0.105865E-01,0.107803E-01,0.109777E-01,0.111784E-01,0.113825E-01, & - 0.115902E-01,0.118016E-01,0.120164E-01,0.122348E-01,0.124572E-01, & - 0.126831E-01,0.129132E-01,0.131470E-01,0.133846E-01,0.136264E-01, & - 0.138724E-01,0.141225E-01,0.143771E-01,0.146356E-01,0.148985E-01, & - 0.151661E-01,0.154379E-01,0.157145E-01,0.159958E-01,0.162817E-01, & - 0.165725E-01,0.168680E-01,0.171684E-01,0.174742E-01,0.177847E-01, & - 0.181008E-01,0.184216E-01,0.187481E-01,0.190801E-01,0.194175E-01, & - 0.197608E-01,0.201094E-01,0.204637E-01,0.208242E-01,0.211906E-01, & - 0.215631E-01,0.219416E-01,0.223263E-01,0.227172E-01,0.231146E-01, & - 0.235188E-01,0.239296E-01,0.243465E-01,0.247708E-01,0.252019E-01, & - 0.256405E-01,0.260857E-01,0.265385E-01,0.269979E-01,0.274656E-01, & - 0.279405E-01,0.284232E-01,0.289142E-01,0.294124E-01,0.299192E-01, & - 0.304341E-01,0.309571E-01,0.314886E-01,0.320285E-01,0.325769E-01, & - 0.331348E-01,0.337014E-01,0.342771E-01,0.348618E-01,0.354557E-01, & - 0.360598E-01,0.366727E-01,0.372958E-01,0.379289E-01,0.385717E-01, & - 0.392248E-01,0.398889E-01,0.405633E-01,0.412474E-01,0.419430E-01, & - 0.426505E-01,0.433678E-01,0.440974E-01,0.448374E-01,0.455896E-01, & - 0.463545E-01,0.471303E-01,0.479191E-01,0.487190E-01,0.495322E-01/ - data (ES(IES),IES= 96,190) / & - 0.503591E-01,0.511977E-01,0.520490E-01,0.529145E-01,0.537931E-01, & - 0.546854E-01,0.555924E-01,0.565119E-01,0.574467E-01,0.583959E-01, & - 0.593592E-01,0.603387E-01,0.613316E-01,0.623409E-01,0.633655E-01, & - 0.644053E-01,0.654624E-01,0.665358E-01,0.676233E-01,0.687302E-01, & - 0.698524E-01,0.709929E-01,0.721490E-01,0.733238E-01,0.745180E-01, & - 0.757281E-01,0.769578E-01,0.782061E-01,0.794728E-01,0.807583E-01, & - 0.820647E-01,0.833905E-01,0.847358E-01,0.861028E-01,0.874882E-01, & - 0.888957E-01,0.903243E-01,0.917736E-01,0.932464E-01,0.947407E-01, & - 0.962571E-01,0.977955E-01,0.993584E-01,0.100942E+00,0.102551E+00, & - 0.104186E+00,0.105842E+00,0.107524E+00,0.109231E+00,0.110963E+00, & - 0.112722E+00,0.114506E+00,0.116317E+00,0.118153E+00,0.120019E+00, & - 0.121911E+00,0.123831E+00,0.125778E+00,0.127755E+00,0.129761E+00, & - 0.131796E+00,0.133863E+00,0.135956E+00,0.138082E+00,0.140241E+00, & - 0.142428E+00,0.144649E+00,0.146902E+00,0.149190E+00,0.151506E+00, & - 0.153859E+00,0.156245E+00,0.158669E+00,0.161126E+00,0.163618E+00, & - 0.166145E+00,0.168711E+00,0.171313E+00,0.173951E+00,0.176626E+00, & - 0.179342E+00,0.182096E+00,0.184893E+00,0.187724E+00,0.190600E+00, & - 0.193518E+00,0.196473E+00,0.199474E+00,0.202516E+00,0.205604E+00, & - 0.208730E+00,0.211905E+00,0.215127E+00,0.218389E+00,0.221701E+00/ - data (ES(IES),IES=191,285) / & - 0.225063E+00,0.228466E+00,0.231920E+00,0.235421E+00,0.238976E+00, & - 0.242580E+00,0.246232E+00,0.249933E+00,0.253691E+00,0.257499E+00, & - 0.261359E+00,0.265278E+00,0.269249E+00,0.273274E+00,0.277358E+00, & - 0.281498E+00,0.285694E+00,0.289952E+00,0.294268E+00,0.298641E+00, & - 0.303078E+00,0.307577E+00,0.312135E+00,0.316753E+00,0.321440E+00, & - 0.326196E+00,0.331009E+00,0.335893E+00,0.340842E+00,0.345863E+00, & - 0.350951E+00,0.356106E+00,0.361337E+00,0.366636E+00,0.372006E+00, & - 0.377447E+00,0.382966E+00,0.388567E+00,0.394233E+00,0.399981E+00, & - 0.405806E+00,0.411714E+00,0.417699E+00,0.423772E+00,0.429914E+00, & - 0.436145E+00,0.442468E+00,0.448862E+00,0.455359E+00,0.461930E+00, & - 0.468596E+00,0.475348E+00,0.482186E+00,0.489124E+00,0.496160E+00, & - 0.503278E+00,0.510497E+00,0.517808E+00,0.525224E+00,0.532737E+00, & - 0.540355E+00,0.548059E+00,0.555886E+00,0.563797E+00,0.571825E+00, & - 0.579952E+00,0.588198E+00,0.596545E+00,0.605000E+00,0.613572E+00, & - 0.622255E+00,0.631059E+00,0.639962E+00,0.649003E+00,0.658144E+00, & - 0.667414E+00,0.676815E+00,0.686317E+00,0.695956E+00,0.705728E+00, & - 0.715622E+00,0.725641E+00,0.735799E+00,0.746082E+00,0.756495E+00, & - 0.767052E+00,0.777741E+00,0.788576E+00,0.799549E+00,0.810656E+00, & - 0.821914E+00,0.833314E+00,0.844854E+00,0.856555E+00,0.868415E+00/ - data (ES(IES),IES=286,380) / & - 0.880404E+00,0.892575E+00,0.904877E+00,0.917350E+00,0.929974E+00, & - 0.942771E+00,0.955724E+00,0.968837E+00,0.982127E+00,0.995600E+00, & - 0.100921E+01,0.102304E+01,0.103700E+01,0.105116E+01,0.106549E+01, & - 0.108002E+01,0.109471E+01,0.110962E+01,0.112469E+01,0.113995E+01, & - 0.115542E+01,0.117107E+01,0.118693E+01,0.120298E+01,0.121923E+01, & - 0.123569E+01,0.125234E+01,0.126923E+01,0.128631E+01,0.130362E+01, & - 0.132114E+01,0.133887E+01,0.135683E+01,0.137500E+01,0.139342E+01, & - 0.141205E+01,0.143091E+01,0.145000E+01,0.146933E+01,0.148892E+01, & - 0.150874E+01,0.152881E+01,0.154912E+01,0.156970E+01,0.159049E+01, & - 0.161159E+01,0.163293E+01,0.165452E+01,0.167640E+01,0.169852E+01, & - 0.172091E+01,0.174359E+01,0.176653E+01,0.178977E+01,0.181332E+01, & - 0.183709E+01,0.186119E+01,0.188559E+01,0.191028E+01,0.193524E+01, & - 0.196054E+01,0.198616E+01,0.201208E+01,0.203829E+01,0.206485E+01, & - 0.209170E+01,0.211885E+01,0.214637E+01,0.217424E+01,0.220242E+01, & - 0.223092E+01,0.225979E+01,0.228899E+01,0.231855E+01,0.234845E+01, & - 0.237874E+01,0.240937E+01,0.244040E+01,0.247176E+01,0.250349E+01, & - 0.253560E+01,0.256814E+01,0.260099E+01,0.263431E+01,0.266800E+01, & - 0.270207E+01,0.273656E+01,0.277145E+01,0.280671E+01,0.284248E+01, & - 0.287859E+01,0.291516E+01,0.295219E+01,0.298962E+01,0.302746E+01/ - data (ES(IES),IES=381,475) / & - 0.306579E+01,0.310454E+01,0.314377E+01,0.318351E+01,0.322360E+01, & - 0.326427E+01,0.330538E+01,0.334694E+01,0.338894E+01,0.343155E+01, & - 0.347456E+01,0.351809E+01,0.356216E+01,0.360673E+01,0.365184E+01, & - 0.369744E+01,0.374352E+01,0.379018E+01,0.383743E+01,0.388518E+01, & - 0.393344E+01,0.398230E+01,0.403177E+01,0.408175E+01,0.413229E+01, & - 0.418343E+01,0.423514E+01,0.428746E+01,0.434034E+01,0.439389E+01, & - 0.444808E+01,0.450276E+01,0.455820E+01,0.461423E+01,0.467084E+01, & - 0.472816E+01,0.478607E+01,0.484468E+01,0.490393E+01,0.496389E+01, & - 0.502446E+01,0.508580E+01,0.514776E+01,0.521047E+01,0.527385E+01, & - 0.533798E+01,0.540279E+01,0.546838E+01,0.553466E+01,0.560173E+01, & - 0.566949E+01,0.573807E+01,0.580750E+01,0.587749E+01,0.594846E+01, & - 0.602017E+01,0.609260E+01,0.616591E+01,0.623995E+01,0.631490E+01, & - 0.639061E+01,0.646723E+01,0.654477E+01,0.662293E+01,0.670220E+01, & - 0.678227E+01,0.686313E+01,0.694495E+01,0.702777E+01,0.711142E+01, & - 0.719592E+01,0.728140E+01,0.736790E+01,0.745527E+01,0.754352E+01, & - 0.763298E+01,0.772316E+01,0.781442E+01,0.790676E+01,0.800001E+01, & - 0.809435E+01,0.818967E+01,0.828606E+01,0.838343E+01,0.848194E+01, & - 0.858144E+01,0.868207E+01,0.878392E+01,0.888673E+01,0.899060E+01, & - 0.909567E+01,0.920172E+01,0.930909E+01,0.941765E+01,0.952730E+01/ - data (ES(IES),IES=476,570) / & - 0.963821E+01,0.975022E+01,0.986352E+01,0.997793E+01,0.100937E+02, & - 0.102105E+02,0.103287E+02,0.104481E+02,0.105688E+02,0.106909E+02, & - 0.108143E+02,0.109387E+02,0.110647E+02,0.111921E+02,0.113207E+02, & - 0.114508E+02,0.115821E+02,0.117149E+02,0.118490E+02,0.119847E+02, & - 0.121216E+02,0.122601E+02,0.124002E+02,0.125416E+02,0.126846E+02, & - 0.128290E+02,0.129747E+02,0.131224E+02,0.132712E+02,0.134220E+02, & - 0.135742E+02,0.137278E+02,0.138831E+02,0.140403E+02,0.141989E+02, & - 0.143589E+02,0.145211E+02,0.146845E+02,0.148501E+02,0.150172E+02, & - 0.151858E+02,0.153564E+02,0.155288E+02,0.157029E+02,0.158786E+02, & - 0.160562E+02,0.162358E+02,0.164174E+02,0.166004E+02,0.167858E+02, & - 0.169728E+02,0.171620E+02,0.173528E+02,0.175455E+02,0.177406E+02, & - 0.179372E+02,0.181363E+02,0.183372E+02,0.185400E+02,0.187453E+02, & - 0.189523E+02,0.191613E+02,0.193728E+02,0.195866E+02,0.198024E+02, & - 0.200200E+02,0.202401E+02,0.204626E+02,0.206871E+02,0.209140E+02, & - 0.211430E+02,0.213744E+02,0.216085E+02,0.218446E+02,0.220828E+02, & - 0.223241E+02,0.225671E+02,0.228132E+02,0.230615E+02,0.233120E+02, & - 0.235651E+02,0.238211E+02,0.240794E+02,0.243404E+02,0.246042E+02, & - 0.248704E+02,0.251390E+02,0.254109E+02,0.256847E+02,0.259620E+02, & - 0.262418E+02,0.265240E+02,0.268092E+02,0.270975E+02,0.273883E+02/ - data (ES(IES),IES=571,665) / & - 0.276822E+02,0.279792E+02,0.282789E+02,0.285812E+02,0.288867E+02, & - 0.291954E+02,0.295075E+02,0.298222E+02,0.301398E+02,0.304606E+02, & - 0.307848E+02,0.311119E+02,0.314424E+02,0.317763E+02,0.321133E+02, & - 0.324536E+02,0.327971E+02,0.331440E+02,0.334940E+02,0.338475E+02, & - 0.342050E+02,0.345654E+02,0.349295E+02,0.352975E+02,0.356687E+02, & - 0.360430E+02,0.364221E+02,0.368042E+02,0.371896E+02,0.375790E+02, & - 0.379725E+02,0.383692E+02,0.387702E+02,0.391744E+02,0.395839E+02, & - 0.399958E+02,0.404118E+02,0.408325E+02,0.412574E+02,0.416858E+02, & - 0.421188E+02,0.425551E+02,0.429962E+02,0.434407E+02,0.438910E+02, & - 0.443439E+02,0.448024E+02,0.452648E+02,0.457308E+02,0.462018E+02, & - 0.466775E+02,0.471582E+02,0.476428E+02,0.481313E+02,0.486249E+02, & - 0.491235E+02,0.496272E+02,0.501349E+02,0.506479E+02,0.511652E+02, & - 0.516876E+02,0.522142E+02,0.527474E+02,0.532836E+02,0.538266E+02, & - 0.543737E+02,0.549254E+02,0.554839E+02,0.560456E+02,0.566142E+02, & - 0.571872E+02,0.577662E+02,0.583498E+02,0.589392E+02,0.595347E+02, & - 0.601346E+02,0.607410E+02,0.613519E+02,0.619689E+02,0.625922E+02, & - 0.632204E+02,0.638550E+02,0.644959E+02,0.651418E+02,0.657942E+02, & - 0.664516E+02,0.671158E+02,0.677864E+02,0.684624E+02,0.691451E+02, & - 0.698345E+02,0.705293E+02,0.712312E+02,0.719398E+02,0.726542E+02/ - data (ES(IES),IES=666,760) / & - 0.733754E+02,0.741022E+02,0.748363E+02,0.755777E+02,0.763247E+02, & - 0.770791E+02,0.778394E+02,0.786088E+02,0.793824E+02,0.801653E+02, & - 0.809542E+02,0.817509E+02,0.825536E+02,0.833643E+02,0.841828E+02, & - 0.850076E+02,0.858405E+02,0.866797E+02,0.875289E+02,0.883827E+02, & - 0.892467E+02,0.901172E+02,0.909962E+02,0.918818E+02,0.927760E+02, & - 0.936790E+02,0.945887E+02,0.955071E+02,0.964346E+02,0.973689E+02, & - 0.983123E+02,0.992648E+02,0.100224E+03,0.101193E+03,0.102169E+03, & - 0.103155E+03,0.104150E+03,0.105152E+03,0.106164E+03,0.107186E+03, & - 0.108217E+03,0.109256E+03,0.110303E+03,0.111362E+03,0.112429E+03, & - 0.113503E+03,0.114588E+03,0.115684E+03,0.116789E+03,0.117903E+03, & - 0.119028E+03,0.120160E+03,0.121306E+03,0.122460E+03,0.123623E+03, & - 0.124796E+03,0.125981E+03,0.127174E+03,0.128381E+03,0.129594E+03, & - 0.130822E+03,0.132058E+03,0.133306E+03,0.134563E+03,0.135828E+03, & - 0.137109E+03,0.138402E+03,0.139700E+03,0.141017E+03,0.142338E+03, & - 0.143676E+03,0.145025E+03,0.146382E+03,0.147753E+03,0.149133E+03, & - 0.150529E+03,0.151935E+03,0.153351E+03,0.154783E+03,0.156222E+03, & - 0.157678E+03,0.159148E+03,0.160624E+03,0.162117E+03,0.163621E+03, & - 0.165142E+03,0.166674E+03,0.168212E+03,0.169772E+03,0.171340E+03, & - 0.172921E+03,0.174522E+03,0.176129E+03,0.177755E+03,0.179388E+03/ - data (ES(IES),IES=761,855) / & - 0.181040E+03,0.182707E+03,0.184382E+03,0.186076E+03,0.187782E+03, & - 0.189503E+03,0.191240E+03,0.192989E+03,0.194758E+03,0.196535E+03, & - 0.198332E+03,0.200141E+03,0.201963E+03,0.203805E+03,0.205656E+03, & - 0.207532E+03,0.209416E+03,0.211317E+03,0.213236E+03,0.215167E+03, & - 0.217121E+03,0.219087E+03,0.221067E+03,0.223064E+03,0.225080E+03, & - 0.227113E+03,0.229160E+03,0.231221E+03,0.233305E+03,0.235403E+03, & - 0.237520E+03,0.239655E+03,0.241805E+03,0.243979E+03,0.246163E+03, & - 0.248365E+03,0.250593E+03,0.252830E+03,0.255093E+03,0.257364E+03, & - 0.259667E+03,0.261979E+03,0.264312E+03,0.266666E+03,0.269034E+03, & - 0.271430E+03,0.273841E+03,0.276268E+03,0.278722E+03,0.281185E+03, & - 0.283677E+03,0.286190E+03,0.288714E+03,0.291266E+03,0.293834E+03, & - 0.296431E+03,0.299045E+03,0.301676E+03,0.304329E+03,0.307006E+03, & - 0.309706E+03,0.312423E+03,0.315165E+03,0.317930E+03,0.320705E+03, & - 0.323519E+03,0.326350E+03,0.329199E+03,0.332073E+03,0.334973E+03, & - 0.337897E+03,0.340839E+03,0.343800E+03,0.346794E+03,0.349806E+03, & - 0.352845E+03,0.355918E+03,0.358994E+03,0.362112E+03,0.365242E+03, & - 0.368407E+03,0.371599E+03,0.374802E+03,0.378042E+03,0.381293E+03, & - 0.384588E+03,0.387904E+03,0.391239E+03,0.394604E+03,0.397988E+03, & - 0.401411E+03,0.404862E+03,0.408326E+03,0.411829E+03,0.415352E+03/ - data (ES(IES),IES=856,950) / & - 0.418906E+03,0.422490E+03,0.426095E+03,0.429740E+03,0.433398E+03, & - 0.437097E+03,0.440827E+03,0.444570E+03,0.448354E+03,0.452160E+03, & - 0.455999E+03,0.459870E+03,0.463765E+03,0.467702E+03,0.471652E+03, & - 0.475646E+03,0.479674E+03,0.483715E+03,0.487811E+03,0.491911E+03, & - 0.496065E+03,0.500244E+03,0.504448E+03,0.508698E+03,0.512961E+03, & - 0.517282E+03,0.521617E+03,0.525989E+03,0.530397E+03,0.534831E+03, & - 0.539313E+03,0.543821E+03,0.548355E+03,0.552938E+03,0.557549E+03, & - 0.562197E+03,0.566884E+03,0.571598E+03,0.576351E+03,0.581131E+03, & - 0.585963E+03,0.590835E+03,0.595722E+03,0.600663E+03,0.605631E+03, & - 0.610641E+03,0.615151E+03,0.619625E+03,0.624140E+03,0.628671E+03, & - 0.633243E+03,0.637845E+03,0.642465E+03,0.647126E+03,0.651806E+03, & - 0.656527E+03,0.661279E+03,0.666049E+03,0.670861E+03,0.675692E+03, & - 0.680566E+03,0.685471E+03,0.690396E+03,0.695363E+03,0.700350E+03, & - 0.705381E+03,0.710444E+03,0.715527E+03,0.720654E+03,0.725801E+03, & - 0.730994E+03,0.736219E+03,0.741465E+03,0.746756E+03,0.752068E+03, & - 0.757426E+03,0.762819E+03,0.768231E+03,0.773692E+03,0.779172E+03, & - 0.784701E+03,0.790265E+03,0.795849E+03,0.801483E+03,0.807137E+03, & - 0.812842E+03,0.818582E+03,0.824343E+03,0.830153E+03,0.835987E+03, & - 0.841871E+03,0.847791E+03,0.853733E+03,0.859727E+03,0.865743E+03/ - data (ES(IES),IES=951,1045) / & - 0.871812E+03,0.877918E+03,0.884046E+03,0.890228E+03,0.896433E+03, & - 0.902690E+03,0.908987E+03,0.915307E+03,0.921681E+03,0.928078E+03, & - 0.934531E+03,0.941023E+03,0.947539E+03,0.954112E+03,0.960708E+03, & - 0.967361E+03,0.974053E+03,0.980771E+03,0.987545E+03,0.994345E+03, & - 0.100120E+04,0.100810E+04,0.101502E+04,0.102201E+04,0.102902E+04, & - 0.103608E+04,0.104320E+04,0.105033E+04,0.105753E+04,0.106475E+04, & - 0.107204E+04,0.107936E+04,0.108672E+04,0.109414E+04,0.110158E+04, & - 0.110908E+04,0.111663E+04,0.112421E+04,0.113185E+04,0.113952E+04, & - 0.114725E+04,0.115503E+04,0.116284E+04,0.117071E+04,0.117861E+04, & - 0.118658E+04,0.119459E+04,0.120264E+04,0.121074E+04,0.121888E+04, & - 0.122709E+04,0.123534E+04,0.124362E+04,0.125198E+04,0.126036E+04, & - 0.126881E+04,0.127731E+04,0.128584E+04,0.129444E+04,0.130307E+04, & - 0.131177E+04,0.132053E+04,0.132931E+04,0.133817E+04,0.134705E+04, & - 0.135602E+04,0.136503E+04,0.137407E+04,0.138319E+04,0.139234E+04, & - 0.140156E+04,0.141084E+04,0.142015E+04,0.142954E+04,0.143896E+04, & - 0.144845E+04,0.145800E+04,0.146759E+04,0.147725E+04,0.148694E+04, & - 0.149672E+04,0.150655E+04,0.151641E+04,0.152635E+04,0.153633E+04, & - 0.154639E+04,0.155650E+04,0.156665E+04,0.157688E+04,0.158715E+04, & - 0.159750E+04,0.160791E+04,0.161836E+04,0.162888E+04,0.163945E+04/ - data (ES(IES),IES=1046,1140) / & - 0.165010E+04,0.166081E+04,0.167155E+04,0.168238E+04,0.169325E+04, & - 0.170420E+04,0.171522E+04,0.172627E+04,0.173741E+04,0.174859E+04, & - 0.175986E+04,0.177119E+04,0.178256E+04,0.179402E+04,0.180552E+04, & - 0.181711E+04,0.182877E+04,0.184046E+04,0.185224E+04,0.186407E+04, & - 0.187599E+04,0.188797E+04,0.190000E+04,0.191212E+04,0.192428E+04, & - 0.193653E+04,0.194886E+04,0.196122E+04,0.197368E+04,0.198618E+04, & - 0.199878E+04,0.201145E+04,0.202416E+04,0.203698E+04,0.204983E+04, & - 0.206278E+04,0.207580E+04,0.208887E+04,0.210204E+04,0.211525E+04, & - 0.212856E+04,0.214195E+04,0.215538E+04,0.216892E+04,0.218249E+04, & - 0.219618E+04,0.220994E+04,0.222375E+04,0.223766E+04,0.225161E+04, & - 0.226567E+04,0.227981E+04,0.229399E+04,0.230829E+04,0.232263E+04, & - 0.233708E+04,0.235161E+04,0.236618E+04,0.238087E+04,0.239560E+04, & - 0.241044E+04,0.242538E+04,0.244035E+04,0.245544E+04,0.247057E+04, & - 0.248583E+04,0.250116E+04,0.251654E+04,0.253204E+04,0.254759E+04, & - 0.256325E+04,0.257901E+04,0.259480E+04,0.261073E+04,0.262670E+04, & - 0.264279E+04,0.265896E+04,0.267519E+04,0.269154E+04,0.270794E+04, & - 0.272447E+04,0.274108E+04,0.275774E+04,0.277453E+04,0.279137E+04, & - 0.280834E+04,0.282540E+04,0.284251E+04,0.285975E+04,0.287704E+04, & - 0.289446E+04,0.291198E+04,0.292954E+04,0.294725E+04,0.296499E+04/ - data (ES(IES),IES=1141,1235) / & - 0.298288E+04,0.300087E+04,0.301890E+04,0.303707E+04,0.305529E+04, & - 0.307365E+04,0.309211E+04,0.311062E+04,0.312927E+04,0.314798E+04, & - 0.316682E+04,0.318577E+04,0.320477E+04,0.322391E+04,0.324310E+04, & - 0.326245E+04,0.328189E+04,0.330138E+04,0.332103E+04,0.334073E+04, & - 0.336058E+04,0.338053E+04,0.340054E+04,0.342069E+04,0.344090E+04, & - 0.346127E+04,0.348174E+04,0.350227E+04,0.352295E+04,0.354369E+04, & - 0.356458E+04,0.358559E+04,0.360664E+04,0.362787E+04,0.364914E+04, & - 0.367058E+04,0.369212E+04,0.371373E+04,0.373548E+04,0.375731E+04, & - 0.377929E+04,0.380139E+04,0.382355E+04,0.384588E+04,0.386826E+04, & - 0.389081E+04,0.391348E+04,0.393620E+04,0.395910E+04,0.398205E+04, & - 0.400518E+04,0.402843E+04,0.405173E+04,0.407520E+04,0.409875E+04, & - 0.412246E+04,0.414630E+04,0.417019E+04,0.419427E+04,0.421840E+04, & - 0.424272E+04,0.426715E+04,0.429165E+04,0.431634E+04,0.434108E+04, & - 0.436602E+04,0.439107E+04,0.441618E+04,0.444149E+04,0.446685E+04, & - 0.449241E+04,0.451810E+04,0.454385E+04,0.456977E+04,0.459578E+04, & - 0.462197E+04,0.464830E+04,0.467468E+04,0.470127E+04,0.472792E+04, & - 0.475477E+04,0.478175E+04,0.480880E+04,0.483605E+04,0.486336E+04, & - 0.489087E+04,0.491853E+04,0.494623E+04,0.497415E+04,0.500215E+04, & - 0.503034E+04,0.505867E+04,0.508707E+04,0.511568E+04,0.514436E+04/ - data (ES(IES),IES=1236,1330) / & - 0.517325E+04,0.520227E+04,0.523137E+04,0.526068E+04,0.529005E+04, & - 0.531965E+04,0.534939E+04,0.537921E+04,0.540923E+04,0.543932E+04, & - 0.546965E+04,0.550011E+04,0.553064E+04,0.556139E+04,0.559223E+04, & - 0.562329E+04,0.565449E+04,0.568577E+04,0.571727E+04,0.574884E+04, & - 0.578064E+04,0.581261E+04,0.584464E+04,0.587692E+04,0.590924E+04, & - 0.594182E+04,0.597455E+04,0.600736E+04,0.604039E+04,0.607350E+04, & - 0.610685E+04,0.614036E+04,0.617394E+04,0.620777E+04,0.624169E+04, & - 0.627584E+04,0.631014E+04,0.634454E+04,0.637918E+04,0.641390E+04, & - 0.644887E+04,0.648400E+04,0.651919E+04,0.655467E+04,0.659021E+04, & - 0.662599E+04,0.666197E+04,0.669800E+04,0.673429E+04,0.677069E+04, & - 0.680735E+04,0.684415E+04,0.688104E+04,0.691819E+04,0.695543E+04, & - 0.699292E+04,0.703061E+04,0.706837E+04,0.710639E+04,0.714451E+04, & - 0.718289E+04,0.722143E+04,0.726009E+04,0.729903E+04,0.733802E+04, & - 0.737729E+04,0.741676E+04,0.745631E+04,0.749612E+04,0.753602E+04, & - 0.757622E+04,0.761659E+04,0.765705E+04,0.769780E+04,0.773863E+04, & - 0.777975E+04,0.782106E+04,0.786246E+04,0.790412E+04,0.794593E+04, & - 0.798802E+04,0.803028E+04,0.807259E+04,0.811525E+04,0.815798E+04, & - 0.820102E+04,0.824427E+04,0.828757E+04,0.833120E+04,0.837493E+04, & - 0.841895E+04,0.846313E+04,0.850744E+04,0.855208E+04,0.859678E+04/ - data (ES(IES),IES=1331,1425) / & - 0.864179E+04,0.868705E+04,0.873237E+04,0.877800E+04,0.882374E+04, & - 0.886979E+04,0.891603E+04,0.896237E+04,0.900904E+04,0.905579E+04, & - 0.910288E+04,0.915018E+04,0.919758E+04,0.924529E+04,0.929310E+04, & - 0.934122E+04,0.938959E+04,0.943804E+04,0.948687E+04,0.953575E+04, & - 0.958494E+04,0.963442E+04,0.968395E+04,0.973384E+04,0.978383E+04, & - 0.983412E+04,0.988468E+04,0.993534E+04,0.998630E+04,0.100374E+05, & - 0.100888E+05,0.101406E+05,0.101923E+05,0.102444E+05,0.102966E+05, & - 0.103492E+05,0.104020E+05,0.104550E+05,0.105082E+05,0.105616E+05, & - 0.106153E+05,0.106693E+05,0.107234E+05,0.107779E+05,0.108325E+05, & - 0.108874E+05,0.109425E+05,0.109978E+05,0.110535E+05,0.111092E+05, & - 0.111653E+05,0.112217E+05,0.112782E+05,0.113350E+05,0.113920E+05, & - 0.114493E+05,0.115070E+05,0.115646E+05,0.116228E+05,0.116809E+05, & - 0.117396E+05,0.117984E+05,0.118574E+05,0.119167E+05,0.119762E+05, & - 0.120360E+05,0.120962E+05,0.121564E+05,0.122170E+05,0.122778E+05, & - 0.123389E+05,0.124004E+05,0.124619E+05,0.125238E+05,0.125859E+05, & - 0.126484E+05,0.127111E+05,0.127739E+05,0.128372E+05,0.129006E+05, & - 0.129644E+05,0.130285E+05,0.130927E+05,0.131573E+05,0.132220E+05, & - 0.132872E+05,0.133526E+05,0.134182E+05,0.134842E+05,0.135503E+05, & - 0.136168E+05,0.136836E+05,0.137505E+05,0.138180E+05,0.138854E+05/ - data (ES(IES),IES=1426,1520) / & - 0.139534E+05,0.140216E+05,0.140900E+05,0.141588E+05,0.142277E+05, & - 0.142971E+05,0.143668E+05,0.144366E+05,0.145069E+05,0.145773E+05, & - 0.146481E+05,0.147192E+05,0.147905E+05,0.148622E+05,0.149341E+05, & - 0.150064E+05,0.150790E+05,0.151517E+05,0.152250E+05,0.152983E+05, & - 0.153721E+05,0.154462E+05,0.155205E+05,0.155952E+05,0.156701E+05, & - 0.157454E+05,0.158211E+05,0.158969E+05,0.159732E+05,0.160496E+05, & - 0.161265E+05,0.162037E+05,0.162811E+05,0.163589E+05,0.164369E+05, & - 0.165154E+05,0.165942E+05,0.166732E+05,0.167526E+05,0.168322E+05, & - 0.169123E+05,0.169927E+05,0.170733E+05,0.171543E+05,0.172356E+05, & - 0.173173E+05,0.173993E+05,0.174815E+05,0.175643E+05,0.176471E+05, & - 0.177305E+05,0.178143E+05,0.178981E+05,0.179826E+05,0.180671E+05, & - 0.181522E+05,0.182377E+05,0.183232E+05,0.184093E+05,0.184955E+05, & - 0.185823E+05,0.186695E+05,0.187568E+05,0.188447E+05,0.189326E+05, & - 0.190212E+05,0.191101E+05,0.191991E+05,0.192887E+05,0.193785E+05, & - 0.194688E+05,0.195595E+05,0.196503E+05,0.197417E+05,0.198332E+05, & - 0.199253E+05,0.200178E+05,0.201105E+05,0.202036E+05,0.202971E+05, & - 0.203910E+05,0.204853E+05,0.205798E+05,0.206749E+05,0.207701E+05, & - 0.208659E+05,0.209621E+05,0.210584E+05,0.211554E+05,0.212524E+05, & - 0.213501E+05,0.214482E+05,0.215465E+05,0.216452E+05,0.217442E+05/ - data (ES(IES),IES=1521,1552) / & - 0.218439E+05,0.219439E+05,0.220440E+05,0.221449E+05,0.222457E+05, & - 0.223473E+05,0.224494E+05,0.225514E+05,0.226542E+05,0.227571E+05, & - 0.228606E+05,0.229646E+05,0.230687E+05,0.231734E+05,0.232783E+05, & - 0.233839E+05,0.234898E+05,0.235960E+05,0.237027E+05,0.238097E+05, & - 0.239173E+05,0.240254E+05,0.241335E+05,0.242424E+05,0.243514E+05, & - 0.244611E+05,0.245712E+05,0.246814E+05,0.247923E+05,0.249034E+05, & - 0.250152E+05,0.250152E+05/ - - do I = 1, NPNTS - - ! Compute the factor that converts from sat vapour pressure in a - ! pure water system to sat vapour pressure in air, FSUBW. This formula - ! is taken from equation A4.7 of Adrian Gill's book: Atmosphere-Ocean - ! Dynamics. Note that his formula works in terms of pressure in MB and - ! temperature in Celsius, so conversion of units leads to the slightly - ! different equation used here. - - FSUBW = 1.0_kind_real + 1.0E-8_kind_real * P(I) * (4.5 + 6.0E-4 * (T(I) - ZERODEGC) * (T(I) - ZERODEGC)) - - ! Use the lookup table to find saturated vapour pressure, and stored it in QS. - - TT = max (T_LOW, T(I)) - TT = min (T_HIGH, TT) - - ATABLE = (TT - T_LOW + DELTA_T) / DELTA_T - ITABLE = ATABLE - ATABLE = ATABLE - ITABLE - - QS(I) = (1.0_kind_real - ATABLE) * ES(ITABLE) + ATABLE * ES(ITABLE + 1) - - ! Multiply by FSUBW to convert to saturated vapour pressure in air (equation A4.6 - ! of Adrian Gill's book) - - QS(I) = QS(I) * FSUBW - - ! Now form the accurate expression for QS, which is a rearranged - ! version of equation A4.3 of Gill's book. - - ! Note that at very low pressures we apply a fix, to prevent a - ! singularity (Qsat tends to 1.0 kg/kg). - - QS(I) = (EPSILON * QS(I)) / (max (P(I), QS(I)) - ONE_MINUS_EPSILON * QS(I)) + ! Note that at very low pressures we apply a fix, to prevent a singularity + ! (Qsat tends to 1.0 kg/kg) + + QS(I) = (epsilon * QS(I)) / (max (P(I), QS(I)) - one_minus_epsilon * QS(I)) + +end do + +end subroutine Ops_QsatWat + +!------------------------------------------------------------------------------- +!> Split the humidity into water vapour, liquid water and ice. +!! +!! \details Heritage: Ops_SatRad_Qsplit.f90 +!! +!! if output_type=1 : Split total water content (qtotal) into
+!! water vapor content (q) and
+!! cloud liquid water content (ql) and
+!! cloud ice water content (qi)
+!! +!! if output_type ne 1 : Compute derivatives: (q) =dq/dqtotal
+!! (ql)=dql/dqtotal
+!! (qi)=dqi/dqtotal
+!! +!! \warning The derivatives are not valid if LtemperatureVar=.true. since +!! qsaturated depends on temperature. +!! +!! The partitioning of the excess moisture between ice and clw uses a temperature +!! based parametrization based on aircraft data Ref: Jones DC Reading phdthesis +!! p126 +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine Ops_SatRad_Qsplit ( & + output_type, & + p, & + t, & + qtotal, & + q, & + ql, & + qi, & + UseQtSplitRain) + + implicit none + + ! Subroutine arguments: + integer, intent(in) :: output_type + real(kind_real), intent(in) :: p(:) + real(kind_real), intent(in) :: t(:) + real(kind_real), intent(in) :: qtotal(:) + real(kind_real), intent(out) :: q(size(qtotal)) ! humidity component q + real(kind_real), intent(out) :: ql(size(qtotal)) ! liquid component ql + real(kind_real), intent(out) :: qi(size(qtotal)) ! ice component qi + logical, intent(in) :: UseQtSplitRain + + ! Local declarations: + integer :: i + real(kind_real), parameter :: lower_rh = 0.95_kind_real + real(kind_real), parameter :: upper_rh = 1.05_kind_real + real(kind_real), parameter :: Split_Factor = half + real(kind_real), parameter :: MinTempQl = 233.15_kind_real ! temperature (K) below which all cloud is ice + real(kind_real) :: qsaturated(size(qtotal)) + real(kind_real) :: RH_qtotal(size(qtotal)) + real(kind_real) :: qnv(size(qtotal)) ! non vapour component + real(kind_real) :: qc(size(qtotal)) ! cloud component + real(kind_real) :: V1(size(qtotal)) + real(kind_real) :: V2(size(qtotal)) + real(kind_real) :: V1zero + real(kind_real) :: V2zero + real(kind_real) :: W(size(qtotal)) + real(kind_real) :: Y1,Y2,Y3,Y4 + real(kind_real) :: IntConst + real(kind_real) :: Aconst + real(kind_real) :: Bconst + real(kind_real) :: Cconst + real(kind_real) :: Dconst + real(kind_real) :: Denom + real(kind_real) :: SmallValue + real(kind_real) :: LF(size(qtotal)) ! fraction of ql to ql+qi + character(len=*), parameter :: RoutineName = "Ops_SatRad_Qsplit" + + real(kind_real) :: QsplitRainParamA !Parameters used to define proportion of + real(kind_real) :: QsplitRainParamB !qt that is partitioned into a rain compnenent + real(kind_real) :: QsplitRainParamC ! + + !Qsplit_MixPhaseParam = 1 !Jones' method as default + QsplitRainParamA = 0.15_kind_real + QsplitRainParamB = 0.09_kind_real + QsplitRainParamC = 50.0_kind_real + + ! Compute saturated water vapor profile for nlevels_q only + + call Ops_Qsat (qsaturated(:), & ! out + t(:), & ! in + p(:), & ! in + size(qtotal)) ! in + + SmallValue = one / 8.5_kind_real + Denom = SmallValue * (upper_rh - lower_rh) + + ! don't let rh exceed 2.0 to avoid cosh function blowing up + RH_qtotal(:) = min (qtotal(:) / qsaturated(:), two) + + V1(:) = (RH_qtotal(:) - lower_rh) / Denom + V2(:) = (RH_qtotal(:) - upper_rh) / Denom + + Y1 = one + Y2 = Split_Factor + Y3 = zero + Y4 = Split_Factor + + Aconst = (Y2 - Y1) / two + Bconst = -(Y4 - Y3) / two + Cconst = (Y2 + Y1) / two + Dconst = -(Y4 + Y3) / two + + ! Compute fraction of ql to ql+qi based on temperature profile + + where (t(:) - ZeroDegC >= -0.01_kind_real) ! -0.01degc and above + + ! all ql + LF(:) = one + + end where + + where (t(:) <= MinTempql) + + ! all qi + LF(:) = zero + + end where + + where (t(:) > MinTempql .and. t(:) - ZeroDegC < -0.01_kind_real) + + ! Jones' parametrization + LF(:) = sqrt (-1.0_kind_real * log (-0.025_kind_real * (t(:) - ZeroDegC)) / 70.0_kind_real) + + end where + + ! finally set LF to 0.0_kind_real for the rttov levels on which clw jacobians are not + ! calculated since nlevels_mwclw < nlevels_q + + V1zero = -1.0_kind_real * lower_rh / Denom + V2zero = -1.0_kind_real * upper_rh / Denom + IntConst = -(Aconst * Denom * log (cosh (V1zero)) + Bconst * Denom * log (cosh (V2zero))) + W(:) = Aconst * Denom * log (cosh (V1(:))) + Bconst * Denom * log (cosh (V2(:))) + & + (Cconst + Dconst) * RH_qtotal(:) + IntConst + + ! store the components of qtotal + ! ensuring that they are above lower limits + + if (UseQtsplitRain) then + + ! Split qtotal into q and qnv (non-vapour part - includes + ! ql, qi, qr) + + ! Split qtotal into q, ql ,qi + + do i = 1, size(qtotal) + q(i) = max (W(i) * qsaturated(i), min_q) + qnv(i) = max (qtotal(i) - q(i), zero) + + ! Split qnv into a cloud and precipitation part + + qc(i) = max (QsplitRainParamA * (QsplitRainParamB - (QsplitRainParamB / ((QsplitRainParamC * qnv(i)) + 1))), zero) + + ! Finally split non-precip part into liquid and ice + + ql(i) = max (LF(i) * qc(i), zero) + qi(i) = max ((one - LF(i)) * (qc(i)), zero) + + end do + + else + do i = 1, size(qtotal) + + q(i) = max (W(i) * qsaturated(i), min_q) + ql(i) = max (LF(i) * (qtotal(i) - q(i)), zero) + qi(i) = max ((one - LF(i)) * (qtotal(i) - q(i)), zero) + + end do + + end if + + ! Values of q, ql and qi are overwritten if output_type /= 1 + ! and replaced with the derivatives + + if (output_type /= 1) then + + ! Compute derivates + ! q = dq/dqtotal, ql = dql/dqtotal, qi=dqi/dqtotal + + q(:) = Aconst * tanh (V1(:)) + Cconst + Bconst * tanh (V2(:)) + Dconst + + if (UseQtsplitRain) then + + ql(:) = LF(:) * QsplitRainParamA * QsplitRainParamB * QsplitRainParamC * (one - q(:)) / & + ((QsplitRainParamC * qnv(:)) + one) ** 2 + qi(:) = (one - LF(:)) * QsplitRainParamA * QsplitRainParamB * QsplitRainParamC * (one - q(:)) / & + ((QsplitRainParamC * qnv(:)) + one) ** 2 + + else + + ql(:) = LF(:) * (one - q(:)) + qi(:) = (one - LF(:)) * (one - q(:)) + + end if + + end if + +end subroutine Ops_SatRad_Qsplit + +!------------------------------------------------------------------------------- +!> Do cholesky decomposition +!! +!! \details Heritage: Ops_Cholesky.inc +!! +!! Solves the Linear equation UQ=V for Q where U is a symmetric positive definite +!! matrix and U and Q are vectors of length N. The method follows that in Golub +!! and Van Loan although this is pretty standard. +!! +!! if U is not positive definite this will be detected by the program and flagged +!! as an error. U is assumed to be symmetric as only the upper triangle is in +!! fact used. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +subroutine Ops_Cholesky (U, & + V, & + N, & + Q, & + ErrorCode) + +implicit none + +! subroutine arguments: +integer, intent(in) :: n +real(kind_real), intent(in) :: U(n,n) +real(kind_real), intent(in) :: V(n) +real(kind_real), intent(out) :: Q(n) +integer, intent(out) :: ErrorCode + +! Local declarations: +character(len=*), parameter :: RoutineName = "Ops_Cholesky" +real(kind_real), parameter :: Tolerance = tiny (zero) * 100.0_kind_real +character(len=50) :: ErrorMessage +integer :: j +integer :: k +real(kind_real) :: G(n,n) ! The Cholesky Triangle Matrix +real(kind_real) :: X(n) ! Temporary array used in calculating G + +ErrorCode = 0 + +! Determine the Cholesky triangle matrix. + +do j = 1, n + X(j:n) = U(j:n,j) + if (j /= 1) then + do k = 1, j - 1 + X(j:n) = X(j:n) - G(j,k) * G(j:n,k) + end do + end if + if (X(j) <= Tolerance) then + ErrorCode = 1 + Errormessage = ' :U matrix is not positive definite' + write(*,*) trim(RoutineName), trim(ErrorMessage) + goto 9999 + end if + G(J:N,J) = X(J:N) / sqrt (X(J)) +end do + +! Solve Gx=v for x by forward substitution + +X = V +X(1) = X(1) / G(1,1) +do j = 2, n + X(j) = (X(j) - dot_product(G(j,1:j - 1), X(1:j - 1))) / G(j,j) +end do + +! Solve G^T.q=x for q by backward substitution + +Q = x +Q(n) = Q(n) / G(n,n) +do j = n - 1, 1, -1 + Q(j) = (Q(j) - dot_product(G(j + 1:n,j), Q(j + 1:n))) / G(j,j) +end do + +9999 continue + +end subroutine Ops_Cholesky + +!------------------------------------------------------------------------------- +!> Find a free file unit. +!! +!! \author Met Office +!! +!! \date 09/06/2020: Created +!! +function ufo_utils_iogetfreeunit() result(unit) + +implicit none + +integer :: unit + +integer, parameter :: unit_min=10 +integer, parameter :: unit_max=1000 +logical :: opened +integer :: lun +integer :: newunit + +newunit=-1 +do lun=unit_min,unit_max + inquire(unit=lun,opened=opened) + if (.not. opened) then + newunit=lun + exit + end if +end do +unit=newunit + +end function ufo_utils_iogetfreeunit + +!------------------------------------------------------------------------------- +! Invert a matrix. +! +! Variables with intent in: +! +! N: Size of the matrix being inverted +! M: If MATRIX is not present this is the same as N, else this is the other +! dimension of MATRIX. +! +! Variables with intent inout: +! +! A: Real matrix (assumed square and symmetrical) overwritten by its +! inverse if MATRIX is not present. +! +! Variables with intent out: +! +! ExitCode: 0: ok, 1: A is not positive definite. +! +! Optional variables with intent inout: +! +! Matrix: If present this input matrix is replaced by (Matrix).A^-1 on exit +! (leaving A unchanged). +! +! Uses Cholesky decomposition - a method particularly suitable for real +! symmetric matrices. Cholesky decomposition solves the Linear equation UQ=V +! for Q where U is a symmetric positive definite matrix and U and Q are vectors +! of length N. The method follows that in Golub and Van Loan although this is +! pretty standard. If U is not positive definite this will be detected by the +! program and flagged as an error. U is assumed to be symmetric as only the +! upper triangle is in fact used. If the the optional parameter Matrix is +! present, it is replaced by (Matrix).A^-1 on exit and A is left unchanged. +!------------------------------------------------------------------------------- + +subroutine InvertMatrix (n, & + m, & + a, & + status, & + matrix) + +implicit none + +! subroutine arguments: +integer, intent(in) :: n !< order of a +integer, intent(in) :: m !< order of optional matrix, if required +real(kind=kind_real), intent(inout) :: a(n,n) !< square mx, overwritten by its inverse +integer, intent(out) :: status !< 0 if all ok 1 if matrix is not positive definite +real(kind=kind_real), optional, intent(inout) :: matrix(n,m) !< replaced by (matrix).a^-1 on exit + +! local declarations: +character(len=*), parameter :: routinename = "InvertMatrix" +character(len=80) :: errormessage +real(kind=kind_real), parameter :: tolerance = tiny (zero) * 100.0_kind_real +integer :: i +integer :: j +integer :: k +integer :: mm +real(kind=kind_real) :: g(n,n) ! the cholesky triangle matrix +real(kind=kind_real) :: q(n) +real(kind=kind_real) :: tmp(n,m) +real(kind=kind_real) :: v(n) +real(kind=kind_real) :: x(n) ! temporary array + +status = 0 + +! determine the cholesky triangle matrix. + +do j = 1, n + x(j:n) = a(j:n,j) + if (j /= 1) then + do k = 1, j - 1 + x(j:n) = x(j:n) - g(j,k) * g(j:n,k) end do + end if + if (x(j) <= tolerance) then + errormessage = 'Matrix is not positive definite' + call fckit_log % warning(errormessage) + status = 1 + goto 9999 + end if + g(j:n,j) = x(j:n) / sqrt (x(j)) +end do + +! now solve the equation g.g^t.q=v for the set of +! vectors, v, with one element = 1 and the rest zero. +! the solutions q are brought together at the end to form +! the inverse of g.g^t (i.e., the inverse of a). + +if (present (matrix)) then + mm = m +else + ! make sure that the dimensions of tmp were correctly specified + if (m /= n) then + errormessage = '2nd and 3rd arguments of routine must be' + call fckit_log % warning(errormessage) + errormessage = 'identical if the matrix option is not present' + call fckit_log % warning(errormessage) + status = 2 + goto 9999 + end if + mm = n +end if + +main_loop : do i = 1, mm + if (.not. (present (matrix))) then + v(:) = zero + v(i) = one + else + v(:) = matrix(i,:) + end if + + ! solve gx=v for x by forward substitution + + x = v + x(1) = x(1) / g(1,1) + do j = 2, n + x(j) = (x(j) - dot_product (g(j,1:j - 1), x(1:j - 1))) / g(j,j) + end do + + ! solve g^t.q=x for q by backward substitution + + q = x + q(n) = q(n) / g(n,n) + do j = n - 1, 1, -1 + q(j) = (q(j) - dot_product (g(j + 1:n,j), q(j + 1:n))) / g(j,j) + end do + + tmp(:,i) = q(:) + +end do main_loop + +if (.not. (present (matrix))) then + a(:,:) = tmp(:,:) +else + matrix(:,:) = tmp(:,:) +end if + +9999 continue + +end subroutine InvertMatrix + +!------------------------------------------------------------------------------- +! This function will comapre two strings while avoiding trim in order to speed +! up the string comparison. This will allow for trailing blanks on either string +! to count for a match. +! +! Since the strings may be different lengths coming in (due to trailing blanks) +! a helper function (cmp_ordered_strings) is called with the shorter string first +! which helps simplify the logic of the comparison. + +function cmp_strings(str1, str2) + implicit none + + logical :: cmp_strings + character(len=*) :: str1 + character(len=*) :: str2 + + if (len(str1) <= len(str2)) then + cmp_strings = cmp_ordered_strings(str1, str2) + else + cmp_strings = cmp_ordered_strings(str2, str1) + endif + + return +end function cmp_strings + +!------------------------------------------------------------------------------- +! This is a helper function for the public cmp_strings function. It is intended +! for this function to be called with the arguments of cmp_strings, with the shorter +! of the two strings in the first argument. +! +! The algorithm is to compare each character one by one between each string. Then if +! one string is longer, continue looking at that string an make sure the remainder +! consists of blanks before calling it a match between the strings. Knowing that the +! first argument is the shorter of the two stings helps simplify the code doing the check. +! +! The trim call is avoided since it allocates, copies strings and deallocates which +! collectively are operations that are too expensive. + +function cmp_ordered_strings(shorter_str, longer_str) + implicit none + + logical :: cmp_ordered_strings + character(len=*) :: shorter_str + character(len=*) :: longer_str + + integer :: i + integer :: j + + ! Check each character from the beginning of the strings to the length of the + ! shorter string. Exit the loop right away if a mis-match occurs to save time. + cmp_ordered_strings = .true. + do i = 1, len(shorter_str) + if (shorter_str(i:i) /= longer_str(i:i)) then + cmp_ordered_strings = .false. + exit + endif + enddo + + ! If cmp_ordered_strings is false we can return immediately. If true, then we need to check + ! that the remainder of the longer string constists of all blank spaces before + ! declaring the strings equal. + if (cmp_ordered_strings) then + do j = i, len(longer_str) + if (longer_str(j:j) /= " ") then + cmp_ordered_strings = .false. + exit + endif + enddo + endif + + return +end function cmp_ordered_strings - end subroutine Ops_Qsat +!------------------------------------------------------------------------------- end module ufo_utils_mod diff --git a/src/ufo/variabletransforms/CMakeLists.txt b/src/ufo/variabletransforms/CMakeLists.txt new file mode 100644 index 000000000..5dcd8b6a5 --- /dev/null +++ b/src/ufo/variabletransforms/CMakeLists.txt @@ -0,0 +1,24 @@ +# (C) Copyright 2017-2018 Met Office. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +set ( variabletransforms_files + Cal_PressureFromHeight.h + Cal_PressureFromHeight.cc + Cal_Humidity.h + Cal_Humidity.cc + Cal_Wind.h + Cal_Wind.cc + TransformBase.cc + TransformBase.h + Formulas.cc + Formulas.h +) + +PREPEND( _p_variabletransforms_files "variabletransforms" ${variabletransforms_files} ) + +set ( variabletransforms_src_files + ${_p_variabletransforms_files} + PARENT_SCOPE +) diff --git a/src/ufo/variabletransforms/Cal_Humidity.cc b/src/ufo/variabletransforms/Cal_Humidity.cc new file mode 100644 index 000000000..08e45dde3 --- /dev/null +++ b/src/ufo/variabletransforms/Cal_Humidity.cc @@ -0,0 +1,192 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/filters/ProfileConsistencyCheckParameters.h" +#include "ufo/utils/Constants.h" +#include "ufo/variabletransforms/Cal_Humidity.h" + +namespace ufo { +/************************************************************************************/ +// Cal_RelativeHumidity +/************************************************************************************/ + +static TransformMaker + makerCal_RelativeHumidity_("RelativeHumidity"); + +Cal_RelativeHumidity::Cal_RelativeHumidity( + const VariableTransformsParameters &options, ioda::ObsSpace &os, + const std::shared_ptr> &flags) + : TransformBase(options, os, flags) {} + +/************************************************************************************/ + +void Cal_RelativeHumidity::runTransform() { + oops::Log::trace() << " --> Retrieve Relative humidity" + << std::endl; + oops::Log::trace() << " --> method: " << method() << std::endl; + oops::Log::trace() << " --> formulation: " << formulation() << std::endl; + oops::Log::trace() << " --> obsName: " << obsName() << std::endl; + + // Get the right method + switch (method()) { + case formulas::MethodFormulation::NCAR: + case formulas::MethodFormulation::NOAA: + case formulas::MethodFormulation::UKMO: + default: { + methodDEFAULT(); + break; + } + } +} + +/************************************************************************************/ + +void Cal_RelativeHumidity::methodDEFAULT() { + const size_t nlocs = obsdb_.nlocs(); + + float esat, qvs, qv, satVaporPres; + + std::vector specificHumidity; + std::vector airTemperature; + std::vector pressure; + std::vector relativeHumidity(nlocs); + + getObservation("ObsValue", "specific_humidity", + specificHumidity, true); + getObservation("ObsValue", "air_temperature", + airTemperature, true); + getObservation("MetaData", "air_pressure", + pressure, false); + if (pressure.empty()) { + getObservation("ObsValue", "surface_pressure", + pressure, true); + } + + if (!oops::allVectorsSameNonZeroSize(specificHumidity, airTemperature, pressure)) { + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(specificHumidity, airTemperature, + pressure) + << std::endl; + throw eckit::BadValue("At least one vector is the wrong size or empty out of " + "specific_humidity, air_temperature and pressure", Here()); + } + + // Initialise this vector with missing value + relativeHumidity.assign(nlocs, missingValueFloat); + + // Loop over all obs + for (size_t jobs = 0; jobs < nlocs; ++jobs) { + if (specificHumidity[jobs] != missingValueFloat && + airTemperature[jobs] != missingValueFloat && pressure[jobs] != missingValueFloat) { + // Calculate saturation vapor pressure from temperature according to requested formulation + // Double-check result is always lower than 15% of incoming pressure. + satVaporPres = formulas::SatVaporPres_fromTemp(airTemperature[jobs], formulation()); + esat = std::min(pressure[jobs]*0.15f, satVaporPres); + + // Convert sat. vapor pressure to sat water vapor mixing ratio + qvs = 0.622 * esat/(pressure[jobs]-esat); + + // Convert specific humidity to water vapor mixing ratio + qv = std::max(1.0e-12f, specificHumidity[jobs]/(1.0f-specificHumidity[jobs])); + + // Final RH (which can be greater than 100%) is q/qsat, but set sensible lowest limit + relativeHumidity[jobs] = std::max(1.0e-6f, qv/qvs); + } + } + + obsdb_.put_db("DerivedValue", "relative_humidity", relativeHumidity); +} + +/************************************************************************************/ +// Cal_SpecificHumidity +/************************************************************************************/ +static TransformMaker + makerCal_SpecificHumidity_("SpecificHumidity"); + +Cal_SpecificHumidity::Cal_SpecificHumidity( + const VariableTransformsParameters &options, ioda::ObsSpace &os, + const std::shared_ptr> &flags) + : TransformBase(options, os, flags) {} + +/************************************************************************************/ + +void Cal_SpecificHumidity::runTransform() { + oops::Log::trace() << " Retrieve Specific Humidity" << std::endl; + oops::Log::trace() << " --> method: " << method() << std::endl; + oops::Log::trace() << " --> formulation: " << formulation() << std::endl; + oops::Log::trace() << " --> obsName: " << obsName() << std::endl; + + // Get the right method + switch (method()) { + case formulas::MethodFormulation::NCAR: + case formulas::MethodFormulation::NOAA: + case formulas::MethodFormulation::UKMO: + default: { + methodDEFAULT(); + break; + } + } +} +/************************************************************************************/ + +void Cal_SpecificHumidity::methodDEFAULT() { + const size_t nlocs = obsdb_.nlocs(); + float esat, qvs, qv, satVaporPres; + std::vector relativeHumidity; + std::vector airTemperature; + std::vector pressure; + std::vector specificHumidity(nlocs); + + getObservation("ObsValue", "relative_humidity", + relativeHumidity, true); + getObservation("ObsValue", "air_temperature", + airTemperature, true); + getObservation("MetaData", "air_pressure", + pressure, false); + if (pressure.empty()) { + getObservation("ObsValue", "surface_pressure", + pressure, true); + } + + if (!oops::allVectorsSameNonZeroSize(relativeHumidity, airTemperature, pressure)) { + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(relativeHumidity, airTemperature, + pressure) + << std::endl; + throw eckit::BadValue("At least one vector is the wrong size or empty out of " + "relative_humidity, air_temperature and pressure", Here()); + } + + // Initialise this vector with missing value + specificHumidity.assign(nlocs, missingValueFloat); + + // Loop over all obs + for (size_t jobs = 0; jobs < nlocs; ++jobs) { + if (relativeHumidity[jobs] != missingValueFloat && + airTemperature[jobs] != missingValueFloat && pressure[jobs] != missingValueFloat) { + // Calculate saturation vapor pressure from temperature according to requested formulation + // Double-check result is always lower than 15% of incoming pressure. + satVaporPres = formulas::SatVaporPres_fromTemp(airTemperature[jobs], formulation()); + esat = std::min(pressure[jobs]*0.15f, satVaporPres); + + // Convert sat. vapor pressure to sat water vapor mixing ratio + qvs = 0.622 * esat/(pressure[jobs]-esat); + + // Using RH, calculate water vapor mixing ratio + qv = std::max(1.0e-12f, relativeHumidity[jobs]*qvs); + + // Final RH (which can be greater than 100%) is q/qsat, but set sensible lowest limit + specificHumidity[jobs] = std::max(1.0e-12f, qv/(1.0f+qv)); + } + } + obsdb_.put_db("DerivedValue", "specific_humidity", specificHumidity); +} + + + +} // namespace ufo + diff --git a/src/ufo/variabletransforms/Cal_Humidity.h b/src/ufo/variabletransforms/Cal_Humidity.h new file mode 100644 index 000000000..b04ee1357 --- /dev/null +++ b/src/ufo/variabletransforms/Cal_Humidity.h @@ -0,0 +1,88 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_VARIABLETRANSFORMS_CAL_HUMIDITY_H_ +#define UFO_VARIABLETRANSFORMS_CAL_HUMIDITY_H_ + +#include +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "ufo/variabletransforms/TransformBase.h" + +namespace ufo { + +/*! +* \brief Relative Humidity filter +* +* Performs a variable conversion from specific_humidity, temperature, and +* pressure to relative humidity. The newly calculated variable is included in the same +* obs space. +* +* Example: +* +* \code{.yaml} +* obs filters: +* - filter: Variables Transform +* Transform: ["RelativeHumidity"] +* Method: UKMO # Using UKMO method and UKMO default formulation +* Formulation: Sonntag # Using Sonntag formulation +* \endcode +* +* See VariableTransformsParameters for filter setup. +*/ +class Cal_RelativeHumidity : public TransformBase { + public: + Cal_RelativeHumidity(const VariableTransformsParameters &options, + ioda::ObsSpace &os, + const std::shared_ptr> &flags); + // Run variable conversion + void runTransform() override; + + private: + // list of specific implementation(s) - This is controlled by "method" + void methodDEFAULT(); +}; + + +/*! +* \brief Specific Humidity filter +* +* Performs a variable conversion from relative_humidity, temperature, and +* pressure to specific humidity. The newly calculated variable is included in the same +* obs space. +* +* Example: +* +* \code{.yaml} +* obs filters: +* - filter: Variables Transform +* Transform: ["SpecificHumidity"] +* Method: UKMO # Using UKMO method and UKMO default formulation +* Formulation: Sonntag # Using Sonntag formulation +* \endcode +* +* See VariableTransformsParameters for filter setup. +*/ +class Cal_SpecificHumidity : public TransformBase { + public: + Cal_SpecificHumidity(const VariableTransformsParameters &options, + ioda::ObsSpace &os, + const std::shared_ptr> &flags); + // Run check + void runTransform() override; + + private: + // list of specific implementation(s) - This is controlled by "method" + void methodDEFAULT(); +}; +} // namespace ufo + +#endif // UFO_VARIABLETRANSFORMS_CAL_HUMIDITY_H_ diff --git a/src/ufo/variabletransforms/Cal_PressureFromHeight.cc b/src/ufo/variabletransforms/Cal_PressureFromHeight.cc new file mode 100644 index 000000000..690f9775b --- /dev/null +++ b/src/ufo/variabletransforms/Cal_PressureFromHeight.cc @@ -0,0 +1,326 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/variabletransforms/Cal_PressureFromHeight.h" +#include "ufo/filters/ProfileConsistencyCheckParameters.h" +#include "ufo/utils/Constants.h" + +namespace ufo { +/************************************************************************************/ +// Cal_PressureFromHeightForProfile +/************************************************************************************/ + +static TransformMaker + makerCal_PressureFromHeightForProfile_("PressureFromHeightForProfile"); + +Cal_PressureFromHeightForProfile::Cal_PressureFromHeightForProfile( + const VariableTransformsParameters &options, ioda::ObsSpace &os, + const std::shared_ptr> &flags) + : TransformBase(options, os, flags) {} + +/************************************************************************************/ + +void Cal_PressureFromHeightForProfile::runTransform() { + oops::Log::trace() << " --> Retrieve Pressure From Height (Profile)" + << std::endl; + oops::Log::trace() << " --> method: " << method() << std::endl; + oops::Log::trace() << " --> formulation: " << formulation() << std::endl; + oops::Log::trace() << " --> obsName: " << obsName() << std::endl; + + // Get the right method + switch (method()) { + case formulas::MethodFormulation::NCAR: + case formulas::MethodFormulation::NOAA: + case formulas::MethodFormulation::UKMO: + default: { + methodUKMO(); + break; + } + } +} + +/************************************************************************************/ + +void Cal_PressureFromHeightForProfile::methodUKMO() { + std::vector airTemperature; + std::vector airTemperatureSurface; + std::vector geopotentialHeight; + std::vector dewPointTemperature; + std::vector dewPointTemperatureSurface; + std::vector relativeHumidity; + std::vector relativeHumiditySurface; + std::vector pressureStation; + std::vector stationElevation; + std::vector airPressure; + + float Pvap = missingValueFloat; // Vapour pressure + float Zcurrent = missingValueFloat; // Current height value + float Tcurrent = missingValueFloat; // Current temperature value + float Pprev = missingValueFloat; // Previous pressure value [ps] + float Zprev = missingValueFloat; // Previous height value [m] + float Tprev = missingValueFloat; // Previous temperature value [k] + + bool hasBeenUpdated = false; + + const size_t nlocs_ = obsdb_.nlocs(); + ioda::ObsSpace::RecIdxIter irec; + + // return if no data + if (obsdb_.nlocs() == 0) { + return; + } + + // Here we can only use data that have not been QCed + // so making sure UseValidDataOnly_ is set to True + SetUseValidDataOnly(true); + + // 0. Innitialise the ouput array + // ------------------------------------------------------------------------------- + getObservation("MetaData", "air_pressure", + airPressure); + if (airPressure.empty()) { + airPressure = std::vector(nlocs_); + std::fill(airPressure.begin(), airPressure.end(), missingValueFloat); + } + + // 1. get the right variables + // ------------------------------------------------------------------------------- + // Compulsory meta-data + getObservation("MetaData", "station_elevation", + stationElevation, true); + // Compulsory surface observation + getObservation("ObsValue", "pressure_station", + pressureStation, true); + getObservation("ObsValue", "air_temperature_surface", + airTemperatureSurface, true); + + // Compulsory upper air observation + getObservation("ObsValue", "geopotential_height", + geopotentialHeight, true); + getObservation("ObsValue", "air_temperature", + airTemperature, true); + + // Here we have a choice between dew point temperature and relative humidity + // --> By default we chose dew point temperature first! + getObservation("ObsValue", "dew_point_temperature", + dewPointTemperature); + if (dewPointTemperature.empty()) { + // if we don't have dewpoint temperature, use relative humidity. + getObservation("ObsValue", "relative_humidity", + relativeHumidity, true); + getObservation("ObsValue", "relative_humidity_surface", + relativeHumiditySurface, true); + } else { + getObservation("ObsValue", "dew_point_temperature_surface", + dewPointTemperatureSurface, true); + } + + // 3. making sure we have what we need is here + // ------------------------------------------------------------------------------- + if (dewPointTemperature.empty()) { + if (!oops::allVectorsSameNonZeroSize(geopotentialHeight, airTemperature, relativeHumidity)) { + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(geopotentialHeight, airTemperature, + relativeHumidity) + << std::endl; + throw eckit::BadValue("At least one vector is the wrong size or empty out of " + "Z, T and Rh", Here()); + } + } else { + if (!oops::allVectorsSameNonZeroSize(geopotentialHeight, airTemperature, dewPointTemperature)) { + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(geopotentialHeight, airTemperature, + dewPointTemperature) + << std::endl; + throw eckit::BadValue("At least one vector is the wrong size or empty out of " + "Z, T and Td", Here()); + } + } + + // 4. Starting the calculation + // Loop over each record + // ------------------------------------------------------------------------------------- + for (irec = obsdb_.recidx_begin(); irec != obsdb_.recidx_end(); ++irec) { + const std::vector &rSort = obsdb_.recidx_vector(irec); + size_t ilocs = 0; + + // 4.1 Initialise for surface values + Pprev = pressureStation[rSort[ilocs]]; + Zprev = stationElevation[rSort[ilocs]]; + Tprev = airTemperatureSurface[rSort[ilocs]]; + + // Cycle if stationElevation or airTemperatureSurface or pressureStation is + // not valid + if (Zprev == missingValueFloat || Tprev == missingValueFloat || + Pprev == missingValueFloat) + continue; + + // Update Tprev + if (dewPointTemperature.empty()) { + // Update Tprev if Rh is valid + if (relativeHumidity[rSort[ilocs]] != missingValueFloat) { + Pvap = formulas::SatVaporPres_fromTemp(Tprev, formulation()); + Pvap = formulas::SatVaporPres_correction(Pvap, Tprev, formulation()); + Tprev = formulas::VirtualTemp_From_Rh_Psat_P_T( + relativeHumiditySurface[rSort[ilocs]], Pvap, Pprev, Tprev, formulation()); + } + + } else { + // Update Tprev if dew point positive + if (dewPointTemperature[rSort[ilocs]] != missingValueFloat) { + Pvap = formulas::SatVaporPres_fromTemp(dewPointTemperatureSurface[rSort[ilocs]], + formulation()); + Pvap = formulas::SatVaporPres_correction(Pvap, + dewPointTemperatureSurface[rSort[ilocs]], + formulation()); + Tprev = formulas::VirtualTemp_From_Psat_P_T(Pvap, Pprev, Tprev, formulation()); + } + } + + // 4.2 Loop over the length of the profile + for (ilocs = 0; ilocs < rSort.size(); ++ilocs) { + // Take current level values + Zcurrent = geopotentialHeight[rSort[ilocs]]; + Tcurrent = airTemperature[rSort[ilocs]]; + + // Cycle if airPressure is valid + if (airPressure[rSort[ilocs]] != missingValueFloat) continue; + + // Cycle if geopotentialHeight or airTemperatures is not valid + if (Zcurrent == missingValueFloat || Tcurrent == missingValueFloat) continue; + + // Update Tcurrent + if (dewPointTemperature.empty()) { + // Update Tcurrent if Rh is valid + if (relativeHumidity[rSort[ilocs]] != missingValueFloat) { + Pvap = formulas::SatVaporPres_fromTemp(Tprev, formulation()); + Pvap = formulas::SatVaporPres_correction(Pvap, Tprev, formulation()); + Tcurrent = formulas::VirtualTemp_From_Rh_Psat_P_T( + relativeHumidity[rSort[ilocs]], Pvap, Pprev, Tcurrent, formulation()); + } + } else { + // Update Tcurrent if dew point positive + if (dewPointTemperature[rSort[ilocs]] != missingValueFloat) { + Pvap = + formulas::SatVaporPres_fromTemp(dewPointTemperature[rSort[ilocs]], formulation()); + Pvap = formulas::SatVaporPres_correction(Pvap, + dewPointTemperature[rSort[ilocs]], + formulation()); + Tcurrent = formulas::VirtualTemp_From_Psat_P_T(Pvap, Pprev, Tcurrent, formulation()); + } + } + // Hydrostatic equation: + airPressure[rSort[ilocs]] = + Pprev * std::exp((Zprev - Zcurrent) * Constants::grav * 2.0 / + (Constants::rd * (Tprev + Tcurrent))); + + // update previous level for next level up + Pprev = airPressure[rSort[ilocs]]; + Zprev = Zcurrent; + Tprev = Tcurrent; + hasBeenUpdated = true; + } + } + + if (hasBeenUpdated) { + // if updated the airPressure + // assign the derived air pressure as DerivedValue + obsdb_.put_db(outputTag, "air_pressure", airPressure); + } +} + +/************************************************************************************/ +// Cal_PressureFromHeightForICAO +/************************************************************************************/ +static TransformMaker + makerCal_PressureFromHeightForICAO_("PressureFromHeightForICAO"); + +Cal_PressureFromHeightForICAO::Cal_PressureFromHeightForICAO( + const VariableTransformsParameters &options, ioda::ObsSpace &os, + const std::shared_ptr> &flags) + : TransformBase(options, os, flags) {} + +/************************************************************************************/ + +void Cal_PressureFromHeightForICAO::runTransform() { + oops::Log::trace() << " Retrieve Pressure From Height (ICAO)" << std::endl; + oops::Log::trace() << " --> method: " << method() << std::endl; + oops::Log::trace() << " --> formulation: " << formulation() << std::endl; + oops::Log::trace() << " --> obsName: " << obsName() << std::endl; + + // Get the right method + switch (method()) { + case formulas::MethodFormulation::NCAR: + case formulas::MethodFormulation::NOAA: + case formulas::MethodFormulation::UKMO: + default: { + methodUKMO(); + break; + } + } +} +/************************************************************************************/ + +void Cal_PressureFromHeightForICAO::methodUKMO() { + std::vector geopotentialHeight; + std::vector airPressure; + std::vector airPressure_ref; + bool hasBeenUpdated = false; + + const size_t nlocs_ = obsdb_.nlocs(); + + ioda::ObsSpace::RecIdxIter irec; + + // 0. Initialise the ouput array + // ------------------------------------------------------------------------------- + getObservation("MetaData", "air_pressure", + airPressure); + if (airPressure.empty()) { + airPressure = std::vector(nlocs_); + std::fill(airPressure.begin(), airPressure.end(), missingValueFloat); + } + + // 1. get the right variables + // ------------------------------------------------------------------------------- + getObservation("ObsValue", "geopotential_height", + geopotentialHeight); + + // 2. making sure what we need is here + // ------------------------------------------------------------------------------- + if (oops::anyVectorEmpty(geopotentialHeight)) { + oops::Log::warning() << "GeopotentialHeight vector is empty. " + << "Check will not be performed." << std::endl; + throw eckit::BadValue("GeopotentialHeight vector is the wrong size or empty ", Here()); + } + + // 3. Loop over each record + // ------------------------------------------------------------------------------------- + for (irec = obsdb_.recidx_begin(); irec != obsdb_.recidx_end(); ++irec) { + const std::vector &rSort = obsdb_.recidx_vector(irec); + size_t ilocs = 0; + + // 3.1 Loop over each record + for (ilocs = 0; ilocs < rSort.size(); ++ilocs) { + // Cycle if airPressure is valid + if (airPressure[rSort[ilocs]] != missingValueFloat) continue; + + airPressure[rSort[ilocs]] = formulas::Height_To_Pressure_ICAO_atmos( + geopotentialHeight[rSort[ilocs]], formulation()); + + hasBeenUpdated = true; + } + } + + if (hasBeenUpdated) { + // if updated the airPressure + // assign the derived air pressure as DerivedValue + obsdb_.put_db(outputTag, "air_pressure", airPressure); + } +} + +} // namespace ufo + diff --git a/src/ufo/variabletransforms/Cal_PressureFromHeight.h b/src/ufo/variabletransforms/Cal_PressureFromHeight.h new file mode 100644 index 000000000..e2911f63b --- /dev/null +++ b/src/ufo/variabletransforms/Cal_PressureFromHeight.h @@ -0,0 +1,64 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_VARIABLETRANSFORMS_CAL_PRESSUREFROMHEIGHT_H_ +#define UFO_VARIABLETRANSFORMS_CAL_PRESSUREFROMHEIGHT_H_ + +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "ufo/variabletransforms/TransformBase.h" + +namespace ufo { + +/*! +* \brief Derive Pressure from height for vertical profile (e.g. sonde report) +* +* \details This is especially need for radiosonde using a 3 09 055 bufr +* template. +* To date there is only the UKMO formulation available. +* +* See VariableTransformsParameters for filter setup. +*/ +class Cal_PressureFromHeightForProfile : public TransformBase { + public: + Cal_PressureFromHeightForProfile(const VariableTransformsParameters &options, + ioda::ObsSpace &os, + const std::shared_ptr> &flags); + // Run variable conversion + void runTransform() override; + + private: + // list of specific implementation(s) - This is controlled by "method" + void methodUKMO(); +}; + +/*! +* \brief Converts heights to pressures using the ICAO atmosphere. +* +* \details To date there is only the UKMO formulation available. +* +* See VariableTransformsParameters for filter setup. +*/ +class Cal_PressureFromHeightForICAO : public TransformBase { + public: + Cal_PressureFromHeightForICAO(const VariableTransformsParameters &options, + ioda::ObsSpace &os, + const std::shared_ptr> &flags); + // Run check + void runTransform() override; + + private: + // list of specific implementation(s) - This is controled by "method" + void methodUKMO(); +}; +} // namespace ufo + +#endif // UFO_VARIABLETRANSFORMS_CAL_PRESSUREFROMHEIGHT_H_ diff --git a/src/ufo/variabletransforms/Cal_Wind.cc b/src/ufo/variabletransforms/Cal_Wind.cc new file mode 100644 index 000000000..f82937b28 --- /dev/null +++ b/src/ufo/variabletransforms/Cal_Wind.cc @@ -0,0 +1,121 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "ufo/variabletransforms/Cal_Wind.h" +#include "ufo/filters/ProfileConsistencyCheckParameters.h" +#include "ufo/utils/Constants.h" + +namespace ufo { + +/************************************************************************************/ +// Cal_WindSpeedAndDirection +/************************************************************************************/ + +static TransformMaker + makerCal_WindSpeedAndDirection_("WindSpeedAndDirection"); + +Cal_WindSpeedAndDirection::Cal_WindSpeedAndDirection( + const VariableTransformsParameters &options, ioda::ObsSpace &os, + const std::shared_ptr> &flags) + : TransformBase(options, os, flags) {} + +/************************************************************************************/ + +void Cal_WindSpeedAndDirection::runTransform() { + oops::Log::trace() << " --> Retrieve wind speed and direction" + << std::endl; + oops::Log::trace() << " --> method: " << method() << std::endl; + oops::Log::trace() << " --> obsName: " << obsName() << std::endl; + + const size_t nlocs = obsdb_.nlocs(); + + std::vector u, v; + getObservation("ObsValue", "eastward_wind", + u, true); + getObservation("ObsValue", "northward_wind", + v, true); + + if (!oops::allVectorsSameNonZeroSize(u, v)) { + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(u, v) + << std::endl; + throw eckit::BadValue("At least one vector is the wrong size or empty out of " + "U, and V", Here()); + } + + std::vector windSpeed(nlocs), windFromDirection(nlocs); + windSpeed.assign(nlocs, missingValueFloat); + windFromDirection.assign(nlocs, missingValueFloat); + + // Loop over all obs + for (size_t jobs = 0; jobs < nlocs; ++jobs) { + // Calculate wind vector + if (u[jobs] != missingValueFloat && v[jobs] != missingValueFloat) { + windFromDirection[jobs] = formulas::GetWindDirection(u[jobs], v[jobs]); + windSpeed[jobs] = formulas::GetWindSpeed(u[jobs], v[jobs]); + } + } + // put new variable at existing locations + obsdb_.put_db(outputTag, "wind_speed", windSpeed); + obsdb_.put_db(outputTag, "wind_from_direction", windFromDirection); +} + +/************************************************************************************/ +// Cal_WindComponents +/************************************************************************************/ +static TransformMaker + makerCal_WindComponents_("WindComponents"); + +Cal_WindComponents::Cal_WindComponents( + const VariableTransformsParameters &options, ioda::ObsSpace &os, + const std::shared_ptr> &flags) + : TransformBase(options, os, flags) {} + +/************************************************************************************/ + +void Cal_WindComponents::runTransform() { + oops::Log::trace() << " --> Retrieve wind component" + << std::endl; + oops::Log::trace() << " --> method: " << method() << std::endl; + oops::Log::trace() << " --> obsName: " << obsName() << std::endl; + + const size_t nlocs = obsdb_.nlocs(); + + std::vector windSpeed, windFromDirection; + getObservation("ObsValue", "wind_speed", + windSpeed, true); + getObservation("ObsValue", "wind_from_direction", + windFromDirection, true); + + if (!oops::allVectorsSameNonZeroSize(windSpeed, windFromDirection)) { + oops::Log::warning() << "Vector sizes: " + << oops::listOfVectorSizes(windSpeed, windFromDirection) + << std::endl; + throw eckit::BadValue("At least one vector is the wrong size or empty out of " + "wind speed, and direction", Here()); + } + + std::vector u(nlocs), v(nlocs); + u.assign(nlocs, missingValueFloat); + v.assign(nlocs, missingValueFloat); + + // Loop over all obs + for (size_t jobs = 0; jobs < nlocs; ++jobs) { + // Calculate wind vector + if (windFromDirection[jobs] != missingValueFloat && + windSpeed[jobs] != missingValueFloat && windSpeed[jobs] >= 0) { + u[jobs] = formulas::GetWind_U(windSpeed[jobs], windFromDirection[jobs]); + v[jobs] = formulas::GetWind_V(windSpeed[jobs], windFromDirection[jobs]); + } + } + + // put new variable at existing locations + obsdb_.put_db(outputTag, "eastward_wind", u); + obsdb_.put_db(outputTag, "northward_wind", v); +} +} // namespace ufo + diff --git a/src/ufo/variabletransforms/Cal_Wind.h b/src/ufo/variabletransforms/Cal_Wind.h new file mode 100644 index 000000000..25fc23a7a --- /dev/null +++ b/src/ufo/variabletransforms/Cal_Wind.h @@ -0,0 +1,59 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_VARIABLETRANSFORMS_CAL_WIND_H_ +#define UFO_VARIABLETRANSFORMS_CAL_WIND_H_ + +#include +#include +#include +#include + +#include "oops/util/ObjectCounter.h" +#include "ufo/variabletransforms/TransformBase.h" + +namespace ufo { + +/*! +* \brief Wind Speed And Direction filter +* +* \details Performs a variable conversion from the wind components, eastward_wind and +* northward_wind, to wind_speed and wind_from_direction. The newly calculated variables +* are included in the same obs space. The filter does not have any configuration options. +/// +* +* See VariableTransformsParameters for filter setup. +*/ +class Cal_WindSpeedAndDirection : public TransformBase { + public: + Cal_WindSpeedAndDirection(const VariableTransformsParameters &options, + ioda::ObsSpace &os, + const std::shared_ptr> &flags); + // Run variable conversion + void runTransform() override; +}; + +/*! +* \brief Retrieve wind components. +* +* \details Performs a variable conversion from wind_speed and wind_from_direction to +* the wind components, eastward_wind and northward_wind. The newly calculated variables +* are included in the same obs space. +* +* See VariableTransformsParameters for filter setup. +*/ +class Cal_WindComponents : public TransformBase { + public: + Cal_WindComponents(const VariableTransformsParameters &options, + ioda::ObsSpace &os, + const std::shared_ptr> &flags); + // Run check + void runTransform() override; +}; +} // namespace ufo + +#endif // UFO_VARIABLETRANSFORMS_CAL_WIND_H_ diff --git a/src/ufo/variabletransforms/Formulas.cc b/src/ufo/variabletransforms/Formulas.cc new file mode 100644 index 000000000..6ca3c1178 --- /dev/null +++ b/src/ufo/variabletransforms/Formulas.cc @@ -0,0 +1,280 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include +#include +#include "eckit/exception/Exceptions.h" +#include "oops/util/Logger.h" +#include "ufo/utils/Constants.h" +#include "ufo/variabletransforms/Formulas.h" + +namespace ufo { + +namespace formulas { + +MethodFormulation resolveMethods(const std::string& input) { + // Met Centers + if (input == "UKMO") return UKMO; + if (input == "NCAR") return NCAR; + if (input == "NOAA") return NOAA; + return DEFAULT; +} + +MethodFormulation resolveFormulations(const std::string& input, const std::string& method) { + if (input.empty()) { + // if we do not have any formulation set, then we assign + // the method as formulation + return resolveMethods(method); + } else { + // Formulation authors + if (input == "Sonntag") return Sonntag; + if (input == "Walko") return Walko; + if (input == "Murphy") return Murphy; + return DEFAULT; + } +} + +/* -------------------------------------------------------------------------------------*/ +float SatVaporPres_fromTemp(float temp_K, MethodFormulation formulation) { + const float missingValueFloat = util::missingValue(1.0f); + const float t0c = static_cast(ufo::Constants::t0c); + float e_sub_s = missingValueFloat; // Saturation vapour pressure (Pa) + + switch (formulation) { + case formulas::MethodFormulation::UKMO: + case formulas::MethodFormulation::Sonntag: { + /* I. Source: Eqn 7, Sonntag, D., Advancements in the field of hygrometry, + * Meteorol. Zeitschrift, N. F., 3, 51-66, 1994. + * Most radiosonde manufacturers use Wexler, or Hyland and Wexler + * or Sonntag formulations, which are all very similar (Holger Vomel, + * pers. comm., 2011) + */ + if (temp_K != missingValueFloat) { + e_sub_s = std::exp(-6096.9385f / temp_K + 21.2409642f - 2.711193E-2f * temp_K + + 1.673952E-5f * temp_K * temp_K + 2.433502f * std::log(temp_K)); + } else { + e_sub_s = 0.0f; + } + break; + } + case formulas::MethodFormulation::Walko: { + // Polynomial fit of Goff-Gratch (1946) formulation. (Walko, 1991) + float x = std::max(-80.0f, temp_K-t0c); + const std::vector c{610.5851f, 44.40316f, 1.430341f, 0.2641412e-1f, + 0.2995057e-3f, 0.2031998e-5f, 0.6936113e-8f, 0.2564861e-11f, -0.3704404e-13f}; + e_sub_s = c[0]+x*(c[1]+x*(c[2]+x*(c[3]+x*(c[4]+x*(c[5]+x*(c[6]+x*(c[7]+x*c[8]))))))); + break; + } + case formulas::MethodFormulation::Murphy: { + // ALTERNATIVE (costs more CPU, more accurate than Walko, 1991) + // Source: Murphy and Koop, Review of the vapour pressure of ice and + // supercooled water for atmospheric applications, Q. J. R. + // Meteorol. Soc (2005), 131, pp. 1539-1565. + e_sub_s = std::exp(54.842763f - 6763.22f / temp_K - 4.210f * std::log(temp_K) + + 0.000367f * temp_K + std::tanh(0.0415f * (temp_K - 218.8f)) + * (53.878f - 1331.22f / temp_K - 9.44523f * std::log(temp_K) + + 0.014025f * temp_K)); + break; + } + case formulas::MethodFormulation::NCAR: + case formulas::MethodFormulation::NOAA: + case formulas::MethodFormulation::Rogers: + default: { + // Classical formula from Rogers and Yau (1989; Eq2.17) + e_sub_s = 1000. * 0.6112 * std::exp(17.67f * (temp_K - t0c) / (temp_K - 29.65f)); + break; + } + } + return e_sub_s; +} + +/* -------------------------------------------------------------------------------------*/ +float SatVaporPres_correction(float e_sub_s, float temp_K, MethodFormulation formulation) { + const float t0c = static_cast(ufo::Constants::t0c); + + switch (formulation) { + case formulas::MethodFormulation::NCAR: + case formulas::MethodFormulation::NOAA: + case formulas::MethodFormulation::UKMO: + case formulas::MethodFormulation::Sonntag: { + /* e_sub_s above is the saturation vapour pressure of pure water vapour + FsubW (~ 1.005 at 1000 hPa) is the enhancement factor needed for moist + air (eg eqns 20, 22 of Sonntag, but for consistency with QSAT the formula + below is from eqn A4.6 of Adrian Gill's book) + */ + float FsubW; // Enhancement factor + FsubW = 1.0f - 1.0E-8f * + (4.5f + 6.0E-4f * (temp_K - t0c) *(temp_K - t0c)); + e_sub_s = e_sub_s * FsubW; + break; + } + default: { + std::string errString = "Aborting, no method matches enum formulas::MethodFormulation"; + oops::Log::error() << errString; + throw eckit::BadValue(errString); + } + } + return e_sub_s; +} +/* -------------------------------------------------------------------------------------*/ + +float Qsat_From_Psat(float Psat, float P, MethodFormulation formulation) { + float QSat = util::missingValue(1.0f); // Saturated specific humidity or + // saturated vapour pressure (if P<0) + + switch (formulation) { + case formulas::MethodFormulation::NCAR: + case formulas::MethodFormulation::NOAA: + case formulas::MethodFormulation::UKMO: + default: { + // Calculation using the Sonntag (1994) formula. (With fix at low + // pressure) + QSat = (Constants::epsilon * Psat) / + (std::max(P, Psat) - (1.0f - Constants::epsilon) * Psat); + break; + } + } + return QSat; +} + +/* -------------------------------------------------------------------------------------*/ + +// VirtualTemperature() +float VirtualTemp_From_Psat_P_T(float Psat, float P, float T, MethodFormulation formulation) { + float Tv = util::missingValue(1.0f); // virtual temperature + + switch (formulation) { + case formulas::MethodFormulation::NCAR: + case formulas::MethodFormulation::NOAA: + case formulas::MethodFormulation::UKMO: + default: { + Tv = T * ((P + Psat / Constants::epsilon) / (P + Psat)); + break; + } + } + return Tv; +} + +/* -------------------------------------------------------------------------------------*/ + +float VirtualTemp_From_Rh_Psat_P_T(float Rh, float Psat, float P, float T, + MethodFormulation formulation) { + float Tv = util::missingValue(1.0f); // virtual temperature + + switch (formulation) { + case formulas::MethodFormulation::NCAR: + case formulas::MethodFormulation::NOAA: + case formulas::MethodFormulation::UKMO: + default: { + float PsatRh = Psat * Rh * 0.01f; + Tv = VirtualTemp_From_Psat_P_T(PsatRh , P, T, formulation); + break; + } + } + + return Tv; +} + +/* -------------------------------------------------------------------------------------*/ + +float Height_To_Pressure_ICAO_atmos(float height, MethodFormulation formulation) { + const float missingValueFloat = util::missingValue(1.0f); + float Pressure = missingValueFloat; + + switch (formulation) { + case formulas::MethodFormulation::NCAR: + case formulas::MethodFormulation::NOAA: + case formulas::MethodFormulation::UKMO: + default: { + float RepT_Bot, RepT_Top, ZP1, ZP2; + + RepT_Bot = 1.0 / Constants::icao_temp_surface; + RepT_Top = 1.0 / Constants::icao_temp_isothermal_layer; + ZP1 = Constants::g_over_rd / Constants::icao_lapse_rate_l; + ZP2 = Constants::g_over_rd / Constants::icao_lapse_rate_u; + + if (height <= missingValueFloat) { + Pressure = missingValueFloat; + } else if (height < -5000.0) { + // TODO(david simonin): The original code has this test. + // Not sure why! Are we expecting very negative height value?? + Pressure = missingValueFloat; + } else if (height < Constants::icao_height_l) { + // Heights up to 11,000 geopotential heigh in meter [gpm] + Pressure = Constants::icao_lapse_rate_l * height * RepT_Bot; + Pressure = std::pow((1.0 - Pressure), ZP1); + Pressure = 100.0 * Pressure * Constants::icao_pressure_surface; + } else if (height < Constants::icao_height_u) { + // Heights between 11,000 and 20,000 geopotential heigh in meter [gpm] + Pressure = Constants::g_over_rd * (height - Constants::icao_height_l) * RepT_Top; + Pressure = std::log(Constants::icao_pressure_l) - Pressure; + Pressure = 100.0 * std::exp(Pressure); + } else { + // Heights above 20,000 geopotential heigh in meter [gpm] + Pressure = Constants::icao_lapse_rate_u * RepT_Top * + (height - Constants::icao_height_u); + Pressure = 100.0 * Constants::icao_pressure_u * + std::pow((1.0 - Pressure), ZP2); + } + break; + } + } + return Pressure; +} + +float GetWindDirection(float u, float v) { + const float missing = util::missingValue(1.0f); + float windDirection = missing; // wind direction + + if (u != missing && v != missing) { + if (u != 0.0f || v != 0.0f) { + windDirection = fmod((270.0f - atan2(v, u) * Constants::rad2deg), 360.0f); + } else { + windDirection = 0.0f; + } + } + return windDirection; +} + +float GetWindSpeed(float u, float v) { + const float missing = util::missingValue(1.0f); + float windSpeed = missing; // wind speed + + if (u != missing && v != missing) { + windSpeed = hypot(u, v); + } + return windSpeed; +} + +float GetWind_U(float windSpeed, float windFromDirection) { + const float missing = util::missingValue(1.0f); + float u = missing; // wind speed + + if (windFromDirection != missing + && windSpeed != missing && windSpeed >= 0) { + u = -windSpeed * sin(windFromDirection * Constants::deg2rad); + } + return u; +} + + +float GetWind_V(float windSpeed, float windFromDirection) { + const float missing = util::missingValue(1.0f); + float v = missing; // wind speed + + if (windFromDirection != missing + && windSpeed != missing && windSpeed >= 0) { + v = -windSpeed * cos(windFromDirection * Constants::deg2rad); + } + return v; +} + + +} // namespace formulas +} // namespace ufo diff --git a/src/ufo/variabletransforms/Formulas.h b/src/ufo/variabletransforms/Formulas.h new file mode 100644 index 000000000..698d63fc2 --- /dev/null +++ b/src/ufo/variabletransforms/Formulas.h @@ -0,0 +1,235 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_VARIABLETRANSFORMS_FORMULAS_H_ +#define UFO_VARIABLETRANSFORMS_FORMULAS_H_ + +#include +#include +#include +#include + +#include "oops/util/missingValues.h" +#include "ufo/utils/Constants.h" + +namespace ufo { + +namespace formulas { + +/*! Various Methods and Formulations available */ +enum MethodFormulation { + // Methods: Met Center + UKMO, /*!< UKMO specific formulation */ + NCAR, /*!< NCAR specific formulation */ + NOAA, /*!< NOAA specific formulation */ + DEFAULT, /*!< DEFAULT formulation */ + + // Formulations: Specific authors + Murphy, + Sonntag, + Walko, + Rogers +}; + +MethodFormulation resolveMethods(const std::string& input); + +MethodFormulation resolveFormulations(const std::string& input, const std::string& method); + +// ------------------------------------------------------------------------------------- +/*! +* \brief Calculates saturated vapour pressure from temperature +* +* \b Formulation \b available: +* - UKMO: as Sonntag 1997 +* - Sonntag: +* Calculation is using the Eqn 7 Sonntag (1997) +* Reference: "Sonntag, D., Advancements in the field of hygrometry, +* Meteorol. Zeitschrift, N. F., 3, 51-66, 1994." . +* - Walko: +* Polynomial fit of Goff-Gratch (1946) formulation. (Walko, 1991) +* - Murphy: +* Alternative method to Walko 1991 (costs more CPU, more accurate) +* Reference: "Murphy and Koop, Review of the vapour pressure of ice and + supercooled water for atmospheric applications, Q. J. R. + Meteorol. Soc (2005), 131, pp. 1539-1565." +* - NCAR: as default +* - NOAA: as default +* - DEFAULT: +* Classical formula from Rogers and Yau (1989; Eq2.17) +* +* +* \param temp_K +* Temperature [k] +* \return saturated vapour pressure +*/ +float SatVaporPres_fromTemp(const float temp_K, + const MethodFormulation formulation = formulas::MethodFormulation::DEFAULT); + + +// ------------------------------------------------------------------------------------- +/*! +* \brief Calculates saturated vapour pressure from temperature +* +* \b Formulation \b available: +* - NCAR: as Sonntag 1997 +* - NOAA: as Sonntag 1997 +* - UKMO: as Sonntag 1997 +* - Sonntag: +* Correct the the saturation vapour pressure of pure water vapour +* using an enhancement factor needed for moist air. +* Reference: eg eqns 20, 22 of Sonntag, but for consistency with QSAT the formula + below is from eqn A4.6 of Adrian Gill's book. +* +* +* \param e_sub_s +* saturation vapour pressure +* \param temp_K +* Temperature [k] +* \return saturated vapour pressure +*/ +float SatVaporPres_correction(float e_sub_s, float temp_K, + const MethodFormulation formulation = formulas::MethodFormulation::DEFAULT); +// ------------------------------------------------------------------------------------- +/*! +* \brief Calculates Saturated specific humidity or saturated vapour pressure using +* saturation vapour pressure. +* +* +* \b Formulation \b available: +* - NCAR: as default +* - NOAA: as default +* - UKMO: as default +* - DEFAULT: +* Calculation is using the Sonntag (1994) formula. (With fix at low +*pressure) +* +* \param Psat +* saturation vapour pressure of pure water vapour +* \param P +* Pressure +* \return Saturated specific humidity or saturated vapour pressure +*/ +float Qsat_From_Psat(float Psat, float P, + MethodFormulation formulation = formulas::MethodFormulation::DEFAULT); + +// ------------------------------------------------------------------------------------- +/*! +* \brief Derive Virtual Temperature from saturation vapour pressure, pressure and temperature +* +* \b Formulation \b available: +* - NCAR: as default +* - NOAA: as default +* - UKMO: as default +* - DEFAULT: \f$ Tv = T * ((P + Psat / \epsilon) / (P + Psat)) \f$ +* +* \param Psat +* saturation vapour pressure of pure water vapour +* \param T +* Temperature +* \param P +* Pressure +* \return Virtual Temperature +*/ +float VirtualTemp_From_Psat_P_T(float Psat, float P, float T, + MethodFormulation formulation = formulas::MethodFormulation::DEFAULT); + +// ------------------------------------------------------------------------------------- +/*! +* \brief Derive Virtual Tempreture using Relative humidity, sat. vapour pressure, pressure +* and temperature +* +* \b Formulation \b available: +* - NCAR: as default +* - NOAA: as default +* - UKMO: as default +* - DEFAULT: \f$ \alpha = Psat * Rh * 0.01 \f$ +* +* \param Rh +* Relative humidity +* \param Psat +* saturation vapour pressure of pure water vapour +* \param T +* Temperature +* \param P +* Pressure +* \return Virtual Temperature +*/ +float VirtualTemp_From_Rh_Psat_P_T(float Rh, float Psat, float P, float T, + MethodFormulation formulation = formulas::MethodFormulation::DEFAULT); + +// ------------------------------------------------------------------------------------- +/*! +* \brief Converts height to pressure using the International Civil Aviation Organization +* (ICAO) atmosphere. +* +* \b Formulation \b available: +* - NCAR: as default +* - NOAA: as default +* - UKMO: as default +* - DEFAULT: using ICAO standard +* +* \param height +* observation height in geopotential metres [gpm] +* \return pressure +*/ +float Height_To_Pressure_ICAO_atmos(float Height, + MethodFormulation formulation = formulas::MethodFormulation::DEFAULT); + +// ------------------------------------------------------------------------------------- +/*! +* \brief Converts u and v wind component into wind direction. +* Wind direction is defined such that a northerly wind is 0°, an easterly wind is 90°, +* a southerly wind is 180°, and a westerly wind is 270°. +* +* \param u +* eastward (u) wind component[m/s] +* \param v +* northward (v) wind component[m/s] +* \return windDirection +*/ +float GetWindDirection(float u, float v); + +// ------------------------------------------------------------------------------------- +/*! +* \brief Converts u and v wind component into wind speed. +* +* \param u +* eastward (u) wind component[m/s] +* \param v +* northward (v) wind component[m/s] +* \return windSpeed +*/ +float GetWindSpeed(float u, float v); + +// ------------------------------------------------------------------------------------- +/*! +* \brief Get eastward (u) wind component from wind speed and direction. +* +* \param windSpeed +* wind speed [m/s] +* \param windFromDirection +* wind direction [degree] +* \return u +*/ +float GetWind_U(float windSpeed, float windFromDirection); + +// ------------------------------------------------------------------------------------- +/*! +* \brief Get northward (v) wind component from wind speed and direction. +* +* \param windSpeed +* wind speed [m/s] +* \param windFromDirection +* wind direction [degree] +* \return v +*/ +float GetWind_V(float windSpeed, float windFromDirection); + +} // namespace formulas +} // namespace ufo + +#endif // UFO_VARIABLETRANSFORMS_FORMULAS_H_ diff --git a/src/ufo/variabletransforms/TransformBase.cc b/src/ufo/variabletransforms/TransformBase.cc new file mode 100644 index 000000000..0f2a5a300 --- /dev/null +++ b/src/ufo/variabletransforms/TransformBase.cc @@ -0,0 +1,102 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include +#include +#include "oops/util/Logger.h" +#include "ufo/variabletransforms/TransformBase.h" + +namespace ufo { + +TransformBase::TransformBase(const VariableTransformsParameters& options, + ioda::ObsSpace& os, + const std::shared_ptr>& flags) + : options_(options), obsdb_(os) , flags_(*flags) { + method_ = formulas::resolveMethods(options.Method.value()); + formulation_ = formulas::resolveFormulations(options.Formulation.value(), + options.Method.value()); + UseValidDataOnly_ = options.UseValidDataOnly.value(); + obsName_ = os.obsname(); +} + +void TransformBase::filterObservation(const std::string &variableName, + std::vector &obsVector) const { + if (flags_.has(variableName)) { + const float missing = missingValueFloat; + const std::vector *varFlags = &flags_[variableName]; + + std::transform(obsVector.begin(), obsVector.end(), // Input range 1 + varFlags->begin(), // First element of input range vector 2 (must be same size) + obsVector.begin(), // First element of output range (must be same size) + [missing](float obsvalue, int flag) + { return flag == QCflags::missing || flag == QCflags::bounds + ? missing : obsvalue; }); + } +} + +void TransformBase::getObservation(const std::string &originalTag, const std::string &varName, + std::vector &obsVector, bool require) const { + const size_t nlocs = obsdb_.nlocs(); + + if (obsdb_.has("DerivedValue", varName)) { + obsVector = std::vector(nlocs); + obsdb_.get_db("DerivedValue", varName, obsVector); + // Set obsValue to missingValueFloat if flag is equal to QCflags::missing or QCflags::bounds + if (UseValidDataOnly()) filterObservation(varName, obsVector); + } else if (obsdb_.has(originalTag, varName)) { + obsVector = std::vector(nlocs); + obsdb_.get_db(originalTag, varName, obsVector); + // Set obsValue to missingValueFloat if flag is equal to QCflags::missing or QCflags::bounds + if (UseValidDataOnly()) filterObservation(varName, obsVector); + } + + if (require && obsVector.empty()) { + throw eckit::BadValue("The parameter `" + varName + "@" + originalTag + + "` does not exist in the ObsSpace ", Here()); + } +} + +TransformFactory::TransformFactory(const std::string& name) { + if (getMakers().find(name) != getMakers().end()) + throw eckit::BadParameter( + name + " already registered in ufo::TransformFactory.", Here()); + + getMakers()[name] = this; +} + +std::unique_ptr TransformFactory::create( + const std::string& name, const VariableTransformsParameters& options, + ioda::ObsSpace& os, const std::shared_ptr>& flags) { + + oops::Log::trace() << " --> TransformFactory::create" << std::endl; + oops::Log::trace() << " --> name: " << name << std::endl; + + typename std::map::iterator jloc = + getMakers().find(name); + + if (jloc == getMakers().end()) { + std::string makerNameList; + for (const auto& makerDetails : getMakers()) + makerNameList += "\n " + makerDetails.first; + std::cout << " --> makerNameList" << makerNameList << std::endl; + std::cout << " " << name + << " does not exist in ufo::TransformFactory. " + << "Possible values:" << makerNameList << std::endl; + + throw eckit::BadParameter(name + + " does not exist in ufo::TransformFactory. " + "Possible values:" + + makerNameList, + Here()); + } + + std::unique_ptr ptr = jloc->second->make(options, os, flags); + oops::Log::trace() << "TransformBase::create done" << std::endl; + + return ptr; +} +} // namespace ufo diff --git a/src/ufo/variabletransforms/TransformBase.h b/src/ufo/variabletransforms/TransformBase.h new file mode 100644 index 000000000..5279219f8 --- /dev/null +++ b/src/ufo/variabletransforms/TransformBase.h @@ -0,0 +1,125 @@ +/* + * (C) Crown copyright 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef UFO_VARIABLETRANSFORMS_TRANSFORMBASE_H_ +#define UFO_VARIABLETRANSFORMS_TRANSFORMBASE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eckit/exception/Exceptions.h" +#include "eckit/types/FloatCompare.h" + +#include "ioda/ObsDataVector.h" + +#include "oops/util/CompareNVectors.h" +#include "oops/util/Logger.h" +#include "oops/util/missingValues.h" +#include "oops/util/ObjectCounter.h" +#include "oops/util/PropertiesOfNVectors.h" + +#include "ufo/filters/QCflags.h" +#include "ufo/filters/VariableTransformsParameters.h" +#include "ufo/variabletransforms/Formulas.h" + +namespace ioda { +template +class ObsDataVector; +class ObsSpace; +class ObsVector; +} + +namespace ufo { +class VariableTransformsParameters; +} + +namespace ufo { + +/// \brief Base class for variable conversion +class TransformBase { + public: + TransformBase(const VariableTransformsParameters &options, + ioda::ObsSpace &os, + const std::shared_ptr>& flags); + /// Destructor + virtual ~TransformBase() {} + /// Run variable conversion + virtual void runTransform() = 0; + + private: + void filterObservation(const std::string &variable, std::vector &obsVector) const; + /// Method used for the calculation + formulas::MethodFormulation method_; + formulas::MethodFormulation formulation_; + bool UseValidDataOnly_; + /// The observation name + std::string obsName_; + + protected: // variables + void getObservation(const std::string &originalTag, const std::string &varName, + std::vector &obsVector, bool require = false) const; + /// subclasses to access Method and formualtion used for the calculation + formulas::MethodFormulation method() const { return method_; } + formulas::MethodFormulation formulation() const { return formulation_; } + bool UseValidDataOnly() const { return UseValidDataOnly_; } + void SetUseValidDataOnly(bool t) {UseValidDataOnly_ = t; } + /// subclasses to access the observation name + std::string obsName() const { return obsName_; } + /// Configurable parameters + const VariableTransformsParameters &options_; + /// Observation space + ioda::ObsSpace &obsdb_; + const ioda::ObsDataVector &flags_; + /// Missing value (float) + const float missingValueFloat = util::missingValue(1.0f); + /// output tag for derived parameters + const std::string outputTag = "DerivedValue"; +}; + +/// \brief Transform factory +class TransformFactory { + public: + static std::unique_ptr create( + const std::string &, const VariableTransformsParameters &, + ioda::ObsSpace &, const std::shared_ptr> &); + virtual ~TransformFactory() = default; + + protected: + explicit TransformFactory(const std::string &); + + private: + virtual std::unique_ptr make( + const VariableTransformsParameters &, ioda::ObsSpace &, + const std::shared_ptr> &) = 0; + static std::map &getMakers() { + static std::map makers_; + return makers_; + } +}; + +/// \brief Transform maker +template +class TransformMaker : public TransformFactory { + virtual std::unique_ptr make( + const VariableTransformsParameters &options, ioda::ObsSpace &os, + const std::shared_ptr> &flags) { + return std::unique_ptr(new T(options, os, flags)); + } + + public: + explicit TransformMaker(const std::string &name) : TransformFactory(name) {} +}; + +} // namespace ufo + +#endif // UFO_VARIABLETRANSFORMS_TRANSFORMBASE_H_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cbeecfb09..6dffadc9b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,26 +1,11 @@ -# (C) Copyright 2017-2020 UCAR. +# (C) Copyright 2017-2021 UCAR. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -# macro to create a symlink from src to dst -function(CREATE_SYMLINK src dst) - foreach (FILENAME ${ARGN}) - execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink - ${src}/${FILENAME} - ${dst}/${FILENAME} ) - endforeach(FILENAME) -endfunction(CREATE_SYMLINK) - -# macro to create a symlink from src to dst with just filename -function(CREATE_SYMLINK_FILENAME src dst) - foreach (FILENAME ${ARGN}) - get_filename_component(filename ${FILENAME} NAME ) - execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink - ${src}/${FILENAME} - ${dst}/${filename} ) - endforeach(FILENAME) -endfunction(CREATE_SYMLINK_FILENAME) +# Include macros for creating links and symlinks +include( ufo_functions ) +include( git_functions ) # Create Data directory for test input config and symlink all files list( APPEND ufo_test_input @@ -28,8 +13,13 @@ list( APPEND ufo_test_input testinput/adt.yaml testinput/aircraft.yaml testinput/airs_crtm.yaml - testinput/airs_crtm_bc.yaml testinput/airs_qc_filters.yaml + testinput/amsr2_rttov_qc.yaml + testinput/amsr2_crtm.yaml + testinput/amsr2_clw_ret.yaml + testinput/amsr2_qc.yaml + testinput/amsr2_qc_halo.yaml + testinput/amsr2_rttov_ops_qc_rttovonedvarcheck.yaml testinput/amsua_crtm.yaml testinput/amsua_crtm_bc.yaml testinput/amsua_qc.yaml @@ -41,16 +31,28 @@ list( APPEND ufo_test_input testinput/aod_crtm.yaml testinput/aod_luts.yaml testinput/atms_crtm.yaml - testinput/atms_crtm_bc.yaml testinput/atms_qc_filters.yaml + testinput/atms_rttov_ops_qc_rttovonedvarcheck.yaml testinput/atms_rttov_ops.yaml + testinput/atms_rttov_qc.yaml + testinput/amsua_rttovcpp.yaml + testinput/background_error_vert_interp.yaml + testinput/background_error_identity.yaml + testinput/bias_coeff.yaml + testinput/bias_coeff_cov.yaml + testinput/bias_linear_op.yaml + testinput/bias_single_channel.yaml + testinput/categorical.yaml testinput/coolskin.yaml + testinput/composite.yaml + testinput/constant_predictor.yaml testinput/cris_crtm.yaml - testinput/cris_crtm_bc.yaml testinput/cris_qc.yaml testinput/cris_qc_filters.yaml testinput/cris_qc_land.yaml testinput/empty.yaml + testinput/function_bennartzscatindex.yaml + testinput/function_cloudcost.yaml testinput/function_clouddetect_iasi.yaml testinput/function_clouddetect_airs.yaml testinput/function_clouddetect_cris.yaml @@ -72,27 +74,43 @@ list( APPEND ufo_test_input testinput/function_errfwavenum.yaml testinput/function_hydrometeorchk.yaml testinput/function_hydrometeorchk_atms.yaml + testinput/function_bgddepartureanomaly.yaml testinput/function_nsstret.yaml + testinput/function_obserrorinflatesfcp.yaml + testinput/function_obserrorfactorconv.yaml testinput/function_obserrmean.yaml testinput/function_obserrmean_abi.yaml testinput/function_obserrmean_mhs.yaml testinput/function_obserrmean_atms.yaml + testinput/function_reperr.yaml + testinput/function_satwind_indiv_errors.yaml testinput/function_scatret.yaml testinput/function_scatret_atms.yaml testinput/function_errfsdoei.yaml testinput/function_errfsdoei_atms.yaml testinput/function_velocity.yaml + testinput/function_obserrorstepwiselinear.yaml testinput/gmi_crtm.yaml + testinput/gmi_clw_ret.yaml + testinput/gmi_qc_filters.yaml + testinput/gmi_scan_angles.yaml testinput/genericprof.yaml testinput/geovals.yaml testinput/geos_aod.yaml + testinput/aod_extinction.yaml testinput/geovals_spec.yaml testinput/gnssrobendmetoffice.yaml + testinput/gnssrobendmetoffice_qc.yaml + testinput/gnssrobendmetoffice_obserror.yaml testinput/gnssrobendmetoffice_nopseudo.yaml + testinput/gnssrobendmetoffice_qc.yaml testinput/gnssrobndropp1d.yaml + testinput/gnssrobndropp1d_qc.yaml testinput/gnssrobndropp2d.yaml testinput/gnssrobndnbam.yaml testinput/gnssro_obs_error.yaml + testinput/gnssro_obs_error_halo.yaml + testinput/gnssro_obs_error_ropp.yaml testinput/gnssrobndnbam_backgroundcheck_qc.yaml testinput/gnssrobndnbam_super_refraction.yaml testinput/gnssrobndnbam_qc_action_obserr_inflation.yaml @@ -103,15 +121,21 @@ list( APPEND ufo_test_input testinput/gome_metop-a.yaml testinput/gome_metop-a_flipz.yaml testinput/groundgnssmetoffice.yaml + testinput/groundgnssropp.yaml testinput/gsisfcmodel.yaml testinput/hirs4_crtm.yaml testinput/iasi_crtm.yaml - testinput/iasi_crtm_bc.yaml testinput/iasi_qc.yaml testinput/iasi_qc_filters.yaml + testinput/interpolate_data_from_file_predictor.yaml + testinput/legendre_predictor.yaml testinput/locations.yaml testinput/metar_qc_filters.yaml testinput/mhs_crtm.yaml + testinput/obsdiag_background_error_identity.yaml + testinput/obsdiag_background_error_upper_air_and_surface_variables.yaml + testinput/obsdiag_background_error_vert_interp_air_pressure.yaml + testinput/obsdiag_background_error_vert_interp_height.yaml testinput/obsdiag_crtm_airs_jacobian.yaml testinput/obsdiag_crtm_airs_optics.yaml testinput/obsdiag_crtm_amsua_jacobian.yaml @@ -122,22 +146,28 @@ list( APPEND ufo_test_input testinput/obsdiag_crtm_cris_optics.yaml testinput/obsdiag_crtm_iasi_jacobian.yaml testinput/obsdiag_crtm_iasi_optics.yaml + testinput/obserror_assign_unittests.yaml testinput/obsfilterdata.yaml testinput/omi_aura.yaml testinput/omi_aura_flipz.yaml testinput/ompsnp_npp.yaml testinput/ompsnp_npp_flipz.yaml testinput/ompsnp_npp_L127.yaml + testinput/ompsnp_npp_qc_L127.yaml testinput/ompstc_npp.yaml testinput/ompstc_npp_flipz.yaml + testinput/ompslp_npp.yaml + testinput/operatorutils.yaml + testinput/orbital_angle_predictor.yaml testinput/processwhere.yaml - testinput/parallel_obs_distribution.yaml testinput/parameters.yaml - testinput/qc_trackcheck.yaml - testinput/qc_trackcheck_unittests.yaml - testinput/qc_trackcheckship_unittests.yaml - testinput/qc_trackcheckship_mainloop_unittests.yaml + testinput/parameters_older_eckit.yaml + testinput/primitive_variables.yaml + testinput/metoffice_radiance_error_matrices.yaml + testinput/qc_actions.yaml testinput/qc_backgroundcheck.yaml + testinput/qc_bayesianbackgroundqcflags.yaml + testinput/qc_bayesian_background_check.yaml testinput/qc_boundscheck.yaml testinput/qc_velocitycheck.yaml testinput/qc_defer_to_post.yaml @@ -146,24 +176,43 @@ list( APPEND ufo_test_input testinput/qc_differencecheck.yaml testinput/qc_gauss_thinning.yaml testinput/qc_gauss_thinning_unittests.yaml + testinput/qc_historycheck_unittests.yaml testinput/qc_met_office_buddy_check.yaml testinput/qc_met_office_buddy_check_unittests.yaml testinput/qc_met_office_buddy_pair_finder.yaml - testinput/qc_recursive_splitter.yaml + testinput/qc_modelobthreshold.yaml testinput/qc_oceancolor_preqc.yaml + testinput/qc_performaction.yaml testinput/qc_poisson_disk_thinning.yaml testinput/qc_poisson_disk_thinning_unittests.yaml + testinput/qc_poisson_disk_thinning_parallel.yaml testinput/qc_preqc.yaml + testinput/qc_preqc_halo.yaml + testinput/qc_profile_background_check.yaml + testinput/qc_profile_fewobs_check.yaml + testinput/qc_stuckcheck.yaml + testinput/qc_stuckcheck_unittests.yaml testinput/qc_thinning.yaml testinput/qc_temporal_thinning.yaml testinput/qc_temporal_thinning_unittests.yaml + testinput/qc_trackcheck.yaml + testinput/qc_trackcheck_unittests.yaml + testinput/qc_trackcheckship.yaml + testinput/qc_trackcheckship_mainloop_unittests.yaml + testinput/qc_trackcheckship_unittests.yaml + testinput/qc_variableassignment.yaml + testinput/qc_acceptlist.yaml testinput/radiosonde.yaml testinput/radialvelocity.yaml testinput/reflectivity.yaml + testinput/satname.yaml + testinput/sattcwv.yaml testinput/satwind.yaml + testinput/satwind_metoffice.yaml testinput/sbuv2_n19.yaml testinput/sbuv2_n19_flipz.yaml testinput/sbuv2_n19_L127.yaml + testinput/scatwind_neutral_metoffice.yaml testinput/seaicefrac.yaml testinput/seaicethick.yaml testinput/chleuzintegr.yaml @@ -172,29 +221,70 @@ list( APPEND ufo_test_input testinput/smap_crtm.yaml testinput/sndrd1-4_crtm.yaml testinput/sfcpcorrected.yaml + testinput/thickness_predictor.yaml testinput/profileconsistencychecks_monolithicfilter.yaml testinput/profileconsistencychecks_OPScomparison.yaml testinput/profileconsistencychecks_wrongOPScomparison.yaml testinput/profileconsistencychecks_separatefilters.yaml testinput/profileconsistencychecks_unittests.yaml testinput/profileconsistencychecks_unittest_oneprofileMPI.yaml + testinput/profileconsistencychecks_unittests_vertaverage.yaml + testinput/profileconsistencychecks_unittests_vertaverage_ascending.yaml + testinput/profileconsistencychecks_unittests_vertinterp.yaml + testinput/profileconsistencychecks_unittests_profiledataholder.yaml testinput/profileconsistencychecks_RH_obsfilter.yaml testinput/profileconsistencychecks_RH_OPScomparison.yaml testinput/profileconsistencychecks_UInterp_obsfilter.yaml testinput/profileconsistencychecks_UInterp_OPScomparison.yaml + testinput/profileconsistencychecks_UInterpAlternative_obsfilter.yaml + testinput/profileconsistencychecks_UInterpAlternative_OPScomparison.yaml testinput/profileconsistencychecks_bkgqc_modobs_obsfilter.yaml + testinput/profileconsistencychecks_bkgqc_modobs_separatefilters_obsfilter.yaml testinput/profileconsistencychecks_bkgqc_repobs_obsfilter.yaml + testinput/profileconsistencychecks_bkgqc_repobs_separatefilters_obsfilter.yaml testinput/profileconsistencychecks_bkgqc_modobs_OPScomparison.yaml testinput/profileconsistencychecks_bkgqc_repobs_OPScomparison.yaml + testinput/profileconsistencychecks_sondeflags_obsfilter.yaml + testinput/profileconsistencychecks_sondeflags_OPScomparison.yaml + testinput/profileconsistencychecks_winproflags_obsfilter.yaml + testinput/profileconsistencychecks_winproflags_OPScomparison.yaml + testinput/profileconsistencychecks_pressure_obsfilter.yaml + testinput/profileconsistencychecks_pressure_OPScomparison.yaml + testinput/profileconsistencychecks_average_pressure_obsfilter.yaml + testinput/profileconsistencychecks_average_pressure_OPScomparison.yaml + testinput/profileconsistencychecks_average_temperature_obsfilter.yaml + testinput/profileconsistencychecks_average_temperature_OPScomparison.yaml + testinput/profileconsistencychecks_average_windspeed_obsfilter.yaml + testinput/profileconsistencychecks_average_windspeed_OPScomparison.yaml + testinput/profileconsistencychecks_average_relativehumidity_obsfilter.yaml + testinput/profileconsistencychecks_average_relativehumidity_OPScomparison.yaml + testinput/profileconsistencychecks_opr_average.yaml + testinput/ssmis_f17_gfs_backgroundcheck_bc.yaml + testinput/ssmis_f17_gfs_backgroundcheck_nbc.yaml testinput/timeoper.yaml testinput/tprof.yaml testinput/tropomi_no2.yaml testinput/variables.yaml testinput/windprof.yaml + testinput/unit_tests/function_clwret_ssmis.yaml + testinput/unit_tests/function_tropo_estimate.yaml + testinput/unit_tests/function_lam_domaincheck.yaml + testinput/unit_tests/function_lam_domaincheck_circle.yaml + testinput/unit_tests/function_obserrorfactorquotient.yaml + testinput/unit_tests/function_satwindsLNVDcheck.yaml + testinput/unit_tests/function_satwindsSPDBcheck.yaml + testinput/unit_tests/function_wdirdiff.yaml + testinput/unit_tests/variabletransforms_rhumidity.yaml + testinput/unit_tests/variabletransforms_shumidity.yaml + testinput/unit_tests/variabletransforms_SondePressureFromHeight.yaml + testinput/unit_tests/variabletransforms_SondePressureFromHeightICAO.yaml + testinput/unit_tests/variabletransforms_windcomponents.yaml + testinput/unit_tests/variabletransforms_windspeedanddirection.yaml ) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/testinput) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/testinput/unit_tests) CREATE_SYMLINK( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${ufo_test_input} ) @@ -208,27 +298,10 @@ ecbuild_add_resources( TARGET ufo_test_scripts file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Data) # Test data filenames -set(ioda_test_data ioda_testinput_tier_1.tar.gz) set(ufo_test_data ufo_testinput_tier_1.tar.gz) set(crtm_test_data crtm_coefficients.tar.gz) -# Determine the branch name -set( UFO_TESTFILES_BRANCH "$ENV{UFO_TESTFILES_BRANCH}") -if ( UFO_TESTFILES_BRANCH STREQUAL "" ) - # Get the current git branch of ufo repo - execute_process( - COMMAND bash "-c" "git symbolic-ref -q --short HEAD || git describe --tags --exact-match" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE GIT_BRANCH_UFO - OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "UFO is in branch: ${GIT_BRANCH_UFO}") -else() - message(STATUS "Branch name provided by user: ${UFO_TESTFILES_BRANCH}") - set(GIT_BRANCH_UFO "${UFO_TESTFILES_BRANCH}") -endif() -# Currently only use 2.3.0 CRTM coefficients -set( CRTM_COEFFS_BRANCH "2.3.0" ) - +############################################# # macro for downloading test data # options: # - tag 1.0.0: download from UCAR ftp, flat directory @@ -238,73 +311,100 @@ set( CRTM_COEFFS_BRANCH "2.3.0" ) # input: repo (e.g. ioda or ufo) # test_data: test data filename (archive) # output: output_data_path: path to data on local machine after download -# uses ${GIT_BRANCH_UFO} -function(ADD_DOWNLOAD_TEST repo test_data output_data_path) - # for 1.0.0 release always download from UCAR ftp, flat directory - if(${GIT_BRANCH_UFO} STREQUAL "1.0.0") - set(ECBUILD_DOWNLOAD_BASE_URL https://dashrepo.ucar.edu/dataset/147_miesch/file) - set(download_data_name ${test_data}) - set(data_path ${CMAKE_BINARY_DIR}/test_data/${repo}/1.0.0) - set(checksum "0") - # for all other branches/tags download from AWS - else() - set(ECBUILD_DOWNLOAD_BASE_URL https://jedi-test-files.s3.amazonaws.com) - # if branch-specific files exist, download them - ecbuild_check_urls(NAMES ${repo}/${GIT_BRANCH_UFO}/${test_data} - RESULT UFO_SPECIFIC_TEST_FILES) - if(${UFO_SPECIFIC_TEST_FILES} MATCHES 0) - set(download_data_name ${repo}/${GIT_BRANCH_UFO}/${test_data}) - set(data_path ${CMAKE_BINARY_DIR}/test_data/${repo}/${GIT_BRANCH_UFO}) - # if there are no branch-specific files, download develop files by default + +# Currently only use 2.3.0 CRTM coefficients +set( CRTM_COEFFS_BRANCH "2.3.0" ) + +function(ADD_DOWNLOAD_TEST repo test_files_dirname output_data_path) + # If REPO branch is being built set GIT_BRANCH_FUNC to REPO's current branch. + # If a tagged version of REPO is being built set GIT_TAG_FUNC to REPO's current tag. + find_branch_name(REPO_DIR_NAME ${repo}) + if( DEFINED GIT_BRANCH_FUNC ) + set( REPO_GIT_BRANCH ${GIT_BRANCH_FUNC} ) + elseif( DEFINED GIT_TAG_FUNC ) + set( REPO_GIT_BRANCH ${GIT_TAG_FUNC} ) + endif() + + if ( ${repo} STREQUAL "crtm" ) + set( GIT_TAG_FUNC ${CRTM_COEFFS_BRANCH} ) + endif() + + # When env veriable LOCAL_PATH_JEDI_TESTFILES is set, simply link test files + # to build directory. get_${repo}_test_data checks the existence of test file directory. + # ${repo} test data must be stored in + # ${LOCAL_PATH_JEDI_TESTFILES}/${repo}/${BRANCH}/testinput_tier_1 + if( DEFINED ENV{LOCAL_PATH_JEDI_TESTFILES} ) + set( LOCAL_PATH_JEDI_TESTFILES "$ENV{LOCAL_PATH_JEDI_TESTFILES}" ) + message( STATUS "use LOCAL_PATH_JEDI_TESTFILES: ${LOCAL_PATH_JEDI_TESTFILES}" ) + # If test data specific to testing branch exists locally use it. + # If not use test data specific to develop branch. + if( EXISTS ${LOCAL_PATH_JEDI_TESTFILES}/${repo}/${REPO_GIT_BRANCH} ) + set( TESTFILE_DIR_REPO "${LOCAL_PATH_JEDI_TESTFILES}/${repo}/${REPO_GIT_BRANCH}" ) + else() + set( TESTFILE_DIR_REPO "${LOCAL_PATH_JEDI_TESTFILES}/${repo}/develop" ) + endif() + + message( STATUS "Test data in ${TESTFILE_DIR_REPO} is linked to build directory" ) + list( APPEND REPO_DATA_DOWNLOADER_ARGS + ${TESTFILE_DIR_REPO} ) + set ( REPO_DATA_DOWNLOADER ufo_data_checker.py ) + + # Any tagged version of REPO is being built. + # Build ufo_get_{repo}_test_data test to download test data from DASH. + elseif( DEFINED GIT_TAG_FUNC) + message( STATUS "Tagged version of ${repo} is used" ) + # set ARGS for get_${repo}_test_data + set( ECBUILD_DOWNLOAD_BASE_URL https://dashrepo.ucar.edu/api/v1/dataset/147_miesch/version/1.1.0/file ) + set( DIRNAME ${GIT_TAG_FUNC} ) + set( checksum "0" ) + set( TESTFILE_DIR_REPO "${CMAKE_SOURCE_DIR}/test-data-release/${repo}/${DIRNAME}" ) + + # Create test-data-release in source directory + file( MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/test-data-release ) + + # temporary solution for release 1.1.0 + if ( ${repo} STREQUAL "crtm" ) + list( APPEND REPO_DATA_DOWNLOADER_ARGS + ${ECBUILD_DOWNLOAD_BASE_URL} + ${TESTFILE_DIR_REPO} + ${test_files_dirname} + ${checksum} ) + file( MAKE_DIRECTORY ${TESTFILE_DIR_REPO} ) else() - set(download_data_name ${repo}/develop/${test_data}) - set(data_path ${CMAKE_BINARY_DIR}/test_data/${repo}/develop) + list( APPEND REPO_DATA_DOWNLOADER_ARGS + ${ECBUILD_DOWNLOAD_BASE_URL} + ${CMAKE_SOURCE_DIR}/test-data-release + ${test_files_dirname} + ${checksum} ) endif() - set(checksum "1") + + set ( REPO_DATA_DOWNLOADER ufo_data_downloader.py ) + message( STATUS "Test data will be downloaded from: ${ECBUILD_DOWNLOAD_BASE_URL}" ) + + # Any branch of REPO is being built. + # repo-data repository is already cloned by bundle/CMakeLists.txt. + # Link test files in repo-data repo to build directory. + # get_repo_test_data checks the existence of test file directory + else() + set( TESTFILE_DIR_REPO ${CMAKE_SOURCE_DIR}/${repo}-data ) + list( APPEND REPO_DATA_DOWNLOADER_ARGS + ${TESTFILE_DIR_REPO} ) + set (REPO_DATA_DOWNLOADER ufo_data_checker.py ) endif() - message(STATUS "${repo} test data will be downloaded from ${ECBUILD_DOWNLOAD_BASE_URL}/${download_data_name}") - file(MAKE_DIRECTORY ${data_path}) # add test for downloading data - set(testname "ufo_get_${repo}_test_data") - ecbuild_add_test( TARGET ${testname} + ecbuild_add_test( TARGET ufo_get_${repo}_test_data TYPE SCRIPT - COMMAND ${CMAKE_BINARY_DIR}/bin/ufo_ioda_data_downloader.py - ARGS ${download_data_name} ${test_data} ${data_path} ${ECBUILD_DOWNLOAD_BASE_URL} ${checksum}) + COMMAND ${CMAKE_BINARY_DIR}/bin/${REPO_DATA_DOWNLOADER} + ARGS ${REPO_DATA_DOWNLOADER_ARGS} ) # pass output data path back to calling code - set(${output_data_path} ${data_path} PARENT_SCOPE) -endfunction() - -if( DEFINED ENV{LOCAL_PATH_JEDI_TESTFILES}) - set(LOCAL_PATH_JEDI_TESTFILES "$ENV{LOCAL_PATH_JEDI_TESTFILES}") -endif() - -# If local path to testfiles is defined don't download -if( DEFINED LOCAL_PATH_JEDI_TESTFILES ) - if(EXISTS ${LOCAL_PATH_JEDI_TESTFILES}/ioda/${GIT_BRANCH_UFO}) - set(UFO_IODA_TESTFILES_PATH ${LOCAL_PATH_JEDI_TESTFILES}/ioda/${GIT_BRANCH_UFO}) - else() - set(UFO_IODA_TESTFILES_PATH ${LOCAL_PATH_JEDI_TESTFILES}/ioda/develop) - endif() - message(STATUS "use LOCAL_PATH_JEDI_TESTFILES, ioda files path: ${UFO_IODA_TESTFILES_PATH}") - if(EXISTS ${LOCAL_PATH_JEDI_TESTFILES}/ufo//${GIT_BRANCH_UFO}) - set(UFO_UFO_TESTFILES_PATH ${LOCAL_PATH_JEDI_TESTFILES}/ufo/${GIT_BRANCH_UFO}) - else() - set(UFO_UFO_TESTFILES_PATH ${LOCAL_PATH_JEDI_TESTFILES}/ufo/develop) - endif() - message(STATUS "use LOCAL_PATH_JEDI_TESTFILES, ufo files path: ${UFO_UFO_TESTFILES_PATH}") - set(UFO_CRTM_COEFFS_PATH ${LOCAL_PATH_JEDI_TESTFILES}/crtm/${CRTM_COEFFS_BRANCH}) - message(STATUS "use LOCAL_PATH_JEDI_TESTFILES, crtm coeffs path: ${UFO_CRTM_COEFFS_PATH}") -# otherwise download the data -else() - message(STATUS "LOCAL_PATH_JEDI_TESTFILES is not defined, download test files") + set(${output_data_path} ${TESTFILE_DIR_REPO} PARENT_SCOPE) - # Create download script for get_ioda_test_data test - set ( FILENAME ufo_ioda_data_downloader.py) + # Create download script for ufo_get_*_test_data test + set ( FILENAME ${REPO_DATA_DOWNLOADER}) set ( SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME} ) set ( DEST_FILE ${CMAKE_BINARY_DIR}/bin/${FILENAME} ) - list( APPEND bin_ufo_ioda_test_download_scripts_deps ${DEST_FILE} ) if( EXISTS "${SOURCE_FILE}.in" ) configure_file( ${SOURCE_FILE}.in ${DEST_FILE} @ONLY ) @@ -312,52 +412,44 @@ else() configure_file( ${SOURCE_FILE} ${DEST_FILE} @ONLY ) endif() - add_custom_target( bin_ufo_ioda_test_download_scripts ALL - COMMAND chmod +x ${bin_ufo_ioda_test_download_scripts_deps} - DEPENDS ${bin_ufo_ioda_test_download_scripts_deps}) - - # add tests to download ioda & ufo data - ADD_DOWNLOAD_TEST( "ioda" ${ioda_test_data} UFO_IODA_TESTFILES_PATH ) - ADD_DOWNLOAD_TEST( "ufo" ${ufo_test_data} UFO_UFO_TESTFILES_PATH ) - - # download CRTM coefficients - if( crtm_FOUND ) - set(UFO_CRTM_COEFFS_PATH ${CMAKE_BINARY_DIR}/test_data/crtm/${CRTM_COEFFS_BRANCH}) - file(MAKE_DIRECTORY ${UFO_CRTM_COEFFS_PATH}) - # set filenames for downloading: flat directory for release (from UCAR ftp) - if(${GIT_BRANCH_UFO} STREQUAL "1.0.0") - set(ECBUILD_DOWNLOAD_BASE_URL https://dashrepo.ucar.edu/dataset/147_miesch/file) - set(crtm_download_data_name ${crtm_test_data}) - set(checksum_crtm "0") - # set filenames for downloading: use directories and branch names for everything else (AWS) - else() - set(ECBUILD_DOWNLOAD_BASE_URL https://jedi-test-files.s3.amazonaws.com) - set(crtm_download_data_name crtm/${CRTM_COEFFS_BRANCH}/${crtm_test_data}) - set(checksum_crtm "1") - endif() - ecbuild_add_test( TARGET ufo_get_crtm_test_data - TYPE SCRIPT - COMMAND ${CMAKE_BINARY_DIR}/bin/ufo_ioda_data_downloader.py - ARGS ${crtm_download_data_name} ${crtm_test_data} ${UFO_CRTM_COEFFS_PATH} ${ECBUILD_DOWNLOAD_BASE_URL} ${checksum_crtm}) - endif( crtm_FOUND ) -endif() -execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink - ${UFO_IODA_TESTFILES_PATH} - ${CMAKE_CURRENT_BINARY_DIR}/Data/ioda) + add_custom_target( bin_ufo_${repo}_test_download_scripts ALL + COMMAND chmod +x ${DEST_FILE} + DEPENDS ${DEST_FILE} ) + +endfunction() +############################################# +# add tests to download ufo & crtm data + +ADD_DOWNLOAD_TEST( "ufo" ${ufo_test_data} UFO_UFO_TESTFILES_PATH ) +ADD_DOWNLOAD_TEST( "crtm" ${crtm_test_data} UFO_CRTM_TESTFILES_PATH ) execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink ${UFO_UFO_TESTFILES_PATH} ${CMAKE_CURRENT_BINARY_DIR}/Data/ufo) - +execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink + ${UFO_CRTM_TESTFILES_PATH} + ${CMAKE_CURRENT_BINARY_DIR}/Data/crtm) ##################################################################### # Install headers used in tests of interfaces that may be implemented by clients. -install( FILES ufo/ObsDiagnostics.h ufo/ObsFilters.h ufo/ObsFunction.h +install( FILES ufo/ObsDiagnostics.h ufo/ObsFilters.h ufo/ObsFunction.h ufo/Predictor.h DESTINATION ${INSTALL_INCLUDE_DIR}/ufo/test/ufo) ##################################################################### +# Build executables used by multiple tests + +ecbuild_add_executable( TARGET test_ObsBias.x + SOURCES mains/TestObsBias.cc + LIBS ufo + ) + +ecbuild_add_executable( TARGET test_ObsDiagnostics.x + SOURCES mains/TestObsDiagnostics.cc + LIBS ufo + ) + ecbuild_add_executable( TARGET test_ObsOperator.x SOURCES mains/TestObsOperator.cc LIBS ufo @@ -373,11 +465,28 @@ ecbuild_add_executable( TARGET test_ObsFilters.x LIBS ufo ) +ecbuild_add_executable( TARGET test_ObsFunction.x + SOURCES mains/TestObsFunction.cc + LIBS ufo + ) + ecbuild_add_executable( TARGET test_ProfileConsistencyChecks.x SOURCES mains/TestProfileConsistencyChecks.cc LIBS ufo ) +ecbuild_add_executable( TARGET test_Predictor.x + SOURCES mains/TestPredictor.cc + LIBS ufo + ) + +#################################################################### +# Establish test tiering + +set( UFO_TEST_TIER 1 CACHE STRING "UFO test tier. Run tests up to and including this tier.") + +message(STATUS "Running ufo tests for tiers up to and including tier " ${UFO_TEST_TIER}) + #################################################################### # Test Locations, GeoVaLs with oops tests: @@ -385,15 +494,16 @@ ecbuild_add_test( TARGET test_ufo_geovals SOURCES mains/TestGeoVaLs.cc ARGS "testinput/geovals.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + LIBS ufo + MPI 2 + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_locations - SOURCES mains/TestLocations.cc + SOURCES mains/TestLocations.cc ufo/Locations.h ufo/locations_test.F90 ARGS "testinput/locations.yaml" ENVIRONMENT OOPS_TRAPFPE=1 LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) # Test UFO-specific classes: @@ -402,7 +512,7 @@ ecbuild_add_test( TARGET test_ufo_obsfilterdata ARGS "testinput/obsfilterdata.yaml" ENVIRONMENT OOPS_TRAPFPE=1 LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_variables SOURCES mains/TestVariables.cc @@ -415,7 +525,7 @@ ecbuild_add_test( TARGET test_ufo_geovals_spec ARGS "testinput/geovals_spec.yaml" ENVIRONMENT OOPS_TRAPFPE=1 LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) # Test Obs Operators and TLAD @@ -424,7 +534,7 @@ ecbuild_add_test( TARGET test_ufo_opr_gsi_sfc_model ARGS "testinput/gsisfcmodel.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) if( crtm_FOUND ) ecbuild_add_test( TARGET test_ufo_opr_crtm_amsua @@ -432,55 +542,98 @@ if( crtm_FOUND ) ARGS "testinput/amsua_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_crtm_bc_amsua COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/amsua_crtm_bc.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_amsua COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/amsua_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_bc_amsua COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + MPI 2 ARGS "testinput/amsua_crtm_bc.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_crtm_atms COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/atms_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_atms COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/atms_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ecbuild_add_test( TARGET test_ufo_opr_gmi_clw_ret + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/gmi_clw_ret.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ecbuild_add_test( TARGET test_ufo_opr_gmi_qc_filters + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/gmi_qc_filters.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ecbuild_add_test( TARGET test_ufo_ssmis_f17_gfs_backgroundcheck_bc_filters + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/ssmis_f17_gfs_backgroundcheck_bc.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data) + + ecbuild_add_test( TARGET test_ufo_ssmis_f17_gfs_backgroundcheck_nbc_filters + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/ssmis_f17_gfs_backgroundcheck_nbc.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data) ecbuild_add_test( TARGET test_ufo_opr_crtm_gmi COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/gmi_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_gmi COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/gmi_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ecbuild_add_test( TARGET test_ufo_opr_crtm_gmi_scan_angles + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/gmi_scan_angles.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ecbuild_add_test( TARGET test_ufo_linopr_crtm_gmi_scan_angles + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/gmi_scan_angles.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_crtm_smap @@ -488,56 +641,56 @@ if( crtm_FOUND ) ARGS "testinput/smap_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_smap COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/smap_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_crtm_seviri COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/seviri_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_seviri COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/seviri_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_crtm_cris COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/cris_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_cris COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/cris_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_crtm_mhs COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/mhs_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_mhs COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/mhs_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_crtm_iasi MPI 2 @@ -545,28 +698,28 @@ if( crtm_FOUND ) ARGS "testinput/iasi_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_iasi COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/iasi_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_crtm_sndrd1-4 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/sndrd1-4_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_sndrd1-4 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/sndrd1-4_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_crtm_airs MPI 4 @@ -574,95 +727,178 @@ if( crtm_FOUND ) ARGS "testinput/airs_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_airs COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/airs_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_crtm_hirs4 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/hirs4_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_hirs4 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/hirs4_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_crtm_abi_ahi COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/abi_ahi_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_crtm_abi_ahi COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/abi_ahi_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_opr_aod_crtm COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/aod_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_aod_crtm COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/aod_crtm.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - DEPENDS test_ObsOperatorTLAD.x + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ecbuild_add_test( TARGET test_ufo_opr_crtm_amsr2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/amsr2_crtm.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ecbuild_add_test( TARGET test_ufo_linopr_crtm_amsr2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/amsr2_crtm.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ecbuild_add_test( TARGET test_ufo_amsr2_clw_ret + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/amsr2_clw_ret.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ecbuild_add_test( TARGET test_ufo_amsr2_qc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/amsr2_qc.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ecbuild_add_test( TARGET test_ufo_amsr2_qc_halo + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/amsr2_qc_halo.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) - if( ${GEOS-AERO_FOUND} ) + if( ${geos-aero_FOUND} ) ecbuild_add_test( TARGET test_ufo_opr_aod_luts COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/aod_luts.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_aod_luts COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/aod_luts.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) - endif( ${GEOS-AERO_FOUND} ) + endif( ${geos-aero_FOUND} ) endif( crtm_FOUND ) -if( ${RTTOV_FOUND} ) +if( ${rttov_FOUND} ) + ecbuild_add_test( TARGET test_ufo_function_cloudcostfunction + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/function_cloudcost.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + ecbuild_add_test( TARGET test_ufo_opr_rttov_atms COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/atms_rttov_ops.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_rttov_atms COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/atms_rttov_ops.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - DEPENDS test_ObsOperatorTLAD.x + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) + + ecbuild_add_test( TARGET test_ufo_qc_atms_rttov + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/atms_rttov_qc.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + + ecbuild_add_test( TARGET test_ufo_atms_ops_qc_rttovonedvarcheck + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/atms_rttov_ops_qc_rttovonedvarcheck.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + TEST_DEPENDS ufo_get_ufo_test_data ) + + ecbuild_add_test( TARGET test_ufo_qc_amsr2_rttov + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/amsr2_rttov_qc.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + + ecbuild_add_test( TARGET test_ufo_amsr2_ops_qc_rttovonedvarcheck + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/amsr2_rttov_ops_qc_rttovonedvarcheck.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) -endif( ${RTTOV_FOUND} ) + + ecbuild_add_test( TARGET test_ufo_opr_rttovcpp_amsua + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/amsua_rttovcpp.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data ) + + ecbuild_add_test( TARGET test_ufo_linopr_rttovcpp_amsua + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/amsua_rttovcpp.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) +endif( ${rttov_FOUND} ) ecbuild_add_test( TARGET test_ufo_opr_atminterplay_gome_metop-a @@ -670,84 +906,201 @@ ecbuild_add_test( TARGET test_ufo_opr_atminterplay_gome_metop-a ARGS "testinput/gome_metop-a.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_gome_metop-a + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/gome_metop-a.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_atminterplay_gome_metop-a_flipz COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/gome_metop-a_flipz.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_gome_metop-a_flipz + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/gome_metop-a_flipz.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_atminterplay_omi_aura COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/omi_aura.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_omi_aura + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/omi_aura.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_atminterplay_omi_aura_flipz COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/omi_aura_flipz.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_opr_atminterp_ompslp_npp + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/ompslp_npp.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterp_ompslp_npp + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/ompslp_npp.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_omi_aura_flipz + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/omi_aura_flipz.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_atminterplay_ompsnp_npp + MPI 4 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/ompsnp_npp.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_ompsnp_npp + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/ompsnp_npp.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) + ecbuild_add_test( TARGET test_ufo_opr_atminterplay_ompsnp_npp_flipz + MPI 4 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/ompsnp_npp_flipz.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_ompsnp_npp_flipz + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/ompsnp_npp_flipz.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_atminterplay_ompsnp_npp_L127 + MPI 4 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/ompsnp_npp_L127.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_ompsnp_npp_L127 + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/ompsnp_npp_L127.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_opr_atminterplay_ompsnp_npp_qc_L127 + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/ompsnp_npp_qc_L127.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_atminterplay_ompstc_npp COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/ompstc_npp.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_ompstc_npp + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/ompstc_npp.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) + ecbuild_add_test( TARGET test_ufo_opr_atminterplay_ompstc_npp_flipz COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/ompstc_npp_flipz.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_ompstc_npp_flipz + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/ompstc_npp_flipz.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) + + ecbuild_add_test( TARGET test_ufo_opr_atminterplay_sbuv2_n19 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/sbuv2_n19.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_sbuv_n19 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/sbuv2_n19.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_atminterplay_sbuv2_n19_flipz COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/sbuv2_n19_flipz.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_sbuv2_n19_flipz + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/sbuv2_n19_flipz.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_atminterplay_sbuv2_n19_L127 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/sbuv2_n19_L127.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_atminterplay_sbuv2_n19_L127 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/sbuv2_n19_L127.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_vertinterp_radiosonde MPI 4 @@ -755,27 +1108,33 @@ ecbuild_add_test( TARGET test_ufo_opr_vertinterp_radiosonde ARGS "testinput/radiosonde.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_vertinterp_radiosonde + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/radiosonde.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_avgkernel_tropomi_no2 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/tropomi_no2.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_linopr_vertinterp_radiosonde +ecbuild_add_test( TARGET test_ufo_linopr_avgkernel_tropomi_no2 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x - ARGS "testinput/radiosonde.yaml" - ENVIRONMENT OOPS_TRAPFPE=1 + ARGS "testinput/tropomi_no2.yaml" DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_timeoper_opr COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/timeoper.yaml" DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_vertinterp_aircraft_opr MPI 4 @@ -783,14 +1142,14 @@ ecbuild_add_test( TARGET test_ufo_vertinterp_aircraft_opr ARGS "testinput/aircraft.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_vertinterp_aircraft COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/aircraft.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_vertinterp_satwind MPI 4 @@ -798,7 +1157,23 @@ ecbuild_add_test( TARGET test_ufo_linopr_vertinterp_satwind ARGS "testinput/satwind.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_vertinterp_satwind_metoffice + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/satwind_metoffice.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_opr_scatwindNeutralMetOffice + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/scatwind_neutral_metoffice.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_timeoper MPI 4 @@ -806,7 +1181,7 @@ ecbuild_add_test( TARGET test_ufo_opr_timeoper ARGS "testinput/timeoper.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_timeoper MPI 4 @@ -814,28 +1189,42 @@ ecbuild_add_test( TARGET test_ufo_linopr_timeoper ARGS "testinput/timeoper.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_opr_background_error_vert_interp + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/background_error_vert_interp.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data) + +ecbuild_add_test( TARGET test_ufo_opr_background_error_identity + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/background_error_identity.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_opr_gnssroRef COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/gnssroref.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_gnssroBndNBAM_0obs COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/gnssrobndnbam_0obs.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_gnssroBndNBAM_1obs COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/gnssrobndnbam_1obs.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_gnssroBndNBAM_1obs_2pe MPI 2 @@ -843,14 +1232,14 @@ ecbuild_add_test( TARGET test_ufo_opr_gnssroBndNBAM_1obs_2pe ARGS "testinput/gnssrobndnbam_1obs.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_gnssroRef COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/gnssroref.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_gnssroBndNBAM MPI 2 @@ -858,26 +1247,14 @@ ecbuild_add_test( TARGET test_ufo_opr_gnssroBndNBAM ARGS "testinput/gnssrobndnbam.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) - -ecbuild_add_test( TARGET test_ufo_qc_derivative_ascent - SOURCES mains/TestObsFilters.cc - ARGS "testinput/qc_derivative_dpdt.yaml" - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) - -ecbuild_add_test( TARGET test_ufo_qc_derivative_speed - SOURCES mains/TestObsFilters.cc - ARGS "testinput/qc_derivative_dxdt.yaml" - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_super_refraction_gnssroBndNBAM MPI 2 COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/gnssrobndnbam_super_refraction.yaml" DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_gnssroBndNBAM MPI 2 @@ -885,37 +1262,44 @@ ecbuild_add_test( TARGET test_ufo_linopr_gnssroBndNBAM ARGS "testinput/gnssrobndnbam.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_gnssroBendMetOffice_pseudo COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/gnssrobendmetoffice.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_gnssroBendMetOffice_pseudo COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/gnssrobendmetoffice.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_gnssroBendMetOffice + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/gnssrobendmetoffice_qc.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_gnssroBendMetOffice_nopseudo COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/gnssrobendmetoffice_nopseudo.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_gnssroBendMetOffice_nopseudo COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/gnssrobendmetoffice_nopseudo.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) -if( ${ROPP-UFO_FOUND} ) +if( ${ropp-ufo_FOUND} ) ecbuild_add_test( TARGET test_ufo_opr_gnssroBndROPP1D COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/gnssrobndropp1d.yaml" @@ -939,7 +1323,7 @@ ecbuild_add_test( TARGET test_ufo_linopr_gnssroBndROPP2D ARGS "testinput/gnssrobndropp2d.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x ) -endif( ${ROPP-UFO_FOUND} ) +endif( ${ropp-ufo_FOUND} ) #ecbuild_add_test( TARGET test_ufo_opr_windprof # COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x @@ -953,77 +1337,77 @@ ecbuild_add_test( TARGET test_ufo_opr_seaicefrac ARGS "testinput/seaicefrac.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_seaicefrac COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/seaicefrac.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_seaicethick COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/seaicethick.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_seaicethick COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/seaicethick.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_chleuzintegr COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/chleuzintegr.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_identity_sst COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/sea_surface_temp.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_identity_sst COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/sea_surface_temp.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_adt COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/adt.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_adt COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/adt.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_coolskin COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/coolskin.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_coolskin COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/coolskin.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) # METAR (sfc. obs) UFO tests ecbuild_add_test( TARGET test_ufo_metar_QC @@ -1031,67 +1415,112 @@ ecbuild_add_test( TARGET test_ufo_metar_QC ARGS "testinput/metar_qc_filters.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) -if( ${GSW_FOUND} ) +if( ${gsw_FOUND} ) ecbuild_add_test( TARGET test_ufo_opr_insitutemperature COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/tprof.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_insitutemperature COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/tprof.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_marinevertinterp COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/genericprof.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_linopr_marinevertinterp COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/genericprof.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperatorTLAD.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) -endif( ${GSW_FOUND} ) +endif( ${gsw_FOUND} ) -if( ${GEOS-AERO_FOUND} ) +if( ${geos-aero_FOUND} ) -ecbuild_add_test( TARGET test_ufo_geos_aero_opr +ecbuild_add_test( TARGET test_ufo_opr_geos_aero MPI 4 - SOURCES mains/TestObsOperator.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/geos_aod.yaml" - LIBS ufo ) + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_geos_aero_tlad - SOURCES mains/TestObsOperatorTLAD.cc +ecbuild_add_test( TARGET test_ufo_linopr_geos_aero + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x ARGS "testinput/geos_aod.yaml" - LIBS ufo ) + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) +endif( ${geos-aero_FOUND} ) -endif( ${GEOS-AERO_FOUND} ) +ecbuild_add_test( TARGET test_ufo_opr_aod_ext + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/aod_extinction.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data ) +ecbuild_add_test( TARGET test_ufo_linopr_aod_ext + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/aod_extinction.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_radarreflectivity COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/reflectivity.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_opr_radarradialvelocity COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/radialvelocity.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_opr_composite + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/composite.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_linopr_composite + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/composite.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_opr_categorical + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/categorical.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_opr_profile_average + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/profileconsistencychecks_opr_average.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperator.x TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) # Test UFO ObsFilters (generic) @@ -1103,128 +1532,245 @@ ecbuild_add_test( TARGET test_ufo_qc_gen_processwhere LIBS ufo TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_gen_backgroundcheck +ecbuild_add_test( TARGET test_ufo_qc_actions COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_backgroundcheck.yaml" + ARGS "testinput/qc_actions.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_gen_boundscheck +ecbuild_add_test( TARGET test_ufo_qc_gen_backgroundcheck COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_boundscheck.yaml" + ARGS "testinput/qc_backgroundcheck.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_velocitybounds +ecbuild_add_test( TARGET test_ufo_qc_gen_bayesianbackgroundcheck COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_velocitycheck.yaml" + ARGS "testinput/qc_bayesian_background_check.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_gen_defer_to_post +ecbuild_add_test( TARGET test_ufo_qc_gen_profilebackgroundcheck COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_defer_to_post.yaml" + ARGS "testinput/qc_profile_background_check.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_gen_differencecheck +ecbuild_add_test( TARGET test_ufo_qc_gen_profilefewobservationscheck COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_differencecheck.yaml" + ARGS "testinput/qc_profile_fewobs_check.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_thinning +ecbuild_add_test( TARGET test_ufo_satname COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_thinning.yaml" + ARGS "testinput/satname.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_gauss_thinning - MPI 4 +ecbuild_add_test( TARGET test_ufo_qc_gen_boundscheck COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_gauss_thinning.yaml" + ARGS "testinput/qc_boundscheck.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_met_office_buddy_check +ecbuild_add_test( TARGET test_ufo_qc_velocitybounds COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_met_office_buddy_check.yaml" + ARGS "testinput/qc_velocitycheck.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_oceancolor_preqc +ecbuild_add_test( TARGET test_ufo_qc_quotientcheck COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_oceancolor_preqc.yaml" + ARGS "testinput/unit_tests/function_obserrorfactorquotient.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_poisson_disk_thinning +ecbuild_add_test( TARGET test_ufo_qc_tropocheck COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_poisson_disk_thinning.yaml" + ARGS "testinput/unit_tests/function_tropo_estimate.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_preqc +ecbuild_add_test( TARGET test_ufo_qc_wdircheck COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_preqc.yaml" + ARGS "testinput/unit_tests/function_wdirdiff.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_qc_temporalthinning +ecbuild_add_test( TARGET test_ufo_qc_lam_domaincheck COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_temporal_thinning.yaml" + ARGS "testinput/unit_tests/function_lam_domaincheck.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ufo_test_data ) + TEST_DEPENDS ) -ecbuild_add_test( TARGET test_ufo_qc_trackcheck +ecbuild_add_test( TARGET test_ufo_qc_lam_domaincheck_circle COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/qc_trackcheck.yaml" + ARGS "testinput/unit_tests/function_lam_domaincheck_circle.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + TEST_DEPENDS ufo_get_test_data ) -ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_monolithicfilter +ecbuild_add_test( TARGET test_ufo_qc_derivative_ascent COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/profileconsistencychecks_monolithicfilter.yaml" + ARGS "testinput/qc_derivative_dpdt.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_separatefilters +ecbuild_add_test( TARGET test_ufo_qc_derivative_speed COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/profileconsistencychecks_separatefilters.yaml" + ARGS "testinput/qc_derivative_dxdt.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_unittests +ecbuild_add_test( TARGET test_ufo_qc_gen_defer_to_post COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/profileconsistencychecks_unittests.yaml" + ARGS "testinput/qc_defer_to_post.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_unittest_oneprofileMPI - MPI 2 +ecbuild_add_test( TARGET test_ufo_qc_gen_differencecheck COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/profileconsistencychecks_unittest_oneprofileMPI.yaml" + ARGS "testinput/qc_differencecheck.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) -ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_RH_obsfilter +ecbuild_add_test( TARGET test_ufo_qc_thinning + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_thinning.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_gauss_thinning + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_gauss_thinning.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_met_office_buddy_check + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_met_office_buddy_check.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_oceancolor_preqc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_oceancolor_preqc.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_poisson_disk_thinning + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_poisson_disk_thinning.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_preqc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_preqc.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_preqc_halo + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_preqc_halo.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_modelobthreshold + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_modelobthreshold.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_stuckcheck + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_stuckcheck.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_temporalthinning + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_temporal_thinning.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_trackcheck + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_trackcheck.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_trackcheckship + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_trackcheckship.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_monolithicfilter + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_monolithicfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_separatefilters + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_separatefilters.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_unittests + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_unittests.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_unittest_oneprofileMPI + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_unittest_oneprofileMPI.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_RH_obsfilter COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/profileconsistencychecks_RH_obsfilter.yaml" ENVIRONMENT OOPS_TRAPFPE=1 @@ -1238,6 +1784,13 @@ ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_UInterp_obsfilter DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_UInterpAlternative_obsfilter + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_UInterpAlternative_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_bkgqc_repobs_obsfilter COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/profileconsistencychecks_bkgqc_repobs_obsfilter.yaml" @@ -1245,6 +1798,13 @@ ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_bkgqc_repobs_obsfilt DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_bkgqc_repobs_separatefilters_obsfilter + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_bkgqc_repobs_separatefilters_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_bkgqc_modobs_obsfilter COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/profileconsistencychecks_bkgqc_modobs_obsfilter.yaml" @@ -1252,6 +1812,254 @@ ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_bkgqc_modobs_obsfilt DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ufo_test_data ) +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_bkgqc_modobs_separatefilters_obsfilter + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_bkgqc_modobs_separatefilters_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_sondeflags_obsfilter + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_sondeflags_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_winproflags_obsfilter + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_winproflags_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_pressure_obsfilter + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_pressure_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_function_satwindsLNVDcheck + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/unit_tests/function_satwindsLNVDcheck.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_function_satwindsSPDBcheck + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/unit_tests/function_satwindsSPDBcheck.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_variableassignment + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_variableassignment.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_variabletransforms_SondePressureFromHeight + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/unit_tests/variabletransforms_SondePressureFromHeight.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_variabletransforms_SondePressureFromHeightICAO + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/unit_tests/variabletransforms_SondePressureFromHeightICAO.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_average_pressure_obsfilter + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_average_pressure_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_average_temperature_obsfilter + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_average_temperature_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_average_windspeed_obsfilter + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_average_windspeed_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_average_relativehumidity_obsfilter + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_average_relativehumidity_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_acceptlist + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_acceptlist.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_monolithicfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_monolithicfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_separatefilters + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_separatefilters.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_unittests + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_unittests.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_RH_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_RH_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_UInterp_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_UInterp_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_UInterpAlternative_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_UInterpAlternative_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_bkgqc_repobs_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_bkgqc_repobs_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_bkgqc_repobs_separatefilters_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_bkgqc_repobs_separatefilters_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_bkgqc_modobs_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_bkgqc_modobs_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_bkgqc_modobs_separatefilters_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_bkgqc_modobs_separatefilters_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_sondeflags_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_sondeflags_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_winproflags_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_winproflags_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_pressure_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_pressure_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_average_pressure_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_average_pressure_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_average_temperature_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_average_temperature_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_average_windspeed_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_average_windspeed_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_average_relativehumidity_obsfilter + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/profileconsistencychecks_average_relativehumidity_obsfilter.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_performaction + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_performaction.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_qc_bayesianbackgroundqcflags + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/qc_bayesianbackgroundqcflags.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + # Test UFO ObsFilters (specific) ecbuild_add_test( TARGET test_ufo_gaussianthinning @@ -1265,11 +2073,25 @@ ecbuild_add_test( TARGET test_ufo_recursivesplitter SOURCES mains/TestRecursiveSplitter.cc # This test doesn't need a configuration file, but oops::Run::Run() requires # a path to a configuration file to be passed in the first command-line parameter. - ARGS "testinput/qc_recursive_splitter.yaml" + ARGS "testinput/empty.yaml" ENVIRONMENT OOPS_TRAPFPE=1 LIBS ufo TEST_DEPENDS ufo_get_ufo_test_data ) +ecbuild_add_test( TARGET test_ufo_qc_stuckcheck_unittests + SOURCES mains/TestStuckCheck.cc + ARGS "testinput/qc_stuckcheck_unittests.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data) + +ecbuild_add_test( TARGET test_ufo_qc_historycheck_unittests + SOURCES mains/TestHistoryCheck.cc + ARGS "testinput/qc_historycheck_unittests.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data) + ecbuild_add_test( TARGET test_ufo_temporalthinning SOURCES mains/TestTemporalThinning.cc ARGS "testinput/qc_temporal_thinning_unittests.yaml" @@ -1277,6 +2099,13 @@ ecbuild_add_test( TARGET test_ufo_temporalthinning LIBS ufo TEST_DEPENDS ufo_get_ufo_test_data) +ecbuild_add_test( TARGET test_ufo_obserror_assign_unittests + SOURCES mains/TestObsErrorAssign.cc + ARGS "testinput/obserror_assign_unittests.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data) + ecbuild_add_test( TARGET test_ufo_qc_trackcheck_unittests SOURCES mains/TestTrackCheck.cc ARGS "testinput/qc_trackcheck_unittests.yaml" @@ -1305,6 +2134,14 @@ ecbuild_add_test( TARGET test_ufo_qc_poisson_disk_thinning_unittests LIBS ufo TEST_DEPENDS ufo_get_ufo_test_data) +ecbuild_add_test( TARGET test_ufo_qc_poisson_disk_thinning_parallel + MPI 2 + SOURCES mains/TestParallelPoissonDiskThinning.cc + ARGS "testinput/qc_poisson_disk_thinning_parallel.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data) + ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_OPScomparison COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x ARGS "testinput/profileconsistencychecks_OPScomparison.yaml" @@ -1347,6 +2184,62 @@ ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_UInterp_OPScompariso DEPENDS test_ProfileConsistencyChecks.x TEST_DEPENDS ufo_get_ufo_test_data ) +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_UInterpAlternative_OPScomparison + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_UInterpAlternative_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_sondeflags_OPScomparison + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_sondeflags_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_winproflags_OPScomparison + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_winproflags_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_pressure_OPScomparison + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_pressure_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_unittests_vertaverage + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_unittests_vertaverage.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_unittests_vertaverage_ascending + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_unittests_vertaverage_ascending.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_unittests_vertinterp + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_unittests_vertinterp.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_unittests_profiledataholder + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_unittests_profiledataholder.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + ecbuild_add_test( TARGET test_ufo_qc_met_office_buddy_check_unittests SOURCES mains/TestMetOfficeBuddyCheck.cc ARGS "testinput/qc_met_office_buddy_check_unittests.yaml" @@ -1361,305 +2254,552 @@ ecbuild_add_test( TARGET test_ufo_qc_met_office_buddy_pair_finder LIBS ufo TEST_DEPENDS ufo_get_ufo_test_data) +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_average_pressure_OPScomparison + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_average_pressure_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_average_temperature_OPScomparison + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_average_temperature_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_average_windspeed_OPScomparison + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_average_windspeed_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_average_relativehumidity_OPScomparison + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_average_relativehumidity_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_bkgqc_modobs_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_bkgqc_modobs_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_bkgqc_repobs_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_bkgqc_repobs_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_RH_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_RH_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_UInterp_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_UInterp_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_sondeflags_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_sondeflags_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_winproflags_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_winproflags_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_pressure_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_pressure_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_average_pressure_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_average_pressure_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_average_temperature_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_average_temperature_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_average_windspeed_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_average_windspeed_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_profileconsistencychecks_MPI_average_relativehumidity_OPScomparison + MPI 2 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ProfileConsistencyChecks.x + ARGS "testinput/profileconsistencychecks_average_relativehumidity_OPScomparison.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ProfileConsistencyChecks.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +# Test Variable Transforms + +ecbuild_add_test( TARGET test_ufo_variabletransforms_windcomponents + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/unit_tests/variabletransforms_windcomponents.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_variabletransforms_windspeedanddirection + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/unit_tests/variabletransforms_windspeedanddirection.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_variabletransforms_humidityrelative + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/unit_tests/variabletransforms_rhumidity.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_variabletransforms_humidityspecific + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/unit_tests/variabletransforms_shumidity.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + # Test Functions +ecbuild_add_test( TARGET test_ufo_function_bgddepartureanomaly + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x + ARGS "testinput/function_bgddepartureanomaly.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_function_bennartzscatindex + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x + ARGS "testinput/function_bennartzscatindex.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data ) + ecbuild_add_test( TARGET test_ufo_function_clouddetect_iasi - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clouddetect_iasi.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_function_clouddetect_airs - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clouddetect_airs.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_clouddetect_cris - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clouddetect_cris.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_clwmatchidx - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clwmatchidx.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_clwmatchidx_atms - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clwmatchidx_atms.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_clwret - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clwret.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_clwret_atms - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clwret_atms.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) + +ecbuild_add_test( TARGET test_ufo_function_clwret_ssmis + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x + ARGS "testinput/unit_tests/function_clwret_ssmis.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_clwretmean - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clwretmean.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_clwretmean_atms - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clwretmean_atms.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_clwret_hofx - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clwret_hofx.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_clwret_hofx_atms - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clwret_hofx_atms.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_clwret_obsval - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clwret_obsval.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_clwret_obsval_atms - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_clwret_obsval_atms.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_errfgrosschk - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_errfgrosschk.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_errfjsfc - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_errfjsfc.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_errflat - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_errflat.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_errftopo - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_errftopo.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_errftransmittop - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_errftransmittop.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_errfwavenum - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_errfwavenum.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_hydrometeorchk - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_hydrometeorchk.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_hydrometeorchk_atms - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_hydrometeorchk_atms.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_nsstret - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_nsstret.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_obserrmean - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_obserrmean.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) -#ecbuild_add_test( TARGET test_ufo_function_obserrmean_abi -# SOURCES mains/TestObsFunction.cc -# ARGS "testinput/function_obserrmean_abi.yaml" -# ENVIRONMENT OOPS_TRAPFPE=1 -# LIBS ufo -# TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) +ecbuild_add_test( TARGET test_ufo_function_obserrmean_abi + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x + ARGS "testinput/function_obserrmean_abi.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_obserrmean_mhs - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_obserrmean_mhs.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_obserrmean_atms - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_obserrmean_atms.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) + +ecbuild_add_test( TARGET test_ufo_function_satwind_indiv_errors + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/function_satwind_indiv_errors.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) ecbuild_add_test( TARGET test_ufo_function_scatret - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_scatret.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_scatret_atms - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_scatret_atms.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_errfsdoei - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_errfsdoei.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_errfsdoei_atms - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_errfsdoei_atms.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_function_velocity - SOURCES mains/TestObsFunction.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x ARGS "testinput/function_velocity.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) + +ecbuild_add_test( TARGET test_ufo_function_obserrormodelstepwiselinear + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x + ARGS "testinput/function_obserrorstepwiselinear.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data) + +ecbuild_add_test( TARGET test_ufo_function_obserrorinflatesfcp + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x + ARGS "testinput/function_obserrorinflatesfcp.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_function_obserrorfactorconv.yaml + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x + ARGS "testinput/function_obserrorfactorconv.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFunction.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_function_linear_combination + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFunction.x + ARGS "testinput/function_reperr.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFunction.x + TEST_DEPENDS test_ufo_function_representation_err_synthetic_data) # Test Diagnostics +ecbuild_add_test( TARGET test_ufo_obsdiag_background_error_vert_interp_air_pressure + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x + ARGS "testinput/obsdiag_background_error_vert_interp_air_pressure.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_obsdiag_background_error_vert_interp_height + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x + ARGS "testinput/obsdiag_background_error_vert_interp_height.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_obsdiag_background_error_identity + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x + ARGS "testinput/obsdiag_background_error_identity.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_obsdiag_background_error_upper_air_and_surface_variables + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x + ARGS "testinput/obsdiag_background_error_upper_air_and_surface_variables.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ) + if( crtm_FOUND ) ecbuild_add_test( TARGET test_ufo_obsdiag_crtm_airs_optics - SOURCES mains/TestObsDiagnostics.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x ARGS "testinput/obsdiag_crtm_airs_optics.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_obsdiag_crtm_airs_jacobian - SOURCES mains/TestObsDiagnostics.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x ARGS "testinput/obsdiag_crtm_airs_jacobian.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_obsdiag_crtm_amsua_optics - SOURCES mains/TestObsDiagnostics.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x ARGS "testinput/obsdiag_crtm_amsua_optics.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_obsdiag_crtm_amsua_jacobian - SOURCES mains/TestObsDiagnostics.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x ARGS "testinput/obsdiag_crtm_amsua_jacobian.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_obsdiag_crtm_atms_optics - SOURCES mains/TestObsDiagnostics.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x ARGS "testinput/obsdiag_crtm_atms_optics.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_obsdiag_crtm_atms_jacobian - SOURCES mains/TestObsDiagnostics.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x ARGS "testinput/obsdiag_crtm_atms_jacobian.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_obsdiag_crtm_cris_optics - SOURCES mains/TestObsDiagnostics.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x ARGS "testinput/obsdiag_crtm_cris_optics.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_obsdiag_crtm_cris_jacobian - SOURCES mains/TestObsDiagnostics.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x ARGS "testinput/obsdiag_crtm_cris_jacobian.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_obsdiag_crtm_iasi_optics - SOURCES mains/TestObsDiagnostics.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x ARGS "testinput/obsdiag_crtm_iasi_optics.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_obsdiag_crtm_iasi_jacobian - SOURCES mains/TestObsDiagnostics.cc + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsDiagnostics.x ARGS "testinput/obsdiag_crtm_iasi_jacobian.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + DEPENDS test_ObsDiagnostics.x + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) endif( crtm_FOUND ) # Test Parameters -ecbuild_add_test( TARGET test_ufo_parameters - SOURCES mains/TestParameters.cc - ARGS "testinput/parameters.yaml" - ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo) +if (eckit_VERSION VERSION_GREATER_EQUAL 1.16) + ecbuild_add_test( TARGET test_ufo_parameters + SOURCES mains/TestParameters.cc + ARGS "testinput/parameters.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + LIBS ufo) +else() + # Due to changes in eckit 1.16 a different input file is required. + ecbuild_add_test( TARGET test_ufo_parameters + SOURCES mains/TestParameters.cc + ARGS "testinput/parameters_older_eckit.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + LIBS ufo) +endif() # Test piecewise linear interpolation ecbuild_add_test( TARGET test_ufo_piecewise_linear_interpolation @@ -1668,14 +2808,26 @@ ecbuild_add_test( TARGET test_ufo_piecewise_linear_interpolation ENVIRONMENT OOPS_TRAPFPE=1 LIBS ufo) -# Test parallel observation distribution -ecbuild_add_test( TARGET test_ufo_parallel_obs_distribution - MPI 4 - SOURCES mains/TestParallelObsDistribution.cc - ARGS "testinput/parallel_obs_distribution.yaml" +# Test operator utils +ecbuild_add_test( TARGET test_ufo_operator_utils + SOURCES mains/TestOperatorUtils.cc + ARGS "testinput/operatorutils.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + LIBS ufo) + +# Test the iterator over primitive variables and their values +ecbuild_add_test( TARGET test_ufo_primitive_variables + SOURCES mains/TestPrimitiveVariables.cc + ARGS "testinput/primitive_variables.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + LIBS ufo) + +# Test the met office radiance read in +ecbuild_add_test( TARGET test_ufo_metoffice_error_matrices + SOURCES mains/TestMetOfficeRadianceErrorMatrices.cc + ARGS "testinput/metoffice_radiance_error_matrices.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + LIBS ufo) # Test QC for specific instruments @@ -1685,208 +2837,282 @@ if( crtm_FOUND ) ARGS "testinput/airs_qc_filters.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_amsua COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/amsua_qc.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_amsua_clwretmw COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/amsua_qc_clwretmw.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_amsua_filters COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/amsua_qc_filters.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_amsua_miss_val COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/amsua_qc_miss_val.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_atms_filters COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/atms_qc_filters.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_cris COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/cris_qc.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_cris_filters COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/cris_qc_filters.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_cris_land COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/cris_qc_land.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_function_scattering COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/amsua_qc.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_iasi COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/iasi_qc.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_iasi_filters COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/iasi_qc_filters.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) ecbuild_add_test( TARGET test_ufo_qc_amsua_allsky_gsi COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/amsua_allsky_gsi_qc.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ufo_get_crtm_test_data ) + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) endif( crtm_FOUND ) -if( ${ROPP-UFO_FOUND} ) +if( ${ropp-ufo_FOUND} ) ecbuild_add_test( TARGET test_ufo_qc_gnssroBndROPP1D COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x - ARGS "testinput/gnssrobndropp1d.yaml" + ARGS "testinput/gnssrobndropp1d_qc.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x ) -endif( ${ROPP-UFO_FOUND} ) +endif( ${ropp-ufo_FOUND} ) ecbuild_add_test( TARGET test_ufo_opr_sfc_p COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x ARGS "testinput/sfcpcorrected.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsOperator.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_qc_amsua_seaice COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/amsua_seaice_qc.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_qc_gnssro_obs_error COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/gnssro_obs_error.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data) + +ecbuild_add_test( TARGET test_ufo_qc_gnssro_obs_error_halo + MPI 4 + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/gnssro_obs_error_halo.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) +ecbuild_add_test( TARGET test_ufo_qc_gnssroBendMetOffice_obs_error + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/gnssrobendmetoffice_obserror.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +if( ${ropp-ufo_FOUND} ) +ecbuild_add_test( TARGET test_ufo_qc_gnssro_obs_error_ROPP + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "testinput/gnssro_obs_error_ropp.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsFilters.x ) +endif( ${ropp-ufo_FOUND} ) + ecbuild_add_test( TARGET test_ufo_qc_gnssro_domain_check COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ARGS "testinput/gnssro_domain_check.yaml" ENVIRONMENT OOPS_TRAPFPE=1 DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_qc_gnssro_backgroundcheck COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ENVIRONMENT OOPS_TRAPFPE=1 ARGS "testinput/gnssrobndnbam_backgroundcheck_qc.yaml" DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + TEST_DEPENDS ufo_get_ufo_test_data) ecbuild_add_test( TARGET test_ufo_qc_action_gnssroBndNBAM_obserr_inflation COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x ENVIRONMENT OOPS_TRAPFPE=1 ARGS "testinput/gnssrobndnbam_qc_action_obserr_inflation.yaml" DEPENDS test_ObsFilters.x - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + TEST_DEPENDS ufo_get_ufo_test_data) # Test bias correction classes -ecbuild_add_test( TARGET test_ufo_bias_airs - SOURCES mains/TestObsBias.cc - ARGS "testinput/airs_crtm_bc.yaml" +ecbuild_add_test( TARGET test_ufo_bias_coeffs + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsBias.x + ARGS "testinput/bias_coeff.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsBias.x + TEST_DEPENDS ufo_get_ufo_test_data) -ecbuild_add_test( TARGET test_ufo_bias_amsua - SOURCES mains/TestObsBias.cc - ARGS "testinput/amsua_crtm_bc.yaml" +ecbuild_add_test( TARGET test_ufo_bias_coeff_increment + SOURCES mains/TestObsBiasIncrement.cc + ARGS "testinput/bias_coeff_cov.yaml" ENVIRONMENT OOPS_TRAPFPE=1 LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + TEST_DEPENDS ufo_get_ufo_test_data) -ecbuild_add_test( TARGET test_ufo_bias_atms - SOURCES mains/TestObsBias.cc - ARGS "testinput/atms_crtm_bc.yaml" +ecbuild_add_test( TARGET test_ufo_bias_covariance_amsua + SOURCES mains/TestObsBiasCovariance.cc + ARGS "testinput/bias_coeff_cov.yaml" ENVIRONMENT OOPS_TRAPFPE=1 LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + TEST_DEPENDS ufo_get_ufo_test_data) -ecbuild_add_test( TARGET test_ufo_bias_cris - SOURCES mains/TestObsBias.cc - ARGS "testinput/cris_crtm_bc.yaml" +ecbuild_add_test( TARGET test_ufo_bias_covariance_details + SOURCES mains/TestObsBiasCovarianceDetails.cc + ARGS "testinput/bias_coeff_cov.yaml" ENVIRONMENT OOPS_TRAPFPE=1 LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + TEST_DEPENDS ufo_get_ufo_test_data) -ecbuild_add_test( TARGET test_ufo_bias_iasi - SOURCES mains/TestObsBias.cc - ARGS "testinput/iasi_crtm_bc.yaml" +ecbuild_add_test( TARGET test_ufo_bias_single_channel + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/bias_single_channel.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data) -ecbuild_add_test( TARGET test_ufo_bias_increment_amsua - SOURCES mains/TestObsBiasIncrement.cc - ARGS "testinput/amsua_crtm_bc.yaml" +ecbuild_add_test( TARGET test_ufo_bias_lin_op + SOURCES mains/TestLinearObsBiasOperator.cc + ARGS "testinput/bias_linear_op.yaml" ENVIRONMENT OOPS_TRAPFPE=1 LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + TEST_DEPENDS ufo_get_ufo_test_data) -ecbuild_add_test( TARGET test_ufo_bias_covariance_amsua - SOURCES mains/TestObsBiasCovariance.cc - ARGS "testinput/amsua_crtm_bc.yaml" +ecbuild_add_test( TARGET test_ufo_opr_groundgnssMetOffice + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/groundgnssmetoffice.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data) -ecbuild_add_test( TARGET test_ufo_bias_covariance_details - SOURCES mains/TestObsBiasCovarianceDetails.cc - ARGS "testinput/amsua_crtm_bc.yaml" + +ecbuild_add_test( TARGET test_ufo_opr_groundgnssROPP + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "testinput/groundgnssropp.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo - TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data) + DEPENDS test_ObsOperator.x + TEST_DEPENDS ufo_get_ufo_test_data) -ecbuild_add_test( TARGET test_ufo_opr_groundgnssMetOffice +ecbuild_add_test( TARGET test_ufo_linopr_groundgnssMetOffice + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/groundgnssmetoffice.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_ufo_opr_sattcwv COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x - ARGS "testinput/groundgnssmetoffice.yaml" + ARGS "testinput/sattcwv.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - DEPENDS test_ObsOperator.x + DEPENDS test_ObsOperator.x TEST_DEPENDS ufo_get_ufo_test_data) + +ecbuild_add_test( TARGET test_ufo_linopr_sattcwv + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperatorTLAD.x + ARGS "testinput/sattcwv.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_ObsOperatorTLAD.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) +# Test predictors +ecbuild_add_test( TARGET test_constant_predictor + COMMAND ${CMAKE_BINARY_DIR}/bin/test_Predictor.x + ARGS "testinput/constant_predictor.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_Predictor.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_orbital_angle_predictor + COMMAND ${CMAKE_BINARY_DIR}/bin/test_Predictor.x + ARGS "testinput/orbital_angle_predictor.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_Predictor.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_legendre_predictor + COMMAND ${CMAKE_BINARY_DIR}/bin/test_Predictor.x + ARGS "testinput/legendre_predictor.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_Predictor.x + TEST_DEPENDS ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_thickness_predictor + COMMAND ${CMAKE_BINARY_DIR}/bin/test_Predictor.x + ARGS "testinput/thickness_predictor.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_Predictor.x + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + +ecbuild_add_test( TARGET test_interpolate_data_from_file_predictor + COMMAND ${CMAKE_BINARY_DIR}/bin/test_Predictor.x + ARGS "testinput/interpolate_data_from_file_predictor.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + DEPENDS test_Predictor.x + TEST_DEPENDS ufo_get_ufo_test_data ) + ##################################################################### # Files for CRTM tests ##################################################################### @@ -1907,18 +3133,44 @@ EmisCoeff/IR_Land/SEcategory/Little_Endian/USGS.IRland.EmisCoeff.bin EmisCoeff/VIS_Land/SEcategory/Little_Endian/USGS.VISland.EmisCoeff.bin SpcCoeff/Little_Endian/hirs4_metop-a.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/hirs4_metop-a.TauCoeff.bin +SpcCoeff/Little_Endian/amsr2_gcom-w1.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/amsr2_gcom-w1.TauCoeff.bin +SpcCoeff/Little_Endian/amsua_aqua.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/amsua_aqua.TauCoeff.bin +SpcCoeff/Little_Endian/amsua_metop-a.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/amsua_metop-a.TauCoeff.bin +SpcCoeff/Little_Endian/amsua_metop-b.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/amsua_metop-b.TauCoeff.bin +SpcCoeff/Little_Endian/amsua_metop-c.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/amsua_metop-c.TauCoeff.bin +SpcCoeff/Little_Endian/amsua_n15.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/amsua_n15.TauCoeff.bin +SpcCoeff/Little_Endian/amsua_n18.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/amsua_n18.TauCoeff.bin SpcCoeff/Little_Endian/amsua_n19.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/amsua_n19.TauCoeff.bin SpcCoeff/Little_Endian/atms_npp.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/atms_npp.TauCoeff.bin +SpcCoeff/Little_Endian/atms_n20.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/atms_n20.TauCoeff.bin SpcCoeff/Little_Endian/gmi_gpm.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/gmi_gpm.TauCoeff.bin SpcCoeff/Little_Endian/seviri_m08.SpcCoeff.bin TauCoeff/ODAS/Little_Endian/seviri_m08.TauCoeff.bin +SpcCoeff/Little_Endian/seviri_m11.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/seviri_m11.TauCoeff.bin SpcCoeff/Little_Endian/cris-fsr_npp.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/cris-fsr_npp.TauCoeff.bin SpcCoeff/Little_Endian/iasi_metop-a.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/iasi_metop-a.TauCoeff.bin +SpcCoeff/Little_Endian/iasi_metop-b.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/iasi_metop-b.TauCoeff.bin +SpcCoeff/Little_Endian/mhs_metop-a.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/mhs_metop-a.TauCoeff.bin +SpcCoeff/Little_Endian/mhs_metop-b.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/mhs_metop-b.TauCoeff.bin +SpcCoeff/Little_Endian/mhs_metop-c.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/mhs_metop-c.TauCoeff.bin SpcCoeff/Little_Endian/mhs_n19.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/mhs_n19.TauCoeff.bin SpcCoeff/Little_Endian/sndrD1_g15.SpcCoeff.bin @@ -1929,6 +3181,8 @@ SpcCoeff/Little_Endian/sndrD3_g15.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/sndrD3_g15.TauCoeff.bin SpcCoeff/Little_Endian/sndrD4_g15.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/sndrD4_g15.TauCoeff.bin +SpcCoeff/Little_Endian/ssmis_f17.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/ssmis_f17.TauCoeff.bin SpcCoeff/Little_Endian/airs_aqua.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/airs_aqua.TauCoeff.bin SpcCoeff/Little_Endian/v.viirs-m_npp.SpcCoeff.bin @@ -1939,11 +3193,17 @@ SpcCoeff/Little_Endian/ahi_himawari8.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/ahi_himawari8.TauCoeff.bin SpcCoeff/Little_Endian/radiometer_smap.SpcCoeff.bin TauCoeff/ODPS/Little_Endian/radiometer_smap.TauCoeff.bin +SpcCoeff/Little_Endian/avhrr3_metop-a.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/avhrr3_metop-a.TauCoeff.bin +SpcCoeff/Little_Endian/avhrr3_n18.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/avhrr3_n18.TauCoeff.bin +SpcCoeff/Little_Endian/cris-fsr_n20.SpcCoeff.bin +TauCoeff/ODPS/Little_Endian/cris-fsr_n20.TauCoeff.bin ) if( crtm_FOUND ) # symlink to selected CRTM coefficient files. - create_symlink_filename( ${UFO_CRTM_COEFFS_PATH} + create_symlink_filename( ${UFO_CRTM_TESTFILES_PATH} ${CMAKE_CURRENT_BINARY_DIR}/Data ${crtm_test_input} ) endif() @@ -1951,23 +3211,25 @@ endif() ##################################################################### # Files for RTTOV tests ##################################################################### -if( ${RTTOV_FOUND} ) +if( ${rttov_FOUND} ) list( APPEND rttov_test_input rttov7pred54L/rtcoef_noaa_19_amsua.dat rttov7pred54L/rtcoef_noaa_20_atms.dat +rttov7pred54L/rtcoef_dmsp_17_ssmis.dat +rttov7pred54L/rtcoef_gcom-w_1_amsr2.dat ) # Symlink all CRTM files CREATE_SYMLINK_FILENAME( ${rttov_SOURCE_DIR}/rtcoef_rttov12 ${CMAKE_CURRENT_BINARY_DIR}/Data ${rttov_test_input} ) -endif( ${RTTOV_FOUND} ) +endif( ${rttov_FOUND} ) ###### #Files for geos-aero tests ###### -if( ${GEOS-AERO_FOUND} ) +if( ${geos-aero_FOUND} ) list( APPEND geos-aero_test_data Data/optics_BC.v1_3_.nc Data/optics_BRC.v1_5_.nc @@ -1976,18 +3238,20 @@ Data/optics_NI.v2_5_.nc Data/optics_OC.v1_3_.nc Data/optics_SS.v3_3_.nc Data/optics_SU.v1_3_.nc -) +) CREATE_SYMLINK_FILENAME( ${geos-aero_SOURCE_DIR}/test/ - ${CMAKE_CURRENT_BINARY_DIR}/Data + ${CMAKE_CURRENT_BINARY_DIR}/Data ${geos-aero_test_data} ) list( APPEND geos-aero_test_input testinput/geosaod.rc -testinput/Chem_MieRegistry.rc) +testinput/Chem_MieRegistry.rc) CREATE_SYMLINK_FILENAME( ${geos-aero_SOURCE_DIR}/test/ ${CMAKE_CURRENT_BINARY_DIR} ${geos-aero_test_input} ) -endif( ${GEOS-AERO_FOUND} ) +endif( ${geos-aero_FOUND} ) + +add_subdirectory(testinput/instrumentTests) diff --git a/test/mains/TestHistoryCheck.cc b/test/mains/TestHistoryCheck.cc new file mode 100644 index 000000000..009deaf07 --- /dev/null +++ b/test/mains/TestHistoryCheck.cc @@ -0,0 +1,13 @@ +/* + * (C) 2021 Crown Copyright Met Office. All rights reserved. + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ +#include "../ufo/HistoryCheck.h" +#include "oops/runs/Run.h" + +int main(int argc, char ** argv) { + oops::Run run(argc, argv); + ufo::test::HistoryCheck tests; + return run.execute(tests); +} diff --git a/test/mains/TestLinearObsBiasOperator.cc b/test/mains/TestLinearObsBiasOperator.cc new file mode 100644 index 000000000..cf13bfe1a --- /dev/null +++ b/test/mains/TestLinearObsBiasOperator.cc @@ -0,0 +1,16 @@ +/* + * (C) Crown copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "../ufo/LinearObsBiasOperator.h" +#include "oops/runs/Run.h" +#include "ufo/ObsTraits.h" + +int main(int argc, char ** argv) { + oops::Run run(argc, argv); + ufo::test::LinearObsBiasOperator tests; + return run.execute(tests); +} diff --git a/test/mains/TestMetOfficeRadianceErrorMatrices.cc b/test/mains/TestMetOfficeRadianceErrorMatrices.cc new file mode 100644 index 000000000..d70894f09 --- /dev/null +++ b/test/mains/TestMetOfficeRadianceErrorMatrices.cc @@ -0,0 +1,15 @@ +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "../ufo/MetOfficeRadianceErrorMatrices.h" +#include "oops/runs/Run.h" + +int main(int argc, char ** argv) { + oops::Run run(argc, argv); + ufo::test::MetOfficeRadianceErrorMatrices tests; + return run.execute(tests); +} diff --git a/test/mains/TestObsErrorAssign.cc b/test/mains/TestObsErrorAssign.cc new file mode 100644 index 000000000..74d8e882c --- /dev/null +++ b/test/mains/TestObsErrorAssign.cc @@ -0,0 +1,15 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "../ufo/ObsErrorAssign.h" +#include "oops/runs/Run.h" + +int main(int argc, char ** argv) { + oops::Run run(argc, argv); + ufo::test::ObsErrorAssign tests; + return run.execute(tests); +} diff --git a/test/mains/TestOperatorUtils.cc b/test/mains/TestOperatorUtils.cc new file mode 100644 index 000000000..6b2fe2edb --- /dev/null +++ b/test/mains/TestOperatorUtils.cc @@ -0,0 +1,15 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "../ufo/OperatorUtils.h" +#include "oops/runs/Run.h" + +int main(int argc, char ** argv) { + oops::Run run(argc, argv); + ufo::test::OperatorUtils tests; + return run.execute(tests); +} diff --git a/test/mains/TestParallelPoissonDiskThinning.cc b/test/mains/TestParallelPoissonDiskThinning.cc new file mode 100644 index 000000000..85105a015 --- /dev/null +++ b/test/mains/TestParallelPoissonDiskThinning.cc @@ -0,0 +1,15 @@ +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "../ufo/ParallelPoissonDiskThinning.h" +#include "oops/runs/Run.h" + +int main(int argc, char ** argv) { + oops::Run run(argc, argv); + ufo::test::ParallelPoissonDiskThinning tests; + return run.execute(tests); +} diff --git a/test/mains/TestPredictor.cc b/test/mains/TestPredictor.cc new file mode 100644 index 000000000..9b682ad37 --- /dev/null +++ b/test/mains/TestPredictor.cc @@ -0,0 +1,15 @@ +/* + * (C) Crown copyright 2021 Met Office. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "../ufo/Predictor.h" +#include "oops/runs/Run.h" + +int main(int argc, char ** argv) { + oops::Run run(argc, argv); + ufo::test::Predictor tests; + return run.execute(tests); +} diff --git a/test/mains/TestPrimitiveVariables.cc b/test/mains/TestPrimitiveVariables.cc new file mode 100644 index 000000000..38d924f65 --- /dev/null +++ b/test/mains/TestPrimitiveVariables.cc @@ -0,0 +1,15 @@ +/* + * (C) Copyright 2021 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#include "../ufo/PrimitiveVariables.h" +#include "oops/runs/Run.h" + +int main(int argc, char ** argv) { + oops::Run run(argc, argv); + ufo::test::PrimitiveVariables tests; + return run.execute(tests); +} diff --git a/test/mains/TestParallelObsDistribution.cc b/test/mains/TestStuckCheck.cc similarity index 70% rename from test/mains/TestParallelObsDistribution.cc rename to test/mains/TestStuckCheck.cc index f26e13358..4309a690c 100644 --- a/test/mains/TestParallelObsDistribution.cc +++ b/test/mains/TestStuckCheck.cc @@ -5,12 +5,11 @@ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ -#include "../ufo/ParallelObsDistribution.h" +#include "../ufo/StuckCheck.h" #include "oops/runs/Run.h" int main(int argc, char ** argv) { oops::Run run(argc, argv); - ufo::test::ParallelObsDistribution tests; - run.execute(tests); - return 0; + ufo::test::StuckCheck tests; + return run.execute(tests); } diff --git a/test/testinput/abi_ahi_crtm.yaml b/test/testinput/abi_ahi_crtm.yaml index 2b14d03b7..1129a65c6 100644 --- a/test/testinput/abi_ahi_crtm.yaml +++ b/test/testinput/abi_ahi_crtm.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + #SurfaceWindGeoVars: uv obs options: inspectProfile: 1 Sensor_ID: ahi_himawari8 @@ -13,7 +14,7 @@ observations: obs space: name: ahi_himawari8 obsdatain: - obsfile: Data/ioda/testinput_tier_1/ahi_himawari8_obs_2019042306_m.nc4 + obsfile: Data/ufo/testinput_tier_1/ahi_himawari8_obs_2019042306_m.nc4 obsdataout: obsfile: Data/ahi_himawari8_obs_2019042306_m_out.nc4 simulated variables: [brightness_temperature] @@ -29,6 +30,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + #SurfaceWindGeoVars: uv obs options: inspectProfile: 1 Sensor_ID: abi_g16 @@ -37,7 +39,7 @@ observations: obs space: name: abi_g16 obsdatain: - obsfile: Data/ioda/testinput_tier_1/abi_g16_obs_2019042306_m.nc4 + obsfile: Data/ufo/testinput_tier_1/abi_g16_obs_2019042306_m.nc4 obsdataout: obsfile: Data/abi_g16_obs_2019042306_m_out.nc4 simulated variables: [brightness_temperature] diff --git a/test/testinput/adt.yaml b/test/testinput/adt.yaml index b86a84b8d..8fa70ff87 100644 --- a/test/testinput/adt.yaml +++ b/test/testinput/adt.yaml @@ -5,7 +5,7 @@ observations: - obs space: name: ADT obsdatain: - obsfile: Data/ioda/testinput_tier_1/Jason-2-2018-04-15.nc + obsfile: Data/ufo/testinput_tier_1/Jason-2-2018-04-15.nc simulated variables: [obs_absolute_dynamic_topography] obs operator: name: ADT diff --git a/test/testinput/aircraft.yaml b/test/testinput/aircraft.yaml index 193a9f951..eb9f42907 100644 --- a/test/testinput/aircraft.yaml +++ b/test/testinput/aircraft.yaml @@ -5,7 +5,7 @@ observations: - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 simulated variables: [air_temperature,specific_humidity] #simulated variables: [eastward_wind, northward_wind] obs operator: diff --git a/test/testinput/airs_crtm.yaml b/test/testinput/airs_crtm.yaml index c97b08d76..4a8593744 100644 --- a/test/testinput/airs_crtm.yaml +++ b/test/testinput/airs_crtm.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: airs_aqua EndianType: little_endian @@ -12,7 +13,7 @@ observations: obs space: name: airs_aqua obsdatain: - obsfile: Data/ioda/testinput_tier_1/airs_aqua_obs_2018041500_m_unittest.nc4 + obsfile: Data/ufo/testinput_tier_1/airs_aqua_obs_2018041500_m_unittest.nc4 simulated variables: [brightness_temperature] channels: 1, 6, 7, 10, 11, 15, 16, 17, 20, 21, 22, 24, 27, 28, 30, 36, 39, 40, 42, 51, 52, 54, 55, 56, 59, 62, 63, 68, 69, 71, diff --git a/test/testinput/airs_qc_filters.yaml b/test/testinput/airs_qc_filters.yaml index 497b33ff4..b1bc30520 100755 --- a/test/testinput/airs_qc_filters.yaml +++ b/test/testinput/airs_qc_filters.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: &Sensor_ID airs_aqua EndianType: little_endian @@ -12,8 +13,8 @@ observations: obs space: name: airs_aqua obsdatain: -# obsfile: Data/ioda/testinput_tier_1/airs_aqua_obs_2018041500_m_qc.nc4 - obsfile: Data/ioda/testinput_tier_1/airs_aqua_obs_2018041500_m_unittest.nc4 +# obsfile: Data/ufo/testinput_tier_1/airs_aqua_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/airs_aqua_obs_2018041500_m_unittest.nc4 # obsdataout: # obsfile: Data/airs_aqua_obs_2018041500_m_qc_out.nc4 # obsfile: Data/airs_aqua_obs_2018041500_m_unittest_out.nc4 @@ -45,37 +46,28 @@ observations: # filename: Data/ufo/testinput_tier_1/airs_aqua_geoval_2018041500_m_qc.nc4 filename: Data/ufo/testinput_tier_1/airs_aqua_geoval_2018041500_m_unittest.nc4 obs bias: - abias_in: Data/ufo/testinput_tier_1/satbias_crtm_in - sensor: *Sensor_ID - jobs: *all_channels - predictors: - - predictor: - name: constant - - predictor: - name: lapse_rate + input file: Data/ufo/testinput_tier_1/satbias_airs_aqua.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate options: order: 2 tlapse: &airs_aqua-atlap Data/ufo/testinput_tier_1/airs_aqua_tlapmean.txt - - predictor: - name: lapse_rate + - name: lapse_rate options: tlapse: *airs_aqua-atlap - - predictor: - name: emissivity - - predictor: - name: scan_angle + - name: emissivity + - name: scan_angle options: order: 4 - - predictor: - name: scan_angle + - name: scan_angle options: order: 3 - - predictor: - name: scan_angle + - name: scan_angle options: order: 2 - - predictor: - name: scan_angle + - name: scan_angle obs filters: # Wavenumber Check - filter: BlackList diff --git a/test/testinput/amsr2_clw_ret.yaml b/test/testinput/amsr2_clw_ret.yaml new file mode 100644 index 000000000..19adcbf1c --- /dev/null +++ b/test/testinput/amsr2_clw_ret.yaml @@ -0,0 +1,67 @@ +window begin: 2020-11-14T21:00:00Z +window end: 2020-11-15T03:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + linear obs operator: + Absorbers: [H2O,O3,CO2] + Clouds: [Water] + obs options: + Sensor_ID: amsr2_gcom-w1 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsr2_gcom-w1 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsr2_gcom-w1_obs_2020111500.nc4 + obsdataout: + obsfile: Data/amsr2_gcom-w1_obs_2020111500_out.nc4 + simulated variables: [brightness_temperature] + channels: 1-14 + geovals: + filename: Data/ufo/testinput_tier_1/amsr2_gcom-w1_geoval_2020111500.nc4 + obs filters: + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + minvalue: 50.0 + maxvalue: 340.0 + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: &amsr2_sys_bias [0.4800, 3.0737, 0.7433, 3.6430, + 3.5304, 4.4270, 5.1448, 5.0785, + 4.9763, 9.3215, 2.5789, 5.5274, + 0.6641, 1.3674] + clwret_types: [ObsValue] + maxvalue: 999.0 + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: *amsr2_sys_bias + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject + passedBenchmark: 91210 diff --git a/test/testinput/amsr2_crtm.yaml b/test/testinput/amsr2_crtm.yaml new file mode 100644 index 000000000..09c4ee7a7 --- /dev/null +++ b/test/testinput/amsr2_crtm.yaml @@ -0,0 +1,33 @@ +window begin: 2020-11-14T21:00:00Z +window end: 2020-11-15T03:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + linear obs operator: + Absorbers: [H2O,O3,CO2] + Clouds: [Water] + obs options: + Sensor_ID: amsr2_gcom-w1 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsr2_gcom-w1 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsr2_gcom-w1_obs_2020111500.nc4 + obsdataout: + obsfile: Data/amsr2_gcom-w1_obs_2020111500_out.nc4 + simulated variables: [brightness_temperature] + channels: 1-14 + geovals: + filename: Data/ufo/testinput_tier_1/amsr2_gcom-w1_geoval_2020111500.nc4 + rms ref: 177.40258653168425 +# rms_ref: 172.16660000870766 + tolerance: 1.e-6 + linear obs operator test: + coef TL: 1.e-3 + tolerance TL: 5.0e-3 + tolerance AD: 1.0e-11 diff --git a/test/testinput/amsr2_qc.yaml b/test/testinput/amsr2_qc.yaml new file mode 100644 index 000000000..c44721e2a --- /dev/null +++ b/test/testinput/amsr2_qc.yaml @@ -0,0 +1,179 @@ +window begin: 2020-11-14T21:00:00Z +window end: 2020-11-15T03:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + linear obs operator: + Absorbers: [H2O,O3,CO2] + Clouds: [Water] + obs options: + Sensor_ID: amsr2_gcom-w1 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsr2_gcom-w1 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsr2_gcom-w1_obs_2020111500.nc4 + obsdataout: + obsfile: Data/amsr2_gcom-w1_obs_2020111500_out.nc4 + simulated variables: [brightness_temperature] + channels: 1-14 + geovals: + filename: Data/ufo/testinput_tier_1/amsr2_gcom-w1_geoval_2020111500.nc4 + obs filters: + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + minvalue: 50.0 + maxvalue: 340.0 + - filter: Domain Check + filter variables: + - name: brightness_temperature + channels: 1-14 + where: + - variable: + name: water_area_fraction@GeoVaLs + minvalue: 1 + - variable: + name: surface_temperature_where_sea@GeoVaLs + minvalue: 275 + - variable: + name: surface_wind_speed@GeoVaLs + maxvalue: 12 + - variable: + name: latitude@MetaData + minvalue: -60.0 + maxvalue: 60.0 + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + test variables: + - name: TotalColumnVaporGuess@ObsFunction + minvalue: 10.0 + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + test variables: + - name: SunGlintAngle@ObsFunction + minvalue: 20.0 + # Ckeck CLW retrievals from observations + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: &amsr2_sys_bias [0.4800, 3.0737, 0.7433, 3.6430, + 3.5304, 4.4270, 5.1448, 5.0785, + 4.9763, 9.3215, 2.5789, 5.5274, + 0.6641, 1.3674] + clwret_types: [ObsValue] + maxvalue: 1.0 + # Ckeck CLW retrievals from HofX + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: *amsr2_sys_bias + clwret_types: [HofX] + maxvalue: 1.0 + - filter: Difference Check + filter variables: + - name: brightness_temperature + channels: 1-14 + value: + name: CLWRetMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: *amsr2_sys_bias + clwret_types: [ObsValue] + reference: + name: CLWRetMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: *amsr2_sys_bias + clwret_types: [HofX] + minvalue: -0.5 + maxvalue: 0.5 + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 1-14 + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: 1-14 + options: + channels: 1-14 + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: *amsr2_sys_bias + clwret_types: [ObsValue, HofX] + x0: [ 0.05, 0.05, 0.05, 0.05, 0.10, 0.10, + 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, + 0.05, 0.05] + x1: [ 0.60, 0.60, 0.60, 0.60, 0.60, 0.50, + 0.30, 0.30, 0.30, 0.30, 0.30, 0.30, + 0.30, 0.30] + err0: [ 0.8, 0.9, 0.8, 0.9, 1.0, 1.1, + 2.0, 3.5, 3.0, 4.8, 5.0, 6.0, + 4.5, 6.3] + err1: [ 5.0, 5.0, 5.0, 5.0, 5.0, 18.5, + 20.0, 40.0, 20.0, 25.0, 30.0, 30.0, + 30.0, 20.0] + - filter: Background Check + apply at iterations: 0, 1 + filter variables: + - name: brightness_temperature + channels: 1-14 + threshold: 2.0 + action: + name: reject + - filter: Background Check + apply at iterations: 0, 1 + filter variables: + - name: brightness_temperature + channels: 7-10 + absolute threshold: 30 + action: + name: reject + - filter: Background Check + apply at iterations: 0, 1 + filter variables: + - name: brightness_temperature + channels: 11-14 + absolute threshold: 50 + action: + name: reject + passedBenchmark: 45464 diff --git a/test/testinput/amsr2_qc_halo.yaml b/test/testinput/amsr2_qc_halo.yaml new file mode 100644 index 000000000..a576d5b6c --- /dev/null +++ b/test/testinput/amsr2_qc_halo.yaml @@ -0,0 +1,173 @@ +window begin: 2020-11-14T21:00:00Z +window end: 2020-11-15T03:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + linear obs operator: + Absorbers: [H2O,O3,CO2] + Clouds: [Water] + obs options: + Sensor_ID: amsr2_gcom-w1 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsr2_gcom-w1 + distribution: Halo + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsr2_gcom-w1_obs_2020111500.nc4 + obsdataout: + obsfile: Data/amsr2_gcom-w1_obs_2020111500_out.nc4 + simulated variables: [brightness_temperature] + channels: 1-14 + geovals: + filename: Data/ufo/testinput_tier_1/amsr2_gcom-w1_geoval_2020111500.nc4 + obs filters: + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + minvalue: 50.0 + maxvalue: 340.0 + - filter: Domain Check + filter variables: + - name: brightness_temperature + channels: 1-14 + where: + - variable: + name: water_area_fraction@GeoVaLs + minvalue: 1 + - variable: + name: surface_temperature_where_sea@GeoVaLs + minvalue: 275 + - variable: + name: surface_wind_speed@GeoVaLs + maxvalue: 12 + - variable: + name: latitude@MetaData + minvalue: -60.0 + maxvalue: 60.0 + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + test variables: + - name: TotalColumnVaporGuess@ObsFunction + minvalue: 10.0 + # Ckeck CLW retrievals from observations + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: &amsr2_sys_bias [0.4800, 3.0737, 0.7433, 3.6430, + 3.5304, 4.4270, 5.1448, 5.0785, + 4.9763, 9.3215, 2.5789, 5.5274, + 0.6641, 1.3674] + clwret_types: [ObsValue] + maxvalue: 1.0 + # Ckeck CLW retrievals from HofX + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-14 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: *amsr2_sys_bias + clwret_types: [HofX] + maxvalue: 1.0 + - filter: Difference Check + filter variables: + - name: brightness_temperature + channels: 1-14 + value: + name: CLWRetMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: *amsr2_sys_bias + clwret_types: [ObsValue] + reference: + name: CLWRetMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: *amsr2_sys_bias + clwret_types: [HofX] + minvalue: -0.5 + maxvalue: 0.5 + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 1-14 + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: 1-14 + options: + channels: 1-14 + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch18v: 7 + clwret_ch18h: 8 + clwret_ch36v: 11 + clwret_ch36h: 12 + sys_bias: *amsr2_sys_bias + clwret_types: [ObsValue, HofX] + x0: [ 0.05, 0.05, 0.05, 0.05, 0.10, 0.10, + 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, + 0.05, 0.05] + x1: [ 0.60, 0.60, 0.60, 0.60, 0.60, 0.50, + 0.30, 0.30, 0.30, 0.30, 0.30, 0.30, + 0.30, 0.30] + err0: [ 0.8, 0.9, 0.8, 0.9, 1.0, 1.1, + 2.0, 3.5, 3.0, 4.8, 5.0, 6.0, + 4.5, 6.3] + err1: [ 5.0, 5.0, 5.0, 5.0, 5.0, 18.5, + 20.0, 40.0, 20.0, 25.0, 30.0, 30.0, + 30.0, 20.0] + - filter: Background Check + apply at iterations: 0, 1 + filter variables: + - name: brightness_temperature + channels: 1-14 + threshold: 2.0 + action: + name: reject + - filter: Background Check + apply at iterations: 0, 1 + filter variables: + - name: brightness_temperature + channels: 7-10 + absolute threshold: 30 + action: + name: reject + - filter: Background Check + apply at iterations: 0, 1 + filter variables: + - name: brightness_temperature + channels: 11-14 + absolute threshold: 50 + action: + name: reject + passedBenchmark: 45736 diff --git a/test/testinput/amsr2_rttov_ops_qc_rttovonedvarcheck.yaml b/test/testinput/amsr2_rttov_ops_qc_rttovonedvarcheck.yaml new file mode 100644 index 000000000..f8de48b0f --- /dev/null +++ b/test/testinput/amsr2_rttov_ops_qc_rttovonedvarcheck.yaml @@ -0,0 +1,82 @@ +window begin: 2019-12-29T21:00:00Z +window end: 2019-12-30T03:00:00Z + +observations: +# Test Newton minimizer +- obs operator: + name: RTTOV + GeoVal_type: MetO + Absorbers: &rttov_absobers [Water_vapour, CLW, CIW] + linear obs operator: + Absorbers: [Water_vapour] + obs options: + RTTOV_default_opts: UKMO_PS44 + SatRad_compatibility: true + RTTOV_GasUnitConv: true + UseRHwaterForQC: &UseRHwaterForQC true # default + UseColdSurfaceCheck: &UseColdSurfaceCheck true # default + Sensor_ID: &sensor_id gcom-w_1_amsr2 + CoefficientPath: Data/ + obs space: + name: amsr2 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsr2_obs_20191230T0000Z_100subset.nc4 + simulated variables: [brightness_temperature] + channels: &ops_channels 7-12 + geovals: + filename: Data/ufo/testinput_tier_1/amsr2_geovals_20191230T0000Z_100subset.nc4 + obs filters: + # Do 1D-Var check + - filter: RTTOV OneDVar Check + ModOptions: + Absorbers: *rttov_absobers + obs options: + RTTOV_default_opts: UKMO_PS44 + SatRad_compatibility: false # done in filter + RTTOV_GasUnitConv: true + UseRHwaterForQC: *UseRHwaterForQC + UseColdSurfaceCheck: *UseColdSurfaceCheck + Sensor_ID: *sensor_id + CoefficientPath: Data/ + BMatrix: ../resources/bmatrix/rttov/amsr_bmatrix_70_test.dat + RMatrix: ../resources/rmatrix/rttov/amsr_gcomw1_rmatrix_test.nc4 + filter variables: + - name: brightness_temperature + channels: *ops_channels + retrieval variables: # Variables needed in the geovals + - air_temperature # 1 + - specific_humidity # 10 + - mass_content_of_cloud_liquid_water_in_atmosphere_layer # required for qtotal + - mass_content_of_cloud_ice_in_atmosphere_layer # required for qtotal + - surface_temperature # 3 + - specific_humidity_at_two_meters_above_surface # 4 + - skin_temperature # 5 + - air_pressure_at_two_meters_above_surface # 6 + - eastward_wind # 11 - required for windspeed retrieval + - northward_wind # 11 - required for windspeed retrieval + nlevels: 70 + qtotal: true + UseQtSplitRain: true + UseMLMinimization: false + UseJforConvergence: true + UseRHwaterForQC: *UseRHwaterForQC # setting the same as obs operator + UseColdSurfaceCheck: *UseColdSurfaceCheck # setting the same as obs operator + JConvergenceOption: 1 + ConvergenceFactor: 0.40 + CostConvergenceFactor: 0.01 + Max1DVarIterations: 7 + EmissLandDefault: 0.95 + EmissSeaIceDefault: 0.92 + Store1DVarLWP: true + defer to post: true + # Reject channels when highretlwp + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 7-12 + where: + - variable: + name: LWP@OneDVar + maxvalue: 1.0e-1 + defer to post: true + passedBenchmark: 450 # number of passed obs diff --git a/test/testinput/amsr2_rttov_qc.yaml b/test/testinput/amsr2_rttov_qc.yaml new file mode 100644 index 000000000..e66e1d5b6 --- /dev/null +++ b/test/testinput/amsr2_rttov_qc.yaml @@ -0,0 +1,49 @@ +window begin: 2019-12-29T21:00:00Z +window end: 2019-12-30T03:00:00Z + +observations: +- obs operator: + name: RTTOV + GeoVal_type: MetO + Absorbers: &rttov_absobers [Water_vapour] + linear obs operator: + Absorbers: [Water_vapour] + obs options: &rttov_options + RTTOV_default_opts: UKMO_PS44 + SatRad_compatibility: true + Sensor_ID: gcom-w_1_amsr2 + CoefficientPath: Data/ + obs space: + name: amsr2 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsr2_obs_20191230T0000Z_100subset.nc4 + simulated variables: [brightness_temperature] + channels: 7-9, 11-13 + geovals: + filename: Data/ufo/testinput_tier_1/amsr2_geovals_20191230T0000Z_100subset.nc4 + obs filters: + # Equivalent to OPS Im36HCloud = T, OminBCloud_36H = 4.3 + - filter: Difference Check + filter variables: + - name: brightness_temperature + channels: 7-9,11-13 + reference: brightness_temperature_11@ObsValue + value: brightness_temperature_11@HofX + threshold: 4.3 + action: + name: reject + # Equivalent to OPS ImCloudAnomaly = T, ImCloudThresh = 5 + - filter: Bounds Check + test variables: + - name: BgdDepartureAnomaly@ObsFunction + options: + channel_low_freq: 11 + channel_high_freq: 13 + ObsBias: ObsBias + maxvalue: 5 + action: + name: reject + # QC amsr2 brightness_temperature_13: 17 out of bounds. + # QC amsr2 brightness_temperature_13: 74 rejected by difference check. + # QC amsr2 brightness_temperature_13: 9 passed out of 100 observations. + passedBenchmark: 54 diff --git a/test/testinput/amsua_allsky_gsi_qc.yaml b/test/testinput/amsua_allsky_gsi_qc.yaml index 92412184c..cabc61323 100644 --- a/test/testinput/amsua_allsky_gsi_qc.yaml +++ b/test/testinput/amsua_allsky_gsi_qc.yaml @@ -7,6 +7,7 @@ observations: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv obs options: Sensor_ID: amsua_n19 EndianType: little_endian @@ -14,7 +15,7 @@ observations: obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 obsdataout: obsfile: Data/amsua_n19_obs_2018041500_m_out.nc4 simulated variables: [brightness_temperature] diff --git a/test/testinput/amsua_crtm.yaml b/test/testinput/amsua_crtm.yaml index 6291d0db4..82b945276 100644 --- a/test/testinput/amsua_crtm.yaml +++ b/test/testinput/amsua_crtm.yaml @@ -7,6 +7,7 @@ observations: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv linear obs operator: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] @@ -18,7 +19,7 @@ observations: obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 # obsdataout: # obsfile: Data/amsua_n19_obs_2018041500_m_qc_crtm_out.nc4 simulated variables: [brightness_temperature] diff --git a/test/testinput/amsua_crtm_bc.yaml b/test/testinput/amsua_crtm_bc.yaml index d4f13b00c..246a6b224 100644 --- a/test/testinput/amsua_crtm_bc.yaml +++ b/test/testinput/amsua_crtm_bc.yaml @@ -7,6 +7,7 @@ observations: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv linear obs operator: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] @@ -18,7 +19,7 @@ observations: obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 # obsdataout: # obsfile: Data/amsua_n19_obs_2018041500_m_qc_crtm_bc_out.nc4 simulated variables: [brightness_temperature] @@ -26,55 +27,42 @@ observations: geovals: filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 obs bias: - abias_in: Data/ufo/testinput_tier_1/satbias_crtm_in - abias_out: Data/ufo/testinput_tier_1/satbias_crtm_out - sensor: *Sensor_ID - jobs: *channels - covariance: - minimal required obs number: 20 - variance range: [1.0e-6, 10.] - step size: 1.0e-4 - largest analysis variance: 10000.0 - prior: - datain: Data/ufo/testinput_tier_1/satbias_crtm_pc - inflation: - ratio: 1.1 - ratio for small dataset: 2.0 - predictors: - - predictor: - name: constant - - predictor: - name: cosine_of_latitude_times_orbit_node + input file: Data/ufo/testinput_tier_1/satbias_amsua_n19.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node options: preconditioner: 0.01 - - predictor: - name: sine_of_latitude - - predictor: - name: lapse_rate + - name: sine_of_latitude + - name: lapse_rate options: order: 2 tlapse: &amsua19tlap Data/ufo/testinput_tier_1/amsua_n19_tlapmean.txt - - predictor: - name: lapse_rate + - name: lapse_rate options: tlapse: *amsua19tlap - - predictor: - name: emissivity - - predictor: - name: scan_angle + - name: emissivity + - name: scan_angle options: order: 4 - - predictor: - name: scan_angle + - name: scan_angle options: order: 3 - - predictor: - name: scan_angle + - name: scan_angle options: order: 2 - - predictor: - name: scan_angle - tolerance: 1.e-5 + - name: scan_angle + covariance: + minimal required obs number: 20 + variance range: [1.0e-6, 10.0] + step size: 1.0e-4 + largest analysis variance: 10000.0 + prior: + input file: Data/ufo/testinput_tier_1/satbias_amsua_n19.nc4 + inflation: + ratio: 1.1 + ratio for small dataset: 2.0 vector ref: GsiHofXBc tolerance: 1.e-7 linear obs operator test: diff --git a/test/testinput/amsua_qc.yaml b/test/testinput/amsua_qc.yaml index 3015ee454..86ecf043c 100644 --- a/test/testinput/amsua_qc.yaml +++ b/test/testinput/amsua_qc.yaml @@ -7,6 +7,7 @@ observations: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv obs options: Sensor_ID: amsua_n19 EndianType: little_endian @@ -14,7 +15,7 @@ observations: obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 1-15 geovals: diff --git a/test/testinput/amsua_qc_clwretmw.yaml b/test/testinput/amsua_qc_clwretmw.yaml index 66ac57a11..5351dca14 100755 --- a/test/testinput/amsua_qc_clwretmw.yaml +++ b/test/testinput/amsua_qc_clwretmw.yaml @@ -7,6 +7,7 @@ observations: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv obs options: Sensor_ID: amsua_n19 EndianType: little_endian @@ -14,7 +15,7 @@ observations: obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 # obsdataout: # obsfile: Data/amsua_n19_obs_2018041500_m_qc_clwretmw_out.nc4 simulated variables: [brightness_temperature] diff --git a/test/testinput/amsua_qc_filters.yaml b/test/testinput/amsua_qc_filters.yaml index 896c549a3..579301676 100755 --- a/test/testinput/amsua_qc_filters.yaml +++ b/test/testinput/amsua_qc_filters.yaml @@ -7,6 +7,7 @@ observations: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv obs options: Sensor_ID: &Sensor_ID amsua_n19 EndianType: little_endian @@ -14,7 +15,7 @@ observations: obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 # obsdataout: # obsfile: Data/amsua_n19_obs_2018041500_m_qc_filters_out.nc4 simulated variables: [brightness_temperature] @@ -22,44 +23,33 @@ observations: geovals: filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 obs bias: - abias_in: Data/ufo/testinput_tier_1/satbias_crtm_in - abias_out: Data/ufo/testinput_tier_1/satbias_crtm_out - sensor: *Sensor_ID - jobs: *all_channels - predictors: - - predictor: - name: constant -# - predictor: -# name: cloud_liquid_water -# options: -# clwret_ch238: 1 -# clwret_ch314: 2 -# clwret_types: [HofX] - - predictor: - name: lapse_rate + input file: Data/ufo/testinput_tier_1/satbias_amsua_n19.nc4 + variational bc: + predictors: + - name: constant + # - name: cloud_liquid_water + # options: + # clwret_ch238: 1 + # clwret_ch314: 2 + # clwret_types: [HofX] + - name: lapse_rate options: order: 2 tlapse: &amsua19tlap Data/ufo/testinput_tier_1/amsua_n19_tlapmean.txt - - predictor: - name: lapse_rate + - name: lapse_rate options: tlapse: *amsua19tlap - - predictor: - name: emissivity - - predictor: - name: scan_angle + - name: emissivity + - name: scan_angle options: order: 4 - - predictor: - name: scan_angle + - name: scan_angle options: order: 3 - - predictor: - name: scan_angle + - name: scan_angle options: order: 2 - - predictor: - name: scan_angle + - name: scan_angle obs filters: - filter: BlackList filter variables: diff --git a/test/testinput/amsua_qc_miss_val.yaml b/test/testinput/amsua_qc_miss_val.yaml index e246e3b9b..aba13186a 100644 --- a/test/testinput/amsua_qc_miss_val.yaml +++ b/test/testinput/amsua_qc_miss_val.yaml @@ -7,6 +7,7 @@ observations: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv obs options: Sensor_ID: amsua_n19 EndianType: little_endian @@ -14,7 +15,7 @@ observations: obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_miss_val.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_miss_val.nc4 simulated variables: [brightness_temperature] channels: 1 geovals: diff --git a/test/testinput/amsua_rttovcpp.yaml b/test/testinput/amsua_rttovcpp.yaml new file mode 100644 index 000000000..97953f5e0 --- /dev/null +++ b/test/testinput/amsua_rttovcpp.yaml @@ -0,0 +1,25 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs operator: + name: RTTOVCPP + SensorID: noaa_19_amsua + CoefPath: Data/ +# linear obs operator: + obs space: + name: noaa_19_amsua + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_hofxnm_2018041500_m_rttovcpp.nc4 +# obsdataout: +# obsfile: Data/amsua_n19_obs_2018041500_m_rttovcpp_out.nc4 + simulated variables: [brightness_temperature] + channels: 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_rttovcpp.nc4 + vector ref: HofX + tolerance: 1.e-7 + linear obs operator test: + coef TL: 1.e-3 + tolerance TL: 1.0e-3 + tolerance AD: 1.0e-11 diff --git a/test/testinput/amsua_seaice_qc.yaml b/test/testinput/amsua_seaice_qc.yaml index 938b1cb5a..b8ac054b6 100644 --- a/test/testinput/amsua_seaice_qc.yaml +++ b/test/testinput/amsua_seaice_qc.yaml @@ -5,7 +5,7 @@ observations: - obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 obsdataout: obsfile: Data/amsua_n19_obs_2018041500_out.nc4 simulated variables: [brightness_temperature] diff --git a/test/testinput/aod_crtm.yaml b/test/testinput/aod_crtm.yaml index 175bceaf5..df37e1551 100644 --- a/test/testinput/aod_crtm.yaml +++ b/test/testinput/aod_crtm.yaml @@ -13,7 +13,7 @@ observations: obs space: name: Aod obsdatain: - obsfile: Data/ioda/testinput_tier_1/aod_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aod_obs_2018041500_m.nc4 simulated variables: [aerosol_optical_depth] channels: 4 geovals: diff --git a/test/testinput/aod_extinction.yaml b/test/testinput/aod_extinction.yaml new file mode 100644 index 000000000..d0ce1a2e9 --- /dev/null +++ b/test/testinput/aod_extinction.yaml @@ -0,0 +1,25 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: + - obs operator: + name: AodExt + nprofiles: 2 + bkg_wavelengths: [470.0,870.0] + obs space: + name: AodExt + obsdatain: + obsfile: Data/ufo/testinput_tier_1/nnr_aod_3ch_obs_2018041500_m.nc4 + simulated variables: [Total_Aerosol_Optical_Depth_470, Total_Aerosol_Optical_Depth_550, Total_Aerosol_Optical_Depth_870] + obs bias: {} + geovals: + filename: Data/ufo/testinput_tier_1/extinction_profiles_470_870nm_geoval_2018041500_m.nc4 + vector ref: HofX + tolerance: 1.0e-04 + linear obs operator test: + coefL : 1.0e-8 + coef TL: 1.0e-8 + tolerance TL: 1.0e-08 + tolerance AD: 1.0e-09 + + diff --git a/test/testinput/aod_luts.yaml b/test/testinput/aod_luts.yaml index bcd4e6b10..c643114c6 100644 --- a/test/testinput/aod_luts.yaml +++ b/test/testinput/aod_luts.yaml @@ -13,7 +13,7 @@ observations: obs space: name: Aod obsdatain: - obsfile: Data/ioda/testinput_tier_1/aod_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aod_obs_2018041500_m.nc4 simulated variables: [aerosol_optical_depth] channels: 4 geovals: diff --git a/test/testinput/atms_crtm.yaml b/test/testinput/atms_crtm.yaml index d26844d35..66186dd98 100644 --- a/test/testinput/atms_crtm.yaml +++ b/test/testinput/atms_crtm.yaml @@ -7,6 +7,7 @@ observations: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv linear obs operator: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] @@ -17,7 +18,7 @@ observations: obs space: name: Radiance obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 # obsdataout: # obsfile: Data/atms_npp_obs_2018041500_m_qc_atms_crtm_out.nc4 simulated variables: [brightness_temperature] diff --git a/test/testinput/atms_crtm_bc.yaml b/test/testinput/atms_crtm_bc.yaml deleted file mode 100644 index 381c1da89..000000000 --- a/test/testinput/atms_crtm_bc.yaml +++ /dev/null @@ -1,75 +0,0 @@ -window begin: 2018-04-14T21:00:00Z -window end: 2018-04-15T03:00:00Z - -observations: -- obs operator: - name: CRTM - Absorbers: [H2O,O3,CO2] - Clouds: [Water, Ice] - Cloud_Fraction: 1.0 - linear obs operator: - Absorbers: [H2O,O3,CO2] - Clouds: [Water, Ice] - obs options: - inspectProfile: 1 - Sensor_ID: &Sensor_ID atms_npp - EndianType: little_endian - CoefficientPath: Data/ - obs space: - name: atms_npp - obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 -# obsdataout: -# obsfile: Data/atms_npp_obs_2018041500_m_qc_atms_crtm_bc_out.nc4 - simulated variables: [brightness_temperature] - channels: &channels - 1-22 - geovals: - filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 - obs bias: - name: LinearCombination - abias_in: Data/ufo/testinput_tier_1/satbias_crtm_in - abias_out: Data/ufo/testinput_tier_1/satbias_crtm_out - sensor: *Sensor_ID - jobs: *channels - predictors: - - predictor: - name: constant -# - predictor: -# name: cloud_liquid_water -# options: -# clwret_ch238: 1 -# clwret_ch314: 2 -# clwret_types: [GsiHofX] - - predictor: - name: lapse_rate - options: - order: 2 - tlapse: &atms_npp_tlap Data/ufo/testinput_tier_1/atms_npp_tlapmean.txt - - predictor: - name: lapse_rate - options: - tlapse: *atms_npp_tlap - - predictor: - name: emissivity - - predictor: - name: scan_angle - options: - order: 4 - - predictor: - name: scan_angle - options: - order: 3 - - predictor: - name: scan_angle - options: - order: 2 - - predictor: - name: scan_angle - vector ref: GsiHofXBc - tolerance: 1.e-7 - linear obs operator test: - iterations TL: 2 - coef TL: 1.e-3 - tolerance TL: 1.0e-3 - tolerance AD: 1.0e-11 diff --git a/test/testinput/atms_qc_filters.yaml b/test/testinput/atms_qc_filters.yaml index dcf8907b6..e0dcd7a81 100755 --- a/test/testinput/atms_qc_filters.yaml +++ b/test/testinput/atms_qc_filters.yaml @@ -7,6 +7,7 @@ observations: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv obs options: Sensor_ID: &Sensor_ID atms_npp EndianType: little_endian @@ -14,46 +15,36 @@ observations: obs space: name: atms_npp obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 # obsdataout: -# obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc_out.nc4 +# obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc_out.nc4 simulated variables: [brightness_temperature] channels: &all_channels 1-22 geovals: filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 obs bias: - abias_in: Data/ufo/testinput_tier_1/satbias_crtm_in - abias_out: Data/ufo/testinput_tier_1/satbias_crtm_out - sensor: *Sensor_ID - jobs: *all_channels - predictors: - - predictor: - name: constant - - predictor: - name: lapse_rate + input file: Data/ufo/testinput_tier_1/satbias_atms_npp.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate options: order: 2 tlapse: &atms_npp_tlap Data/ufo/testinput_tier_1/atms_npp_tlapmean.txt - - predictor: - name: lapse_rate + - name: lapse_rate options: tlapse: *atms_npp_tlap - - predictor: - name: emissivity - - predictor: - name: scan_angle + - name: emissivity + - name: scan_angle options: order: 4 - - predictor: - name: scan_angle + - name: scan_angle options: order: 3 - - predictor: - name: scan_angle + - name: scan_angle options: order: 2 - - predictor: - name: scan_angle + - name: scan_angle obs filters: - filter: BlackList filter variables: diff --git a/test/testinput/atms_rttov_ops.yaml b/test/testinput/atms_rttov_ops.yaml index 9198c8d27..8f21664cf 100644 --- a/test/testinput/atms_rttov_ops.yaml +++ b/test/testinput/atms_rttov_ops.yaml @@ -4,26 +4,65 @@ window end: 2019-12-30T03:00:00Z observations: - obs operator: name: RTTOV - GeoVal_type: MetO + GeoVal_type: MetO # default + Debug: false # default Absorbers: [Water_vapour] linear obs operator: Absorbers: [Water_vapour] obs options: - RTTOV_default_opts: OPS - SatRad_compatibility: true + RTTOV_default_opts: UKMO_PS43 # non-default + RTTOV_apply_reg_limits: true # for compatibility with previous + # version of this test + SatRad_compatibility: true # default + InspectProfileNumber: 1 Sensor_ID: noaa_20_atms CoefficientPath: Data/ + UseRHwaterForQC: true # default + UseColdSurfaceCheck: true # default + prof_by_prof: false # default obs space: name: atms_n20 obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_n20_obs_20191230T0000_rttov.nc4 + obsfile: Data/ufo/testinput_tier_1/atms_n20_obs_20191230T0000_rttov.nc4 obsdataout: - obsfile: Data/atms_npp_obs_2019123000_m_rttov_out.nc4 + obsfile: Data/atms_n20_obs_2019123000_m_rttov1_out.nc4 simulated variables: [brightness_temperature] channels: 1-22 geovals: filename: Data/ufo/testinput_tier_1/geovals_atms_20191230T0000Z_benchmark.nc4 - rms ref: 228.50280967174939 + rms ref: 228.54196297632672 + tolerance: 1.e-7 + linear obs operator test: + coef TL: 1.e-4 + tolerance TL: 2.0e-2 + tolerance AD: 1.0e-11 +- obs operator: + name: RTTOV + GeoVal_type: MetO # default + Debug: false # default + Absorbers: [Water_vapour] + linear obs operator: + Absorbers: [Water_vapour] + obs options: + RTTOV_default_opts: UKMO_PS43 # non-default + RTTOV_apply_reg_limits: true # for compatibility + SatRad_compatibility: false # non-default + Sensor_ID: noaa_20_atms + CoefficientPath: Data/ + UseRHwaterForQC: false # non-default + UseColdSurfaceCheck: false # non-default + prof_by_prof: true # non-default + obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_n20_obs_20191230T0000_rttov.nc4 + obsdataout: + obsfile: Data/atms_n20_obs_2019123000_m_rttov2_out.nc4 + simulated variables: [brightness_temperature] + channels: 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/geovals_atms_20191230T0000Z_benchmark.nc4 + rms ref: 227.88586699673394 tolerance: 1.e-7 linear obs operator test: coef TL: 1.e-4 diff --git a/test/testinput/atms_rttov_ops_qc_rttovonedvarcheck.yaml b/test/testinput/atms_rttov_ops_qc_rttovonedvarcheck.yaml new file mode 100644 index 000000000..d99cd2113 --- /dev/null +++ b/test/testinput/atms_rttov_ops_qc_rttovonedvarcheck.yaml @@ -0,0 +1,143 @@ +window begin: 2019-12-29T21:00:00Z +window end: 2019-12-30T03:00:00Z + +observations: +# Test Newton minimizer +- obs operator: + name: RTTOV + GeoVal_type: MetO + Absorbers: &rttov_absobers [Water_vapour, CLW, CIW] + linear obs operator: + Absorbers: [Water_vapour] + obs options: + RTTOV_default_opts: UKMO_PS43 + SatRad_compatibility: true + RTTOV_GasUnitConv: true + UseRHwaterForQC: &UseRHwaterForQC1 true # default + UseColdSurfaceCheck: &UseColdSurfaceCheck1 true # default + Sensor_ID: &sensor_id noaa_20_atms + CoefficientPath: Data/ + obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_n20_obs_20191230T0000_rttov.nc4 + simulated variables: [brightness_temperature] + channels: &ops_channels 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/geovals_atms_20191230T0000Z_benchmark.nc4 + obs filters: + # BlackList these channels but still want hofx for monitoring + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 1-5, 16-17 + # Do 1D-Var check + - filter: RTTOV OneDVar Check + ModName: RTTOV + ModOptions: + Absorbers: *rttov_absobers + obs options: + RTTOV_default_opts: UKMO_PS43 + SatRad_compatibility: false # done in filter +# RTTOV_GasUnitConv: false + RTTOV_GasUnitConv: true + Sensor_ID: *sensor_id + CoefficientPath: Data/ + BMatrix: ../resources/bmatrix/rttov/atms_bmatrix_70_test.dat + RMatrix: ../resources/rmatrix/rttov/atms_noaa_20_rmatrix_test.nc4 + filter variables: + - name: brightness_temperature + channels: *ops_channels + retrieval variables: + - air_temperature + - specific_humidity + - mass_content_of_cloud_liquid_water_in_atmosphere_layer + - mass_content_of_cloud_ice_in_atmosphere_layer + - surface_temperature + - specific_humidity_at_two_meters_above_surface + - skin_temperature + - air_pressure_at_two_meters_above_surface + nlevels: 70 + qtotal: true + UseQtSplitRain: true + UseMLMinimization: false + UseJforConvergence: true + UseRHwaterForQC: *UseRHwaterForQC1 # setting the same as obs operator + UseColdSurfaceCheck: *UseColdSurfaceCheck1 # setting the same as obs operator + FullDiagnostics: true + JConvergenceOption: 1 + ConvergenceFactor: 0.40 + CostConvergenceFactor: 0.01 + Max1DVarIterations: 7 + EmissLandDefault: 0.95 + EmissSeaIceDefault: 0.92 + passedBenchmark: 1410 # number of passed obs +## Test ML minimizer +- obs operator: + name: RTTOV + GeoVal_type: MetO + Absorbers: &rttov_absobers [Water_vapour, CLW, CIW] + linear obs operator: + Absorbers: [Water_vapour] + obs options: &rttov_options + RTTOV_default_opts: UKMO_PS43 + SatRad_compatibility: true + RTTOV_GasUnitConv: false + UseRHwaterForQC: &UseRHwaterForQC2 false # non-default + UseColdSurfaceCheck: &UseColdSurfaceCheck2 false # non-default + Sensor_ID: *sensor_id + CoefficientPath: Data/ + QtSplitRain: false + obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_n20_obs_20191230T0000_rttov.nc4 + simulated variables: [brightness_temperature] + channels: &ops_channels 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/geovals_atms_20191230T0000Z_benchmark.nc4 + obs filters: + # BlackList these channels but still want hofx for monitoring + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 1-5, 16-17 + # Do 1D-Var check + - filter: RTTOV OneDVar Check + ModName: RTTOV + ModOptions: + Absorbers: *rttov_absobers + obs options: + RTTOV_default_opts: UKMO_PS43 + SatRad_compatibility: false # done in filter + Sensor_ID: *sensor_id + CoefficientPath: Data/ + BMatrix: ../resources/bmatrix/rttov/atms_bmatrix_70_test.dat + RMatrix: ../resources/rmatrix/rttov/atms_noaa_20_rmatrix_test.nc4 + filter variables: + - name: brightness_temperature + channels: *ops_channels + retrieval variables: + - air_temperature + - specific_humidity + - mass_content_of_cloud_liquid_water_in_atmosphere_layer + - mass_content_of_cloud_ice_in_atmosphere_layer + - surface_temperature + - specific_humidity_at_two_meters_above_surface + - skin_temperature + - air_pressure_at_two_meters_above_surface + nlevels: 70 + qtotal: true + UseQtSplitRain: false + UseMLMinimization: true + UseJforConvergence: true + UseRHwaterForQC: *UseRHwaterForQC2 # setting the same as obs operator + UseColdSurfaceCheck: *UseColdSurfaceCheck2 # setting the same as obs operator + FullDiagnostics: true + JConvergenceOption: 1 + ConvergenceFactor: 0.40 + CostConvergenceFactor: 0.01 + Max1DVarIterations: 7 + EmissLandDefault: 0.95 + EmissSeaIceDefault: 0.92 + passedBenchmark: 1350 # number of passed obs diff --git a/test/testinput/atms_rttov_qc.yaml b/test/testinput/atms_rttov_qc.yaml new file mode 100644 index 000000000..b9ab5a662 --- /dev/null +++ b/test/testinput/atms_rttov_qc.yaml @@ -0,0 +1,89 @@ +window begin: 2019-12-29T21:00:00Z +window end: 2019-12-30T03:00:00Z + +observations: +- obs operator: + name: RTTOV + GeoVal_type: MetO + Absorbers: &rttov_absobers [Water_vapour, CLW] + linear obs operator: + Absorbers: [Water_vapour] + obs options: &rttov_options + RTTOV_default_opts: UKMO_PS45 + SatRad_compatibility: true + Sensor_ID: noaa_20_atms + CoefficientPath: Data/ + RTTOV_GasUnitConv: true + UseRHwaterForQC: true + obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_n20_obs_20191230T0000_rttov.nc4 + simulated variables: [brightness_temperature] + channels: 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/geovals_atms_20191230T0000Z_benchmark.nc4 + obs filters: +### Bennartz scattering test [over land] ### + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-7, 16-22 + where: + - variable: + name: land_sea@MetaData + is_in: 0 # land=0, sea=1, ice=2 + test variables: + - name: BennartzScatIndex@ObsFunction + options: + channel_89ghz: 16 + channel_150ghz: 17 + bennartz_coeff_1: 0.158 + bennartz_coeff_2: 0.0163 + apply_bias: ObsBias + maxvalue: -1.0 + action: + name: reject +### Surface-to-space transmittance check ### + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: &humidity_chans 18-22 + test variables: + - name: toa_total_transmittance@ObsDiag + channels: *humidity_chans + maxvalue: 0.7 # (example) apply to each channel + action: + name: reject # obs rejected per channel if transmittance exceeded +### Cost function cloud check ### + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 18-20 + where: + - variable: + name: land_sea@MetaData + is_in: 0 # land=0, sea=1, ice=2 + test variables: + - name: CloudCostFunction@ObsFunction + options: + cost channels list: 18, 20, 22 + RMatrix: ../resources/rmatrix/rttov/atms_noaa_20_rmatrix_test.nc4 + BMatrix: ../resources/bmatrix/rttov/atms_bmatrix_70_test.dat + background fields: + - air_temperature + - specific_humidity + - mass_content_of_cloud_liquid_water_in_atmosphere_layer + - mass_content_of_cloud_ice_in_atmosphere_layer + - surface_temperature + - specific_humidity_at_two_meters_above_surface + - skin_temperature + - air_pressure_at_two_meters_above_surface + qtotal: true + qtotal split rain: true + reverse Jacobian order: true + HofX group: HofX # default, awaits bias correction + maxvalue: 69.8 # example value + action: + name: reject + passedBenchmark: 2164 # number passing QC after all filters diff --git a/test/testinput/background_error_identity.yaml b/test/testinput/background_error_identity.yaml new file mode 100644 index 000000000..83ace8439 --- /dev/null +++ b/test/testinput/background_error_identity.yaml @@ -0,0 +1,23 @@ +# Verify that the BackgroundErrorIdentity obs operator doesn't mess up the H(x) array produced +# by another component of the Composite operator. + +window begin: '2018-04-14T20:30:00Z' +window end: '2018-04-15T03:30:00Z' + +observations: +- obs space: + name: Sondes + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [surface_pressure] + obs operator: + name: Composite + components: + # operator used to evaluate H(x) + - name: Identity + # operator used to evaluate background errors + - name: BackgroundErrorIdentity + geovals: + filename: Data/ufo/testinput_tier_1/sondes_background_error_vert_interp_height_geoval_2018041500_s.nc4 + vector ref: GsiHofX + tolerance: 1.0e-6 diff --git a/test/testinput/background_error_vert_interp.yaml b/test/testinput/background_error_vert_interp.yaml new file mode 100644 index 000000000..1b0081415 --- /dev/null +++ b/test/testinput/background_error_vert_interp.yaml @@ -0,0 +1,25 @@ +# Verify that the BackgroundErrorVertInterp obs operator doesn't mess up the H(x) array produced +# by another component of the Composite operator.. + +window begin: '2018-04-14T20:30:00Z' +window end: '2018-04-15T03:30:00Z' + +observations: +- obs space: + name: Sondes + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [air_temperature, specific_humidity] + obs operator: + name: Composite + components: + # operator used to evaluate H(x) + - name: VertInterp + # operator used to evaluate background errors + - name: BackgroundErrorVertInterp + observation vertical coordinate: air_pressure + vertical coordinate: background_error_air_pressure + geovals: + filename: Data/ufo/testinput_tier_1/sondes_background_error_vert_interp_air_pressure_geoval_2018041500_s.nc4 + vector ref: GsiHofX + tolerance: 1.0e-6 diff --git a/test/testinput/bias_coeff.yaml b/test/testinput/bias_coeff.yaml new file mode 100644 index 000000000..23596314f --- /dev/null +++ b/test/testinput/bias_coeff.yaml @@ -0,0 +1,104 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-15 + obs bias: # test reading coefficients for 2 predictors; coeffs are zero for all channels + input file: Data/ufo/testinput_tier_1/satbias_amsua_n19.nc4 + variational bc: + predictors: + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + obs bias test: + norm: 0.0 + relative tolerance: 0.0 + +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-15 + obs bias: # test reading coefficients for 1 predictor (all channels) + input file: Data/ufo/testinput_tier_1/satbias_amsua_n19.nc4 + variational bc: + predictors: + - name: emissivity + obs bias test: + norm: 2.521706721918669 + relative tolerance: 1.e-7 + +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &channels 7-9, 12 + obs bias: # test reading coefficients for 1 predictor (subset of channels) + input file: Data/ufo/testinput_tier_1/satbias_amsua_n19.nc4 + variational bc: + predictors: + - name: emissivity + obs bias test: + norm: 0.9845241512216701 + relative tolerance: 1.e-7 + +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &channels 7-9, 12 + obs bias: # test reading coefficients for 2 predictors and subset of channels + input file: Data/ufo/testinput_tier_1/satbias_amsua_n19.nc4 + variational bc: + predictors: + - name: emissivity + - name: constant + obs bias test: + norm: 0.8470894100411567 + relative tolerance: 1.e-7 + +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-15 + obs bias: # test reading all channels and most predictors + input file: Data/ufo/testinput_tier_1/satbias_amsua_n19.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua19tlap Data/ufo/testinput_tier_1/amsua_n19_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua19tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs bias test: + norm: 2.8888588267029465 + relative tolerance: 1.e-7 diff --git a/test/testinput/bias_coeff_cov.yaml b/test/testinput/bias_coeff_cov.yaml new file mode 100644 index 000000000..8ea96b338 --- /dev/null +++ b/test/testinput/bias_coeff_cov.yaml @@ -0,0 +1,48 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-15 + obs bias: + input file: Data/ufo/testinput_tier_1/satbias_amsua_n19.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua19tlap Data/ufo/testinput_tier_1/amsua_n19_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua19tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + covariance: + minimal required obs number: 20 + variance range: [1.0e-6, 10.0] + step size: 1.0e-4 + largest analysis variance: 10000.0 + prior: + input file: Data/ufo/testinput_tier_1/satbias_amsua_n19.nc4 + inflation: + ratio: 1.1 + ratio for small dataset: 2.0 + tolerance: 1.e-7 diff --git a/test/testinput/bias_linear_op.yaml b/test/testinput/bias_linear_op.yaml new file mode 100644 index 000000000..260b6de33 --- /dev/null +++ b/test/testinput/bias_linear_op.yaml @@ -0,0 +1,91 @@ +# Test obs bias increment in the presence of static bias predictors. + +window begin: '2018-04-14T20:30:00Z' +window end: '2018-04-15T03:30:00Z' + +observations: + +# Two variable predictors +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_s.nc4 + simulated variables: [air_temperature, specific_humidity] + obs operator: + name: VertInterp + geovals: + filename: Data/ufo/testinput_tier_1/aircraft_geoval_2018041500_s.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/aircraft_artificial_bias.nc4 + variational bc: + predictors: + - name: constant + - name: sine_of_latitude + target obs bias: + input file: Data/ufo/testinput_tier_1/aircraft_artificial_bias_2.nc4 + variational bc: + predictors: + - name: constant + - name: sine_of_latitude + rms ref: 1.0222204 + relative tolerance: 1.0e-6 + +# One static and one variable predictor +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_s.nc4 + simulated variables: [air_temperature, specific_humidity] + obs operator: + name: VertInterp + geovals: + filename: Data/ufo/testinput_tier_1/aircraft_geoval_2018041500_s.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/aircraft_artificial_bias.nc4 + static bc: + # All static BC predictors have unit cofficients. + predictors: + - name: sine_of_latitude + variational bc: + # The initial coefficients of predictors used in VarBC are loaded from the input file. + predictors: + - name: constant + target obs bias: + input file: Data/ufo/testinput_tier_1/aircraft_artificial_bias_2.nc4 + static bc: + # All static BC predictors have unit cofficients. + predictors: + - name: sine_of_latitude + # The coefficients of variable predictors are loaded from the input file. + variational bc: + predictors: + - name: constant + rms ref: 0.3535534 + relative tolerance: 1.0e-6 + +# Two static predictors +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_s.nc4 + simulated variables: [air_temperature, specific_humidity] + obs operator: + name: VertInterp + geovals: + filename: Data/ufo/testinput_tier_1/aircraft_geoval_2018041500_s.nc4 + obs bias: + # This file should effectively be ignored, since there are no variable coefficients + input file: Data/ufo/testinput_tier_1/aircraft_artificial_bias.nc4 + static bc: + predictors: + - name: constant + - name: sine_of_latitude + obs bias: + # This file should effectively be ignored, since there are no variable coefficients + input file: Data/ufo/testinput_tier_1/aircraft_artificial_bias_2.nc4 + static bc: + predictors: + - name: constant + - name: sine_of_latitude + rms ref: 0 + relative tolerance: 1e-6 diff --git a/test/testinput/bias_single_channel.yaml b/test/testinput/bias_single_channel.yaml new file mode 100644 index 000000000..150a1e616 --- /dev/null +++ b/test/testinput/bias_single_channel.yaml @@ -0,0 +1,90 @@ +# Test bias correction of multiple single-channel variables. + +window begin: '2018-04-14T20:30:00Z' +window end: '2018-04-15T03:30:00Z' + +observations: +# Two variable predictors +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_s.nc4 + obsdataout: + obsfile: Data/aircraft_obs_2018041500_s_out_bc_2var.nc4 + simulated variables: [air_temperature, specific_humidity] + obs operator: + name: VertInterp + geovals: + filename: Data/ufo/testinput_tier_1/aircraft_geoval_2018041500_s.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/aircraft_artificial_bias.nc4 + variational bc: + predictors: + - name: constant + - name: sine_of_latitude + rms ref: 188.80571 + tolerance: 1.0e-6 +# One static and one variable predictor +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_s.nc4 + obsdataout: + obsfile: Data/aircraft_obs_2018041500_s_out_bc_1static_1var.nc4 + simulated variables: [air_temperature, specific_humidity] + obs operator: + name: VertInterp + geovals: + filename: Data/ufo/testinput_tier_1/aircraft_geoval_2018041500_s.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/aircraft_artificial_bias.nc4 + static bc: + # All static BC predictors have unit cofficients. + predictors: + - name: sine_of_latitude + variational bc: + # The initial coefficients of predictors used in VarBC are loaded from the input file. + predictors: + - name: constant + rms ref: 183.78023 + tolerance: 1.0e-6 +# Two static predictors; input file option present +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_s.nc4 + obsdataout: + obsfile: Data/aircraft_obs_2018041500_s_out_bc_2static.nc4 + simulated variables: [air_temperature, specific_humidity] + obs operator: + name: VertInterp + geovals: + filename: Data/ufo/testinput_tier_1/aircraft_geoval_2018041500_s.nc4 + obs bias: + # This file should effectively be ignored, since there are no variable coefficients + input file: Data/ufo/testinput_tier_1/aircraft_artificial_bias.nc4 + static bc: + predictors: + - name: constant + - name: sine_of_latitude + rms ref: 180.93765 + tolerance: 1.0e-6 +# Two static predictors; input file option absent +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_s.nc4 + obsdataout: + obsfile: Data/aircraft_obs_2018041500_s_out_bc_2static_no_bias_file.nc4 + simulated variables: [air_temperature, specific_humidity] + obs operator: + name: VertInterp + geovals: + filename: Data/ufo/testinput_tier_1/aircraft_geoval_2018041500_s.nc4 + obs bias: + static bc: + predictors: + - name: constant + - name: sine_of_latitude + rms ref: 180.93765 + tolerance: 1.0e-6 diff --git a/test/testinput/categorical.yaml b/test/testinput/categorical.yaml new file mode 100644 index 000000000..298ac1b5e --- /dev/null +++ b/test/testinput/categorical.yaml @@ -0,0 +1,451 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +# The value of `rms ref` is calculated for most of the cases below by determining the +# rms values for each variable and operator separately and combining them as follows: +# sqrt((rms(air_temperature)^2 + rms(eastward_wind)^2 + rms(northward_wind)^2) / 3). +# +# The individual rms values are: +# - eastward_wind: VertInterp: 24.343567787559724, Identity: 7.6217412865213365 +# - northward_wind: VertInterp: 5.5678287067725627, Identity: 5.7432477706044107 +# - air_temperature: Identity: 294.79799709699643 +# +# Two of the cases use a mixture of Identity and Composite operators for different +# station_ids. In those cases the rms ref is taken directly from the output rms value. +# It has been verified that each of these rms values is different to the rms values +# obtained when only one of the operators is used. This indicates that the +# categorical operator is performing as expected. + +observations: + +# Categorical variable is station_id@MetaData. +# Composite operator used as fallback. +# Composite operator used for all values of the categorical variable. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: station_id + fallback operator: "Composite" + categorised operators: {"47418": "Composite", + "54857": "Composite", + "94332": "Composite", + "96935": "Composite"} + operator configurations: + - name: Identity + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.81126924331446 + tolerance: 1.0e-06 + +# Categorical variable is record_number@MetaData. +# Composite operator used as fallback. +# Composite operator used for all values of the categorical variable. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: record_number + fallback operator: "Composite" + categorised operators: {"261": "Composite", + "159": "Composite", + "637": "Composite"} + operator configurations: + - name: Identity + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.81126924331446 + tolerance: 1.0e-06 + +# Categorical variable is station_id@MetaData. +# Composite operator used as fallback. +# No operators assigned to values of the categorical variable, so the fallback will be used everywhere. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: station_id + fallback operator: "Composite" + categorised operators: {} + operator configurations: + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.81126924331446 + tolerance: 1.0e-06 + +# Categorical variable is station_id@MetaData. +# Composite operator used as fallback. +# Identity operator used for one value of the categorical variable. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: station_id + fallback operator: "Composite" + categorised operators: {"47418": "Identity"} + operator configurations: + - name: Identity + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.58276494804048 + tolerance: 1.0e-06 + +# Categorical variable is station_id@MetaData. +# Composite operator used as fallback. +# Identity operator used for all values of the categorical variable. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: station_id + fallback operator: "Composite" + categorised operators: {"47418": "Identity", + "54857": "Identity", + "94332": "Identity", + "96935": "Identity"} + operator configurations: + - name: Identity + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.29086384535719 + tolerance: 1.0e-06 + +# Categorical variable is station_id@MetaData. +# Composite operator used as fallback. +# Composite and Identity operators used for different values of the categorical variable. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: station_id + fallback operator: "Composite" + categorised operators: {"47418": "Identity", + "54857": "Composite", + "94332": "Identity", + "96935": "Composite"} + operator configurations: + - name: Identity + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.37753256777677 + tolerance: 1.0e-06 + +# Categorical variable is station_id@MetaData. +# Composite operator used as fallback. +# Composite operator used for all values of the categorical variable. +# Duplicate entry in categorised operators list (expect this to have no effect). +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: station_id + fallback operator: "Composite" + categorised operators: {"47418": "Composite", + "54857": "Composite", + "94332": "Composite", + "96935": "Composite", + "54857": "Composite"} + operator configurations: + - name: Identity + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.81126924331446 + tolerance: 1.0e-06 + +# Categorical variable is station_id@MetaData. +# Composite operator used as fallback. +# Composite operator used for all values of the categorical variable. +# Duplicate entry in operator configurations list (expect this to have no effect). +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: station_id + fallback operator: "Composite" + categorised operators: {"47418": "Composite", + "54857": "Composite", + "94332": "Composite", + "96935": "Composite"} + operator configurations: + - name: Identity + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.81126924331446 + tolerance: 1.0e-06 + +# An invalid operator is provided for the fallback operator, so expect an exception to be thrown. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: station_id + fallback operator: "Nonexistent" + categorised operators: {"47418": "Composite", + "54857": "Composite", + "94332": "Composite", + "96935": "Composite"} + operator configurations: + - name: Identity + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + expect constructor to throw exception with message: The operator Nonexistent has not been configured + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.81126924331446 + tolerance: 1.0e-06 + +# An invalid operator is provided for one of the categorised operators, so expect an exception to be thrown. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: station_id + fallback operator: "Composite" + categorised operators: {"47418": "Composite", + "54857": "Nonexistent", + "94332": "Composite", + "96935": "Composite"} + operator configurations: + - name: Identity + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + expect constructor to throw exception with message: The operator Nonexistent has not been configured + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.81126924331446 + tolerance: 1.0e-06 + +# Incorrect type of categorical variable, so expect an exception to be thrown. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: air_pressure + fallback operator: "Composite" + categorised operators: {"47418": "Composite", + "54857": "Nonexistent", + "94332": "Composite", + "96935": "Composite"} + operator configurations: + - name: Identity + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + expect constructor to throw exception with message: The categorical variable must be a vector of either strings or integers + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.81126924331446 + tolerance: 1.0e-06 + +# A nonexistent categorical variable is provided, so expect an exception to be thrown. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Categorical + categorical variable: Nonexistent + fallback operator: "Composite" + categorised operators: {"47418": "Composite", + "54857": "Nonexistent", + "94332": "Composite", + "96935": "Composite"} + operator configurations: + - name: Identity + - name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + expect constructor to throw exception with message: The categorical variable Nonexistent does not exist + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 170.81126924331446 + tolerance: 1.0e-06 diff --git a/test/testinput/chleuzintegr.yaml b/test/testinput/chleuzintegr.yaml index b4f56c127..ed5f8bce3 100644 --- a/test/testinput/chleuzintegr.yaml +++ b/test/testinput/chleuzintegr.yaml @@ -9,7 +9,7 @@ observations: obs space: name: Chlorophyll obsdatain: - obsfile: Data/ioda/testinput_tier_1/viirs_jpss1_oc_l2_2018-04-15.nc + obsfile: Data/ufo/testinput_tier_1/viirs_jpss1_oc_l2_2018-04-15.nc simulated variables: [mass_concentration_of_chlorophyll_in_sea_water] geovals: filename: Data/ufo/testinput_tier_1/viirs_jpss1_oc_l2_2018-04-15_geovals.nc diff --git a/test/testinput/composite.yaml b/test/testinput/composite.yaml new file mode 100644 index 000000000..bb17b194d --- /dev/null +++ b/test/testinput/composite.yaml @@ -0,0 +1,244 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: + +# The commented-out part of this file defines four "elementary" operators (2x vertical +# interpolation and 2x identity) applied to single variables. The RMS values of vectors produced by +# these operators were used to derive the reference RMS values for tests of the Composite operator +# (below). The commented-out code is preserved here in case these values need to be recalculated, +# e.g. because of a change in the test files. +# +#- obs space: +# name: Radiosonde +# obsdatain: +# obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 +# simulated variables: [eastward_wind] +# obs operator: +# name: VertInterp +# vertical coordinate: air_pressure +# geovals: +# filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 +# rms ref: 24.343567787559724 +# tolerance: 1.0e-06 +#- obs space: +# name: Radiosonde +# obsdatain: +# obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 +# simulated variables: [northward_wind] +# obs operator: +# name: VertInterp +# vertical coordinate: air_pressure +# geovals: +# filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 +# rms ref: 5.5678287067725627 +# tolerance: 1.0e-06 +#- obs space: +# name: Radiosonde +# obsdatain: +# obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 +# simulated variables: [surface_pressure] +# obs operator: +# name: Identity +# geovals: +# filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 +# rms ref: 98283.406637309454 +# tolerance: 1.0e-06 +#- obs space: +# name: Radiosonde +# obsdatain: +# obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 +# # We use the identity operator here just for testing purposes -- in reality we'd use the +# # VertInterp operator +# simulated variables: [air_temperature] +# obs operator: +# name: Identity +# geovals: +# filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 +# rms ref: 294.79799709699643 +# tolerance: 1.0e-06 + +# Composite operator (with the identity operator as the first component and +# the vertical interpolation operator as the second) +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, surface_pressure, northward_wind, air_temperature] + obs operator: + name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: surface_pressure + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + # The reference value is calculated as + # sqrt( (rms(air_temperature)^2 + rms(surface_pressure)^2 + + # rms(eastward_wind)^2 + rms(northward_wind)^2) / 4) + # with the values of rms(...) taken from the four commented-out test cases at the top of this file + rms ref: 49141.92596374258 + tolerance: 1.0e-06 +# Composite operator (with components listed in the opposite order) +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, surface_pressure, northward_wind, air_temperature] + obs operator: + name: Composite + components: + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + - name: Identity + variables: + - name: air_temperature + - name: surface_pressure + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + # The reference value is calculated as + # sqrt( (rms(air_temperature)^2 + rms(surface_pressure)^2 + + # rms(eastward_wind)^2 + rms(northward_wind)^2) / 4) + # with the values of rms(...) taken from the four commented-out test cases at the top of this file + rms ref: 49141.92596374258 + tolerance: 1.0e-06 +# Invalid composite operator with two components said to simulate the same variable +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind] + obs operator: + name: Composite + components: + - name: VertInterp + variables: + - name: eastward_wind + - name: Identity + variables: + - name: eastward_wind + expect constructor to throw exception with message: Multiple components simulate the same variables + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + rms ref: 0 # bogus value; simulateObs won't be called because the constructor will throw an exception + tolerance: 1.0e-06 +# Invalid composite operator with two components said to simulate the same variables +# (listed in different order, which shouldn't matter, though) +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind] + obs operator: + name: Composite + components: + - name: VertInterp + variables: + - name: eastward_wind + - name: northward_wind + - name: Identity + variables: + - name: northward_wind + - name: eastward_wind + expect constructor to throw exception with message: Multiple components simulate the same variables + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + rms ref: 0 # bogus value; simulateObs won't be called because the constructor will throw an exception + tolerance: 1.0e-06 +# Invalid composite operator with some variables not simulated by any component +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, northward_wind, air_temperature] + obs operator: + name: Composite + components: + - name: VertInterp + variables: + - name: eastward_wind + - name: Identity + variables: + - name: northward_wind + expect constructor to throw exception with message: The list of variables simulated by the obs operator differs from the list of simulated variables in the obs space + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + rms ref: 0 # bogus value; simulateObs won't be called because the constructor will throw an exception + tolerance: 1.0e-06 +# Composite operator with the coordinates listed explicitly +- obs space: + name: Radiosonde with coordinates listed explicitly + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, surface_pressure, northward_wind, air_temperature] + obs operator: + name: Composite + components: + - name: Identity + variables: + - name: air_temperature + - name: surface_pressure + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + vertical coordinate: air_pressure + observation vertical coordinate: air_pressure + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_s.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + # The reference value is calculated as + # sqrt( (rms(air_temperature)^2 + rms(surface_pressure)^2 + + # rms(eastward_wind)^2 + rms(northward_wind)^2) / 4) + # with the values of rms(...) taken from the four commented-out test cases at the top of this file + rms ref: 49141.92596374258 + tolerance: 1.0e-06 +# Composite operator with staggered vertical levels +- obs space: + name: Radiosonde with staggered vertical levels + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_composite_operator_sonde_obs.nc4 + simulated variables: [eastward_wind, surface_pressure, northward_wind, air_temperature] + obs operator: + name: Composite + components: + - name: Identity + variables: + - name: surface_pressure + - name: VertInterp + variables: + - name: air_temperature + vertical coordinate: air_pressure + observation vertical coordinate: air_pressure + - name: VertInterp + variables: + - name: northward_wind + - name: eastward_wind + vertical coordinate: air_pressure_levels + observation vertical coordinate: air_pressure + geovals: + filename: Data/ufo/testinput_tier_1/met_office_composite_operator_sonde_geovals.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + vector ref: MetOfficeHofX + tolerance: 1.0e-06 diff --git a/test/testinput/constant_predictor.yaml b/test/testinput/constant_predictor.yaml new file mode 100644 index 000000000..dbef717cc --- /dev/null +++ b/test/testinput/constant_predictor.yaml @@ -0,0 +1,19 @@ +window begin: 2019-12-29T21:00:00Z +window end: 2019-12-30T03:00:00Z + +observations: +- obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_n20_obs_20191230T0000_rttov_predictors.nc4 + obsdataout: + obsfile: Data/atms_npp_obs_2019123000_m_rttov_out_bias.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/geovals_atms_20191230T0000Z_benchmark.nc4 + obs bias: + variational bc: + predictors: + - name: constant + tolerance: 1.0e-16 diff --git a/test/testinput/coolskin.yaml b/test/testinput/coolskin.yaml index cae7689cf..c19e280e9 100644 --- a/test/testinput/coolskin.yaml +++ b/test/testinput/coolskin.yaml @@ -11,7 +11,7 @@ observations: obs space: name: CoolSkin obsdatain: - obsfile: Data/ioda/testinput_tier_1/coolskin_fake_obs_2018041500.nc + obsfile: Data/ufo/testinput_tier_1/coolskin_fake_obs_2018041500.nc simulated variables: [sea_surface_temperature] geovals: filename: Data/ufo/testinput_tier_1/coolskin_fake_geovals_2018041500.nc diff --git a/test/testinput/cris_crtm.yaml b/test/testinput/cris_crtm.yaml index fa16714c1..04c3cd591 100644 --- a/test/testinput/cris_crtm.yaml +++ b/test/testinput/cris_crtm.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: cris-fsr_npp EndianType: little_endian @@ -12,7 +13,7 @@ observations: obs space: name: cris-fsr_npp obsdatain: - obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 + obsfile: Data/ufo/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 simulated variables: [brightness_temperature] channels: &all_channels 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, diff --git a/test/testinput/cris_qc.yaml b/test/testinput/cris_qc.yaml index f9567e596..2fc38c80c 100644 --- a/test/testinput/cris_qc.yaml +++ b/test/testinput/cris_qc.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: cris-fsr_npp EndianType: little_endian @@ -12,9 +13,9 @@ observations: obs space: name: cris-fsr_npp obsdatain: - obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 -# obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_qc.nc4 -# obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 +# obsfile: Data/ufo/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_qc.nc4 +# obsfile: Data/ufo/testinput_tier_1/cris-fsr_npp_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: &all_channels 24,26,28,32,37,39,42,44,47,49,51,53,55,57,59,61,63, diff --git a/test/testinput/cris_qc_filters.yaml b/test/testinput/cris_qc_filters.yaml index 5af433009..33b164b74 100755 --- a/test/testinput/cris_qc_filters.yaml +++ b/test/testinput/cris_qc_filters.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: &Sensor_ID cris-fsr_npp EndianType: little_endian @@ -12,8 +13,8 @@ observations: obs space: name: cris-fsr_npp obsdatain: -# obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_qc.nc4 - obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 +# obsfile: Data/ufo/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 # obsdataout: # obsfile: Data/cris-fsr_npp_obs_2018041500_m_qc_out.nc4 # obsfile: Data/cris-fsr_npp_obs_2018041500_m_unittest_out.nc4 @@ -55,37 +56,28 @@ observations: # filename: Data/ufo/testinput_tier_1/cris-fsr_npp_geoval_2018041500_m_qc.nc4 filename: Data/ufo/testinput_tier_1/cris-fsr_npp_geoval_2018041500_m_unittest.nc4 obs bias: - abias_in: Data/ufo/testinput_tier_1/satbias_crtm_in - sensor: *Sensor_ID - jobs: *all_channels - predictors: - - predictor: - name: constant - - predictor: - name: lapse_rate + input file: Data/ufo/testinput_tier_1/satbias_cris-fsr_npp.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate options: order: 2 tlapse: &cris-fsr_npp-atlap Data/ufo/testinput_tier_1/cris-fsr_npp_tlapmean.txt - - predictor: - name: lapse_rate + - name: lapse_rate options: tlapse: *cris-fsr_npp-atlap - - predictor: - name: emissivity - - predictor: - name: scan_angle + - name: emissivity + - name: scan_angle options: order: 4 - - predictor: - name: scan_angle + - name: scan_angle options: order: 3 - - predictor: - name: scan_angle + - name: scan_angle options: order: 2 - - predictor: - name: scan_angle + - name: scan_angle obs filters: # Wavenumber Check - filter: BlackList diff --git a/test/testinput/cris_qc_land.yaml b/test/testinput/cris_qc_land.yaml index ef65ff545..516d08f5c 100644 --- a/test/testinput/cris_qc_land.yaml +++ b/test/testinput/cris_qc_land.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: cris-fsr_npp EndianType: little_endian @@ -12,8 +13,8 @@ observations: obs space: name: cris-fsr_npp obsdatain: -# obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_qc.nc4 - obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 +# obsfile: Data/ufo/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 # obsdataout: # obsfile: Data/iasi_metop-a_obs_2018041500_m_qc_out.nc4 # obsfile: Data/iasi_metop-a_obs_2018041500_m_unittest_out.nc4 diff --git a/test/testinput/filters/filters_testdata.nc4 b/test/testinput/filters/filters_testdata.nc4 deleted file mode 100644 index 389c673cc..000000000 --- a/test/testinput/filters/filters_testdata.nc4 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d494f86ea847d2ac5680e9dd44b3cdd0d92e3d7a950de200e33f1d2085732bb8 -size 61734 diff --git a/test/testinput/filters/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 b/test/testinput/filters/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 deleted file mode 100644 index 8f76baea9..000000000 --- a/test/testinput/filters/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a07f0c892141c85a5ef8269227bcf980ad39f6eecbd7eb6f0b5f6aaf64397dee -size 22476 diff --git a/test/testinput/function_bennartzscatindex.yaml b/test/testinput/function_bennartzscatindex.yaml new file mode 100644 index 000000000..7b0e3c7d0 --- /dev/null +++ b/test/testinput/function_bennartzscatindex.yaml @@ -0,0 +1,23 @@ +window begin: 2019-12-29T21:00:00Z +window end: 2019-12-30T03:00:00Z + +observations: +- obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_n20_obs_20191230T0000_rttov.nc4 + simulated variables: [brightness_temperature] + channels: 16, 17 + obs function: + name: BennartzScatIndex@ObsFunction + options: + channel_89ghz: 16 + channel_150ghz: 17 + bennartz_coeff_1: 0.158 # example coeffients over land + bennartz_coeff_2: 0.0163 + apply_bias: ObsBias + variables: [bennartz_scattering_index] + tolerance: 1.0e-7 + + + diff --git a/test/testinput/function_bgddepartureanomaly.yaml b/test/testinput/function_bgddepartureanomaly.yaml new file mode 100644 index 000000000..a302eab36 --- /dev/null +++ b/test/testinput/function_bgddepartureanomaly.yaml @@ -0,0 +1,22 @@ +window begin: 2019-12-29T21:00:00Z +window end: 2019-12-30T03:00:00Z + +observations: +- obs space: + name: amsr2 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsr2_obs_20191230T0000Z_100subset.nc4 + simulated variables: [brightness_temperature] + channels: 7-9,11-13 + geovals: + filename: Data/ufo/testinput_tier_1/amsr2_geovals_20191230T0000Z_100subset.nc4 + obs function: + name: BgdDepartureAnomaly@ObsFunction + options: + channel_low_freq: 11 + channel_high_freq: 13 + test_hofx: MetOfficeHofX + ObsBias: ObsBias + variables: [mwicloud] + tolerance: 1.0e-6 + diff --git a/test/testinput/function_cloudcost.yaml b/test/testinput/function_cloudcost.yaml new file mode 100644 index 000000000..7cc40c48f --- /dev/null +++ b/test/testinput/function_cloudcost.yaml @@ -0,0 +1,60 @@ +window begin: 2020-03-31T21:00:00Z +window end: 2020-04-01T03:00:00Z + +observations: +- obs operator: + name: RTTOV + GeoVal_type: MetO + Absorbers: &rttov_absobers [Water_vapour, CLW] + linear obs operator: + Absorbers: [Water_vapour] + obs options: &rttov_options + RTTOV_default_opts: UKMO_PS45 + SatRad_compatibility: true + Sensor_ID: noaa_20_atms + CoefficientPath: Data/ + RTTOV_GasUnitConv: true + UseRHwaterForQC: true + obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_obs_20200401T0000Z_10locs.nc4 + simulated variables: [brightness_temperature] + channels: 18-22 + geovals: + filename: Data/ufo/testinput_tier_1/atms_geovals_20200401T0000Z_10locs.nc4 + obs filters: + - filter: Variable Assignment + assignments: + - name: cloud_cost@MetaData + type: float + function: + name: CloudCostFunction@ObsFunction + options: + cost channels list: 18, 20, 22 + RMatrix: ../resources/rmatrix/rttov/atms_noaa_20_rmatrix_test.nc4 + BMatrix: ../resources/bmatrix/rttov/atms_bmatrix_70_test.dat + background fields: + - air_temperature + - specific_humidity + - mass_content_of_cloud_liquid_water_in_atmosphere_layer + - mass_content_of_cloud_ice_in_atmosphere_layer + - surface_temperature + - specific_humidity_at_two_meters_above_surface + - skin_temperature + - air_pressure_at_two_meters_above_surface + qtotal: true + qtotal split rain: true + reverse Jacobian order: true + scattering radiative transfer: false # default + minimum ObsValue: 70.0 # default + maximum ObsValue: 340.0 # default + minimum specific humidity: 3.0e-6 # default + maximum final cost: 1600.0 # default + HofX group: MetOfficeBiasCorrHofX + compareVariables: + - reference: + name: cloud_cost@TestReference + test: + name: cloud_cost@MetaData + absTol: 1.0e-4 diff --git a/test/testinput/function_clouddetect_airs.yaml b/test/testinput/function_clouddetect_airs.yaml index d8b2be2e3..df1efe972 100755 --- a/test/testinput/function_clouddetect_airs.yaml +++ b/test/testinput/function_clouddetect_airs.yaml @@ -1,107 +1,107 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: airs_aqua - obsdatain: - obsfile: Data/ioda/testinput_tier_1/airs_aqua_obs_2018041500_m_unittest.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels - 1, 6, 7, 10, 11, 15, 16, 17, 20, 21, 22, 24, - 27, 28, 30, 36, 39, 40, 42, 51, 52, 54, 55, 56, 59, 62, 63, 68, 69, 71, - 72, 73, 74, 75, 76, 77, 78, 79, 80, 82, 83, 84, 86, 92, 93, 98, 99, 101, - 104, 105, 108, 110, 111, 113, 116, 117, 123, 124, 128, 129, 138, 139, - 144, 145, 150, 151, 156, 157, 159, 162, 165, 168, 169, 170, 172, 173, - 174, 175, 177, 179, 180, 182, 185, 186, 190, 192, 198, 201, 204, 207, - 210, 215, 216, 221, 226, 227, 232, 252, 253, 256, 257, 261, 262, 267, - 272, 295, 299, 300, 305, 310, 321, 325, 333, 338, 355, 362, 375, 453, - 475, 484, 497, 528, 587, 672, 787, 791, 843, 870, 914, 950, 1003, 1012, - 1019, 1024, 1030, 1038, 1048, 1069, 1079, 1082, 1083, 1088, 1090, 1092, - 1095, 1104, 1111, 1115, 1116, 1119, 1120, 1123, 1130, 1138, 1142, 1178, - 1199, 1206, 1221, 1237, 1252, 1260, 1263, 1266, 1285, 1301, 1304, 1329, - 1371, 1382, 1415, 1424, 1449, 1455, 1466, 1477, 1500, 1519, 1538, 1545, - 1565, 1574, 1583, 1593, 1614, 1627, 1636, 1644, 1652, 1669, 1674, 1681, - 1694, 1708, 1717, 1723, 1740, 1748, 1751, 1756, 1763, 1766, 1771, 1777, - 1780, 1783, 1794, 1800, 1803, 1806, 1812, 1826, 1843, 1852, 1865, 1866, - 1868, 1869, 1872, 1873, 1876, 1881, 1882, 1883, 1911, 1917, 1918, 1924, - 1928, 1937, 1941, 2099, 2100, 2101, 2103, 2104, 2106, 2107, 2108, 2109, - 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, - 2122, 2123, 2128, 2134, 2141, 2145, 2149, 2153, 2164, 2189, 2197, 2209, - 2226, 2234, 2280, 2318, 2321, 2325, 2328, 2333, 2339, 2348, 2353, 2355, - 2357, 2363, 2370, 2371, 2377 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/airs_aqua_geoval_2018041500_m_unittest.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/airs_aqua_obsdiag_2018041500_m_unittest.nc4 -obs function: - name: CloudDetectMinResidualIR@ObsFunction - options: +observations: +- obs space: + name: airs_aqua + obsdatain: + obsfile: Data/ufo/testinput_tier_1/airs_aqua_obs_2018041500_m_unittest.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 1, 6, 7, 10, 11, 15, 16, 17, 20, 21, 22, 24, + 27, 28, 30, 36, 39, 40, 42, 51, 52, 54, 55, 56, 59, 62, 63, 68, 69, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 82, 83, 84, 86, 92, 93, 98, 99, 101, + 104, 105, 108, 110, 111, 113, 116, 117, 123, 124, 128, 129, 138, 139, + 144, 145, 150, 151, 156, 157, 159, 162, 165, 168, 169, 170, 172, 173, + 174, 175, 177, 179, 180, 182, 185, 186, 190, 192, 198, 201, 204, 207, + 210, 215, 216, 221, 226, 227, 232, 252, 253, 256, 257, 261, 262, 267, + 272, 295, 299, 300, 305, 310, 321, 325, 333, 338, 355, 362, 375, 453, + 475, 484, 497, 528, 587, 672, 787, 791, 843, 870, 914, 950, 1003, 1012, + 1019, 1024, 1030, 1038, 1048, 1069, 1079, 1082, 1083, 1088, 1090, 1092, + 1095, 1104, 1111, 1115, 1116, 1119, 1120, 1123, 1130, 1138, 1142, 1178, + 1199, 1206, 1221, 1237, 1252, 1260, 1263, 1266, 1285, 1301, 1304, 1329, + 1371, 1382, 1415, 1424, 1449, 1455, 1466, 1477, 1500, 1519, 1538, 1545, + 1565, 1574, 1583, 1593, 1614, 1627, 1636, 1644, 1652, 1669, 1674, 1681, + 1694, 1708, 1717, 1723, 1740, 1748, 1751, 1756, 1763, 1766, 1771, 1777, + 1780, 1783, 1794, 1800, 1803, 1806, 1812, 1826, 1843, 1852, 1865, 1866, + 1868, 1869, 1872, 1873, 1876, 1881, 1882, 1883, 1911, 1917, 1918, 1924, + 1928, 1937, 1941, 2099, 2100, 2101, 2103, 2104, 2106, 2107, 2108, 2109, + 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, + 2122, 2123, 2128, 2134, 2141, 2145, 2149, 2153, 2164, 2189, 2197, 2209, + 2226, 2234, 2280, 2318, 2321, 2325, 2328, 2333, 2339, 2348, 2353, 2355, + 2357, 2363, 2370, 2371, 2377 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/airs_aqua_geoval_2018041500_m_unittest.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/airs_aqua_obsdiag_2018041500_m_unittest.nc4 + obs function: + name: CloudDetectMinResidualIR@ObsFunction + options: + channels: *all_channels + test_qcflag: PreQC + test_obserr: GsiObsError_clddet + test_hofx: GsiHofXBc + use_flag: [ -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, + 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, + 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, + 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + use_flag_clddet: [ -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, + 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, + 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, + 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] + variables: [cloudy_channel] channels: *all_channels - test_qcflag: PreQC - test_obserr: GsiObsError_clddet - test_hofx: GsiHofX - test_bias: GsiObsBias - use_flag: [ -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, - 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, - 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, - 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, - -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, - -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, - 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, - -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, - 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, - 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, - -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, - -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, - -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1 ] - use_flag_clddet: [ -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, - 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, - 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, - 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, - -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, - -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, - 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, - -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, - 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, - 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, - -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, - -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, - -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1 ] - obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] - variables: [cloudy_channel] - channels: *all_channels - tolerance: 1.0e-8 + tolerance: 1.0e-8 diff --git a/test/testinput/function_clouddetect_cris.yaml b/test/testinput/function_clouddetect_cris.yaml index 9451d814a..e64f70a3f 100755 --- a/test/testinput/function_clouddetect_cris.yaml +++ b/test/testinput/function_clouddetect_cris.yaml @@ -1,147 +1,147 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: cris-fsr_npp - obsdatain: - obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels - 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, - 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, - 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, - 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, - 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, - 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, - 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, - 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, - 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, - 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, - 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, - 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, - 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, - 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, - 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, - 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, - 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, - 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, - 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, - 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, - 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, - 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, - 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, - 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, - 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, - 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, - 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, - 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, - 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, - 2158, 2161, 2168, 2171, 2175, 2182 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/cris-fsr_npp_geoval_2018041500_m_unittest.nc4 -# can have obs diagnostics section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/cris-fsr_npp_obsdiag_2018041500_m_unittest.nc4 -obs function: - name: CloudDetectMinResidualIR@ObsFunction - options: +observations: +- obs space: + name: cris-fsr_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/cris-fsr_npp_geoval_2018041500_m_unittest.nc4 + # can have obs diagnostics section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/cris-fsr_npp_obsdiag_2018041500_m_unittest.nc4 + obs function: + name: CloudDetectMinResidualIR@ObsFunction + options: + channels: *all_channels + test_qcflag: PreQC + test_obserr: GsiObsError_clddet + test_hofx: GsiHofXBc + use_flag: [ -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, + 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + use_flag_clddet: [ -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + obserr_dtempf: [0.5,2.0,4.0,2.0,4.0] + variables: [cloudy_channel] channels: *all_channels - test_qcflag: PreQC - test_obserr: GsiObsError_clddet - test_hofx: GsiHofX - test_bias: GsiObsBias - use_flag: [ -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, - 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, - -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, - -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, - -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, - -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, - -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, - -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, - -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, - -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, - -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, - -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, - -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, - -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, - -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, - -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, - -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, - 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, - -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, - -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, - 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, - -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, - 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1 ] - use_flag_clddet: [ -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, - 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, - -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, - -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, - -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, - -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, - -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, - -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, - -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, - -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, - -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, - -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, - -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, - -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, - -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, - -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, - -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, - 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, - -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, - -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1 ] - obserr_dtempf: [0.5,2.0,4.0,2.0,4.0] - variables: [cloudy_channel] - channels: *all_channels - tolerance: 1.0e-8 + tolerance: 1.0e-8 diff --git a/test/testinput/function_clouddetect_iasi.yaml b/test/testinput/function_clouddetect_iasi.yaml index 7771f52a0..37b27e437 100755 --- a/test/testinput/function_clouddetect_iasi.yaml +++ b/test/testinput/function_clouddetect_iasi.yaml @@ -1,140 +1,140 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: iasi_metop-a - obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, - 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, - 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, - 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, - 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, - 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, - 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, - 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, - 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, - 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, - 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, - 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, - 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, - 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, - 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, - 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, - 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, - 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, - 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, - 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, - 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, - 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, - 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, - 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, - 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, - 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, - 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, - 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, - 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, - 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, - 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, - 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, - 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, - 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, - 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, - 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, - 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, - 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, - 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, - 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, - 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, - 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, - 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, - 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, - 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, - 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, - 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, - 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, - 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 -obs function: - name: CloudDetectMinResidualIR@ObsFunction - options: +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 + obs function: + name: CloudDetectMinResidualIR@ObsFunction + options: + channels: *all_channels + test_qcflag: PreQC + test_obserr: GsiObsError_clddet + test_hofx: GsiHofXBc + use_flag: [1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, + -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, + 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, + 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, + 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, + -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, + 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, + -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] + use_flag_clddet: [1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, + -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, + -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, + -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, + -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, + 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, + 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, + 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] + obserr_dtempf: [0.5,2.0,4.0,2.0,4.0] + variables: [cloudy_channel] channels: *all_channels - test_qcflag: PreQC - test_obserr: GsiObsError_clddet - test_hofx: GsiHofX - test_bias: GsiObsBias - use_flag: [1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, - -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, - -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, - 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, - -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, - 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, - -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, - 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, - -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, - 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, - -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, - -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, - 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, - -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] - use_flag_clddet: [1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, - -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, - -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, - 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, - -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, - 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, - 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, - -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, - 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, - 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, - 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] - obserr_dtempf: [0.5,2.0,4.0,2.0,4.0] - variables: [cloudy_channel] - channels: *all_channels - tolerance: 1.0e-8 + tolerance: 1.0e-8 diff --git a/test/testinput/function_clwmatchidx.yaml b/test/testinput/function_clwmatchidx.yaml index 92a6b2864..148ed78b6 100755 --- a/test/testinput/function_clwmatchidx.yaml +++ b/test/testinput/function_clwmatchidx.yaml @@ -1,36 +1,34 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: amsua_n19 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 1-15 -geovals: - filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 -obs function: - name: CLWMatchIndexMW@ObsFunction - options: +observations: +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 + obs function: + name: CLWMatchIndexMW@ObsFunction + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [GsiHofXBc] + clwret_clearsky: [0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + variables: [Cloud_Match_Index] channels: *all_channels - clwobs_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue] - test_bias: GsiObsBias - clwbkg_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - clwret_clearsky: [0.050, 0.030, 0.030, 0.020, 0.000, - 0.100, 0.000, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.030] - variables: [Cloud_Match_Index] - channels: *all_channels - tolerance: 1.0e-8 + tolerance: 1.0e-8 diff --git a/test/testinput/function_clwmatchidx_atms.yaml b/test/testinput/function_clwmatchidx_atms.yaml index 807fb2f40..a92b47824 100755 --- a/test/testinput/function_clwmatchidx_atms.yaml +++ b/test/testinput/function_clwmatchidx_atms.yaml @@ -1,38 +1,36 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: atms_npp - obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 1-22 -geovals: - filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 -obs function: - name: CLWMatchIndexMW@ObsFunction - options: +observations: +- obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 + obs function: + name: CLWMatchIndexMW@ObsFunction + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [GsiHofXBc] + clwret_clearsky: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + variables: [Cloud_Match_Index] channels: *all_channels - clwobs_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue] - test_bias: GsiObsBias - clwbkg_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - clwret_clearsky: [ 0.030, 0.030, 0.030, 0.020, 0.030, - 0.080, 0.150, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.000, - 0.020, 0.030, 0.030, 0.030, 0.030, - 0.050, 0.100] - variables: [Cloud_Match_Index] - channels: *all_channels - tolerance: 1.0e-8 + tolerance: 1.0e-8 diff --git a/test/testinput/function_clwret.yaml b/test/testinput/function_clwret.yaml index 9b7a68c44..3b8700058 100755 --- a/test/testinput/function_clwret.yaml +++ b/test/testinput/function_clwret.yaml @@ -1,21 +1,20 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: amsua_n19 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: 1, 2 -geovals: - filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 -obs function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue, GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - variables: [clw_retrieved_from_observation, clw_retrieved_from_background] - tolerance: 1.0e-8 +observations: +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: 1, 2 + geovals: + filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 + obs function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, GsiHofXBc] + variables: [clw_retrieved_from_observation, clw_retrieved_from_background] + tolerance: 1.0e-8 diff --git a/test/testinput/function_clwret_atms.yaml b/test/testinput/function_clwret_atms.yaml index 4f167f0d0..daec8bc65 100755 --- a/test/testinput/function_clwret_atms.yaml +++ b/test/testinput/function_clwret_atms.yaml @@ -1,21 +1,20 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: atms_npp - obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: 1, 2 -geovals: - filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 -obs function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue, GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - variables: [clw_retrieved_from_observation, clw_retrieved_from_background] - tolerance: 1.0e-8 +observations: +- obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: 1, 2 + geovals: + filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 + obs function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, GsiHofXBc] + variables: [clw_retrieved_from_observation, clw_retrieved_from_background] + tolerance: 1.0e-8 diff --git a/test/testinput/function_clwret_hofx.yaml b/test/testinput/function_clwret_hofx.yaml index 1b75277f2..49cd6c84d 100755 --- a/test/testinput/function_clwret_hofx.yaml +++ b/test/testinput/function_clwret_hofx.yaml @@ -1,21 +1,20 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: amsua_n19 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: 1, 2 -geovals: - filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 -obs function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - variables: [clw_retrieved_from_background] - tolerance: 1.0e-8 +observations: +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: 1, 2 + geovals: + filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 + obs function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [GsiHofXBc] + variables: [clw_retrieved_from_background] + tolerance: 1.0e-8 diff --git a/test/testinput/function_clwret_hofx_atms.yaml b/test/testinput/function_clwret_hofx_atms.yaml index 138b61deb..f6915eeae 100755 --- a/test/testinput/function_clwret_hofx_atms.yaml +++ b/test/testinput/function_clwret_hofx_atms.yaml @@ -1,21 +1,20 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: atms_npp - obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: 1, 2 -geovals: - filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 -obs function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - variables: [clw_retrieved_from_background] - tolerance: 1.0e-8 +observations: +- obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: 1, 2 + geovals: + filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 + obs function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [GsiHofXBc] + variables: [clw_retrieved_from_background] + tolerance: 1.0e-8 diff --git a/test/testinput/function_clwret_obsval.yaml b/test/testinput/function_clwret_obsval.yaml index ecbeae830..4e5fd4210 100755 --- a/test/testinput/function_clwret_obsval.yaml +++ b/test/testinput/function_clwret_obsval.yaml @@ -1,20 +1,20 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: amsua_n19 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: 1, 2 -geovals: - filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 -obs function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue] - test_bias: GsiObsBias - variables: [clw_retrieved_from_observation] - tolerance: 1.0e-8 +observations: +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: 1, 2 + geovals: + filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 + obs function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + variables: [clw_retrieved_from_observation] + tolerance: 1.0e-8 diff --git a/test/testinput/function_clwret_obsval_atms.yaml b/test/testinput/function_clwret_obsval_atms.yaml index f78e52ce8..f0487bdf1 100755 --- a/test/testinput/function_clwret_obsval_atms.yaml +++ b/test/testinput/function_clwret_obsval_atms.yaml @@ -1,20 +1,20 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: atms_npp - obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: 1, 2 -geovals: - filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 -obs function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue] - test_bias: GsiObsBias - variables: [clw_retrieved_from_observation] - tolerance: 1.0e-8 +observations: +- obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: 1, 2 + geovals: + filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 + obs function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + variables: [clw_retrieved_from_observation] + tolerance: 1.0e-8 diff --git a/test/testinput/function_clwretmean.yaml b/test/testinput/function_clwretmean.yaml index 2c97b02fb..6c70a765b 100755 --- a/test/testinput/function_clwretmean.yaml +++ b/test/testinput/function_clwretmean.yaml @@ -1,21 +1,20 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: amsua_n19 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: 1, 2 -geovals: - filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 -obs function: - name: CLWRetSymmetricMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue, GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - variables: [clw_symmetric_amount] - tolerance: 1.0e-8 +observations: +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: 1, 2 + geovals: + filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 + obs function: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, GsiHofXBc] + variables: [clw_symmetric_amount] + tolerance: 1.0e-8 diff --git a/test/testinput/function_clwretmean_atms.yaml b/test/testinput/function_clwretmean_atms.yaml index 685d30617..d3fdc7265 100755 --- a/test/testinput/function_clwretmean_atms.yaml +++ b/test/testinput/function_clwretmean_atms.yaml @@ -1,21 +1,20 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: atms_npp - obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: 1, 2 -geovals: - filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 -obs function: - name: CLWRetSymmetricMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue, GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - variables: [clw_symmetric_amount] - tolerance: 1.0e-8 +observations: +- obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: 1, 2 + geovals: + filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 + obs function: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, GsiHofXBc] + variables: [clw_symmetric_amount] + tolerance: 1.0e-8 diff --git a/test/testinput/function_errfgrosschk.yaml b/test/testinput/function_errfgrosschk.yaml index 31f287345..dd6c641c1 100755 --- a/test/testinput/function_errfgrosschk.yaml +++ b/test/testinput/function_errfgrosschk.yaml @@ -1,143 +1,144 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: iasi_metop-a - obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, - 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, - 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, - 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, - 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, - 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, - 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, - 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, - 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, - 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, - 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, - 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, - 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, - 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, - 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, - 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, - 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, - 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, - 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, - 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, - 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, - 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, - 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, - 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, - 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, - 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, - 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, - 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, - 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, - 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, - 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, - 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, - 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, - 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, - 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, - 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, - 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, - 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, - 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, - 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, - 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, - 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, - 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, - 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, - 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, - 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, - 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, - 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, - 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 -obs function: - name: ObsErrorBoundIR@ObsFunction - options: - channels: *all_channels - test_qcflag: PreQC - test_obserr: GsiObsError_grosschk - obserr_bound_latitude: - name: ObsErrorFactorLatRad@ObsFunction - options: - latitude_parameters: [25.0, 0.5, 0.04, 1.0] - obserr_bound_transmittop: - name: ObsErrorFactorTransmitTopRad@ObsFunction +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 + obs function: + name: ObsErrorBoundIR@ObsFunction + options: channels: *all_channels - options: + test_qcflag: PreQC + test_obserr: GsiObsError_grosschk + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction channels: *all_channels - obserr_bound_max: [ 3.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - 2.0, 2.0, 4.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 4.0, 4.0, - 3.5, 2.5, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - 3.5, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 3.0, 2.0, 2.0, - 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, - 2.0, 2.0, 2.0, 2.0, 2.5, 2.0, 2.5, 2.5, 3.0, 2.5, - 2.5, 2.5, 2.5, 3.5, 2.5, 2.5, 3.0, 3.5, 3.0, 4.0, - 4.0, 4.0, 4.0, 4.0, 4.0, 4.5, 4.5, 4.5, 4.5, 4.5, - 4.0, 4.5, 4.0, 4.0, 4.5, 2.5, 3.0, 2.5, 3.0, 2.5, - 3.0, 2.0, 2.5, 2.5, 3.0, 3.0, 2.5, 3.0, 3.0, 3.0, - 2.5, 2.5, 4.0, 4.5, 4.5, 5.0, 4.0, 4.0, 5.0, 5.0, - 5.0, 5.0, 5.5, 5.5, 4.0, 5.0, 4.0, 4.5, 5.5, 5.5, - 6.0, 4.5, 4.5, 4.0, 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 5.5, 4.5, 6.0, - 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 5.0, 6.0, - 6.0, 6.0, 4.0, 6.0, 6.0, 6.0, 6.0, 4.5, 6.0, 6.0, - 4.5, 6.0, 6.0, 6.0, 6.0, 6.0, 5.0, 6.0, 6.0, 6.0, - 5.0, 6.0, 6.0, 5.0, 6.0, 5.0, 6.0, 6.0, 6.0, 5.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, - 6.0, 6.0, 6.0, 6.0, 6.0, 6.0] - variables: [error_inflation_factor_grosschk] - channels: *all_channels - tolerance: 1.0e-8 + options: + channels: *all_channels + obserr_bound_max: [ 3.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 4.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 4.0, 4.0, + 3.5, 2.5, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 3.5, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 3.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.5, 2.0, 2.5, 2.5, 3.0, 2.5, + 2.5, 2.5, 2.5, 3.5, 2.5, 2.5, 3.0, 3.5, 3.0, 4.0, + 4.0, 4.0, 4.0, 4.0, 4.0, 4.5, 4.5, 4.5, 4.5, 4.5, + 4.0, 4.5, 4.0, 4.0, 4.5, 2.5, 3.0, 2.5, 3.0, 2.5, + 3.0, 2.0, 2.5, 2.5, 3.0, 3.0, 2.5, 3.0, 3.0, 3.0, + 2.5, 2.5, 4.0, 4.5, 4.5, 5.0, 4.0, 4.0, 5.0, 5.0, + 5.0, 5.0, 5.5, 5.5, 4.0, 5.0, 4.0, 4.5, 5.5, 5.5, + 6.0, 4.5, 4.5, 4.0, 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 5.5, 4.5, 6.0, + 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 5.0, 6.0, + 6.0, 6.0, 4.0, 6.0, 6.0, 6.0, 6.0, 4.5, 6.0, 6.0, + 4.5, 6.0, 6.0, 6.0, 6.0, 6.0, 5.0, 6.0, 6.0, 6.0, + 5.0, 6.0, 6.0, 5.0, 6.0, 5.0, 6.0, 6.0, 6.0, 5.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0] + variables: [error_inflation_factor_grosschk] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/function_errfjsfc.yaml b/test/testinput/function_errfjsfc.yaml index a9a389108..59b5efcc7 100755 --- a/test/testinput/function_errfjsfc.yaml +++ b/test/testinput/function_errfjsfc.yaml @@ -1,74 +1,75 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: iasi_metop-a - obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, - 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, - 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, - 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, - 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, - 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, - 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, - 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, - 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, - 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, - 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, - 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, - 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, - 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, - 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, - 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, - 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, - 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, - 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, - 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, - 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, - 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, - 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, - 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, - 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, - 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, - 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, - 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, - 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, - 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, - 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, - 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, - 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, - 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, - 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, - 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, - 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, - 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, - 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, - 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, - 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, - 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, - 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, - 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, - 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, - 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, - 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, - 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, - 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 -obs function: - name: ObsErrorFactorSurfJacobianRad@ObsFunction - options: +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 + obs function: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + options: + channels: *all_channels + obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] + test_qcflag: PreQC + test_obserr: GsiObsError_jsfc + variables: [error_inflation_factor_jsfc] channels: *all_channels - obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] - obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] - test_qcflag: PreQC - test_obserr: GsiObsError_jsfc - variables: [error_inflation_factor_jsfc] - channels: *all_channels - tolerance: 1.0e-8 + tolerance: 1.0e-8 diff --git a/test/testinput/function_errflat.yaml b/test/testinput/function_errflat.yaml index bb2feca7c..5f763fc8f 100644 --- a/test/testinput/function_errflat.yaml +++ b/test/testinput/function_errflat.yaml @@ -1,69 +1,70 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: iasi_metop-a - obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, - 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, - 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, - 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, - 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, - 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, - 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, - 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, - 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, - 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, - 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, - 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, - 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, - 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, - 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, - 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, - 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, - 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, - 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, - 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, - 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, - 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, - 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, - 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, - 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, - 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, - 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, - 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, - 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, - 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, - 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, - 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, - 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, - 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, - 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, - 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, - 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, - 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, - 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, - 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, - 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, - 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, - 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, - 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, - 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, - 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, - 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, - 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, - 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 -obs function: - name: ObsErrorFactorLatRad@ObsFunction - options: - latitude_parameters: [25.0, 0.5, 0.04, 1.0] - variables: [error_inflation_factor_lat] - tolerance: 1.e-8 +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 + obs function: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + variables: [error_inflation_factor_lat] + tolerance: 1.e-8 diff --git a/test/testinput/function_errfsdoei.yaml b/test/testinput/function_errfsdoei.yaml index 9536a8dc1..b7575457a 100755 --- a/test/testinput/function_errfsdoei.yaml +++ b/test/testinput/function_errfsdoei.yaml @@ -1,77 +1,69 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: amsua_n19 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 1-15 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/amsua_n19_obsdiag_2018041500_m_qc.nc4 -obs function: - name: ObsErrorFactorSituDependMW@ObsFunction - options: - sensor: amsua_n19 - channels: *all_channels - clwobs_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue] - test_bias: GsiObsBias - clwbkg_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - scatobs_function: - name: SCATRetMW@ObsFunction - options: - scatret_ch238: 1 - scatret_ch314: 2 - scatret_ch890: 15 - scatret_types: [ObsValue] - test_bias: GsiObsBias - bias_application: GsiHofX - clwmatchidx_function: - name: CLWMatchIndexMW@ObsFunction +observations: +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/amsua_n19_obsdiag_2018041500_m_qc.nc4 + obs function: + name: ObsErrorFactorSituDependMW@ObsFunction + options: + sensor: amsua_n19 channels: *all_channels - options: + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [GsiHofXBc] + scatobs_function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 15 + scatret_types: [ObsValue] + clwmatchidx_function: + name: CLWMatchIndexMW@ObsFunction channels: *all_channels - clwobs_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue] - test_bias: GsiObsBias - clwbkg_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - clwret_clearsky: [0.050, 0.030, 0.030, 0.020, 0.000, - 0.100, 0.000, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.030] - obserr_clearsky: [2.500, 2.200, 2.000, 0.550, 0.300, - 0.230, 0.230, 0.250, 0.250, 0.350, - 0.400, 0.550, 0.800, 3.000, 3.500] - test_obserr: GsiObsError_sdoei - test_hofx: GsiHofX - test_bias: GsiObsBias - test_qcflag: PreQC - variables: [error_inflation_factor_sdoei] - channels: *all_channels - tolerance: 1.0e-7 + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [GsiHofXBc] + clwret_clearsky: [0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + obserr_clearsky: [2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + test_obserr: GsiObsError_sdoei + test_hofx: GsiHofXBc + test_qcflag: PreQC + variables: [error_inflation_factor_sdoei] + channels: *all_channels + tolerance: 5.0e-7 diff --git a/test/testinput/function_errfsdoei_atms.yaml b/test/testinput/function_errfsdoei_atms.yaml index 25d938112..5827a9826 100755 --- a/test/testinput/function_errfsdoei_atms.yaml +++ b/test/testinput/function_errfsdoei_atms.yaml @@ -1,80 +1,73 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: atms_npp - obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 1-22 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/atms_npp_obsdiag_2018041500_m_qc.nc4 -obs function: - name: ObsErrorFactorSituDependMW@ObsFunction - options: - sensor: atms_npp - channels: *all_channels - clwobs_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue] - test_bias: GsiObsBias - clwbkg_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - scatobs_function: - name: SCATRetMW@ObsFunction - options: - scatret_ch238: 1 - scatret_ch314: 2 - scatret_ch890: 16 - scatret_types: [ObsValue] - test_bias: GsiObsBias - clwmatchidx_function: - name: CLWMatchIndexMW@ObsFunction +observations: +- obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-22 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/atms_npp_obsdiag_2018041500_m_qc.nc4 + obs function: + name: ObsErrorFactorSituDependMW@ObsFunction + options: + sensor: atms_npp channels: *all_channels - options: + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [GsiHofXBc] + scatobs_function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 16 + scatret_types: [ObsValue] + clwmatchidx_function: + name: CLWMatchIndexMW@ObsFunction channels: *all_channels - clwobs_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue] - test_bias: GsiObsBias - clwbkg_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - clwret_clearsky: [ 0.030, 0.030, 0.030, 0.020, 0.030, - 0.080, 0.150, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.000, - 0.020, 0.030, 0.030, 0.030, 0.030, - 0.050, 0.100] - obserr_clearsky: [ 4.500, 4.500, 4.500, 2.500, 0.550, - 0.300, 0.300, 0.400, 0.400, 0.400, - 0.450, 0.450, 0.550, 0.800, 3.000, - 4.000, 4.000, 3.500, 3.000, 3.000, - 3.000, 3.000] - test_obserr: GsiObsError_sdoei - test_hofx: GsiHofX - test_bias: GsiObsBias - test_qcflag: PreQC - variables: [error_inflation_factor_sdoei] - channels: *all_channels - tolerance: 5.0e-7 + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [GsiHofXBc] + clwret_clearsky: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + obserr_clearsky: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + test_obserr: GsiObsError_sdoei + test_hofx: GsiHofXBc + test_qcflag: PreQC + variables: [error_inflation_factor_sdoei] + channels: *all_channels + tolerance: 5.0e-7 diff --git a/test/testinput/function_errftopo.yaml b/test/testinput/function_errftopo.yaml index 259652253..b5934a62d 100755 --- a/test/testinput/function_errftopo.yaml +++ b/test/testinput/function_errftopo.yaml @@ -1,71 +1,72 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: iasi_metop-a - obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, - 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, - 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, - 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, - 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, - 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, - 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, - 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, - 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, - 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, - 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, - 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, - 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, - 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, - 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, - 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, - 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, - 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, - 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, - 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, - 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, - 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, - 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, - 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, - 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, - 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, - 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, - 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, - 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, - 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, - 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, - 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, - 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, - 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, - 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, - 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, - 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, - 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, - 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, - 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, - 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, - 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, - 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, - 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, - 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, - 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, - 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, - 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, - 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 -obs function: - name: ObsErrorFactorTopoRad@ObsFunction - options: +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 + obs function: + name: ObsErrorFactorTopoRad@ObsFunction + options: + channels: *all_channels + sensor: iasi_metop-a + variables: [error_inflation_factor_topo] channels: *all_channels - sensor: iasi_metop-a - variables: [error_inflation_factor_topo] - channels: *all_channels - tolerance: 1.0e-8 + tolerance: 1.0e-8 diff --git a/test/testinput/function_errftransmittop.yaml b/test/testinput/function_errftransmittop.yaml index 946830c7d..fbd425afd 100755 --- a/test/testinput/function_errftransmittop.yaml +++ b/test/testinput/function_errftransmittop.yaml @@ -1,70 +1,71 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: iasi_metop-a - obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, - 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, - 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, - 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, - 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, - 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, - 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, - 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, - 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, - 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, - 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, - 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, - 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, - 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, - 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, - 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, - 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, - 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, - 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, - 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, - 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, - 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, - 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, - 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, - 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, - 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, - 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, - 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, - 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, - 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, - 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, - 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, - 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, - 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, - 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, - 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, - 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, - 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, - 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, - 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, - 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, - 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, - 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, - 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, - 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, - 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, - 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, - 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, - 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 -obs function: - name: ObsErrorFactorTransmitTopRad@ObsFunction - options: +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 + obs function: + name: ObsErrorFactorTransmitTopRad@ObsFunction + options: + channels: *all_channels + variables: [error_inflation_factor_transmittop] channels: *all_channels - variables: [error_inflation_factor_transmittop] - channels: *all_channels - tolerance: 1.0e-8 + tolerance: 1.0e-8 diff --git a/test/testinput/function_errfwavenum.yaml b/test/testinput/function_errfwavenum.yaml index 0aaa9e2be..57b7df040 100755 --- a/test/testinput/function_errfwavenum.yaml +++ b/test/testinput/function_errfwavenum.yaml @@ -1,70 +1,71 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: iasi_metop-a - obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, - 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, - 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, - 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, - 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, - 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, - 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, - 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, - 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, - 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, - 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, - 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, - 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, - 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, - 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, - 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, - 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, - 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, - 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, - 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, - 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, - 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, - 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, - 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, - 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, - 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, - 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, - 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, - 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, - 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, - 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, - 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, - 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, - 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, - 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, - 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, - 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, - 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, - 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, - 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, - 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, - 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, - 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, - 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, - 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, - 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, - 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, - 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, - 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 -obs function: - name: ObsErrorFactorWavenumIR@ObsFunction - options: +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 + obs function: + name: ObsErrorFactorWavenumIR@ObsFunction + options: + channels: *all_channels + variables: [error_inflation_factor_wavenum] channels: *all_channels - variables: [error_inflation_factor_wavenum] - channels: *all_channels - tolerance: 1.0e-8 + tolerance: 1.0e-8 diff --git a/test/testinput/function_hydrometeorchk.yaml b/test/testinput/function_hydrometeorchk.yaml index 81fecefcf..082db64d4 100755 --- a/test/testinput/function_hydrometeorchk.yaml +++ b/test/testinput/function_hydrometeorchk.yaml @@ -1,60 +1,58 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: amsua_n19 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 1-15 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/amsua_n19_obsdiag_2018041500_m_qc.nc4 -obs function: - name: HydrometeorCheckAMSUA@ObsFunction - channels: *all_channels - options: +observations: +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/amsua_n19_obsdiag_2018041500_m_qc.nc4 + obs function: + name: HydrometeorCheckAMSUA@ObsFunction channels: *all_channels - test_biasterm: GsiObsBiasTerm - test_bias: GsiObsBias - test_hofx: GsiHofX - obserr_clearsky: [ 2.500, 2.200, 2.000, 0.550, 0.300, - 0.230, 0.230, 0.250, 0.250, 0.350, - 0.400, 0.550, 0.800, 3.000, 3.500] - clwret_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue] - test_bias: GsiObsBias - obserr_function: - name: ObsErrorModelRamp@ObsFunction + options: channels: *all_channels - options: + test_biasterm: GsiObsBiasTerm + test_bias: GsiObsBias # used in correction of clear-sky simulated values + test_hofx: GsiHofXBc # H(x) has to be bias corrected + obserr_clearsky: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + clwret_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + obserr_function: + name: ObsErrorModelRamp@ObsFunction channels: *all_channels - xvar: - name: CLWRetSymmetricMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue, GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, - 0.100, 0.000, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.030] - x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, - 1.500, 0.000, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.200] - err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, - 0.230, 0.230, 0.250, 0.250, 0.350, - 0.400, 0.550, 0.800, 3.000, 3.500] - err1: [20.000, 18.000, 12.000, 3.000, 0.500, - 0.300, 0.230, 0.250, 0.250, 0.350, - 0.400, 0.550, 0.800, 3.000, 18.000] - variables: [Hydrometeor_Affected_Channels] - tolerance: 1.0e-6 + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, GsiHofXBc] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + variables: [Hydrometeor_Affected_Channels] + tolerance: 1.0e-6 diff --git a/test/testinput/function_hydrometeorchk_atms.yaml b/test/testinput/function_hydrometeorchk_atms.yaml index aa1775ec4..0e9258ba3 100755 --- a/test/testinput/function_hydrometeorchk_atms.yaml +++ b/test/testinput/function_hydrometeorchk_atms.yaml @@ -1,70 +1,68 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: atms_npp - obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 1-22 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/atms_npp_obsdiag_2018041500_m_qc.nc4 -obs function: - name: HydrometeorCheckATMS@ObsFunction - channels: *all_channels - options: +observations: +- obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-22 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/atms_npp_obsdiag_2018041500_m_qc.nc4 + obs function: + name: HydrometeorCheckATMS@ObsFunction channels: *all_channels - test_biasterm: GsiObsBiasTerm - test_bias: GsiObsBias - test_hofx: GsiHofX - obserr_clearsky: [ 4.500, 4.500, 4.500, 2.500, 0.550, - 0.300, 0.300, 0.400, 0.400, 0.400, - 0.450, 0.450, 0.550, 0.800, 3.000, - 4.000, 4.000, 3.500, 3.000, 3.000, - 3.000, 3.000] - clwret_function: - name: CLWRetMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue] - test_bias: GsiObsBias - obserr_function: - name: ObsErrorModelRamp@ObsFunction + options: channels: *all_channels - options: + test_biasterm: GsiObsBiasTerm + test_bias: GsiObsBias + test_hofx: GsiHofXBc + obserr_clearsky: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + clwret_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + obserr_function: + name: ObsErrorModelRamp@ObsFunction channels: *all_channels - xvar: - name: CLWRetSymmetricMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue, GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - x0: [ 0.030, 0.030, 0.030, 0.020, 0.030, - 0.080, 0.150, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.000, - 0.020, 0.030, 0.030, 0.030, 0.030, - 0.050, 0.100] - x1: [ 0.350, 0.380, 0.400, 0.450, 0.500, - 1.000, 1.000, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.000, - 0.350, 0.500, 0.500, 0.500, 0.500, - 0.500, 0.500] - err0: [ 4.500, 4.500, 4.500, 2.500, 0.550, - 0.300, 0.300, 0.400, 0.400, 0.400, - 0.450, 0.450, 0.550, 0.800, 3.000, - 4.000, 4.000, 3.500, 3.000, 3.000, - 3.000, 3.000] - err1: [20.000, 25.000, 12.000, 7.000, 3.500, - 3.000, 0.800, 0.400, 0.400, 0.400, - 0.450, 0.450, 0.550, 0.800, 3.000, - 19.000, 30.000, 25.000, 16.500, 12.000, - 9.000, 6.500] - variables: [Hydrometeor_Affected_Channels] - tolerance: 1.0e-6 + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, GsiHofXBc] + x0: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + x1: [ 0.350, 0.380, 0.400, 0.450, 0.500, + 1.000, 1.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.350, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500] + err0: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + err1: [20.000, 25.000, 12.000, 7.000, 3.500, + 3.000, 0.800, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 19.000, 30.000, 25.000, 16.500, 12.000, + 9.000, 6.500] + variables: [Hydrometeor_Affected_Channels] + tolerance: 1.0e-6 diff --git a/test/testinput/function_nsstret.yaml b/test/testinput/function_nsstret.yaml index 2d3b25379..ddb33fe31 100755 --- a/test/testinput/function_nsstret.yaml +++ b/test/testinput/function_nsstret.yaml @@ -1,106 +1,107 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: iasi_metop-a - obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, - 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, - 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, - 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, - 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, - 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, - 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, - 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, - 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, - 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, - 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, - 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, - 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, - 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, - 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, - 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, - 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, - 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, - 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, - 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, - 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, - 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, - 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, - 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, - 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, - 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, - 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, - 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, - 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, - 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, - 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, - 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, - 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, - 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, - 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, - 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, - 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, - 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, - 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, - 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, - 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, - 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, - 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, - 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, - 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, - 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, - 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, - 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, - 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 -obs function: - name: NearSSTRetCheckIR@ObsFunction - options: - test_qcflag: PreQC - test_obserr: GsiObsError_nsstret - test_hofx: GsiHofX - test_bias: GsiObsBias +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/iasi_metop-a_obsdiag_2018041500_m_unittest.nc4 + obs function: + name: NearSSTRetCheckIR@ObsFunction + options: + test_qcflag: PreQC + test_obserr: GsiObsError_nsstret + test_hofx: GsiHofX + test_bias: GsiObsBias + channels: *all_channels + use_flag: [1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, + -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, + 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, + 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, + 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, + -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, + 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, + -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] + variables: [nsstret_check] channels: *all_channels - use_flag: [1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, - -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, - -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, - 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, - -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, - 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, - -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, - 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, - -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, - 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, - -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, - -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, - 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, - -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] - variables: [nsstret_check] - channels: *all_channels - tolerance: 1.0e-8 + tolerance: 1.0e-8 diff --git a/test/testinput/function_obserrmean.yaml b/test/testinput/function_obserrmean.yaml index e62995632..e439a3740 100755 --- a/test/testinput/function_obserrmean.yaml +++ b/test/testinput/function_obserrmean.yaml @@ -1,42 +1,41 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: amsua_n19 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 1-15 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/amsua_n19_obsdiag_2018041500_m_qc.nc4 -obs function: - name: ObsErrorModelRamp@ObsFunction - options: - channels: *all_channels - xvar: - name: CLWRetSymmetricMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue, GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, - 0.100, 0.000, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.030] - x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, - 1.500, 0.000, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.200] - err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, - 0.230, 0.230, 0.250, 0.250, 0.350, - 0.400, 0.550, 0.800, 3.000, 3.500] - err1: [20.000, 18.000, 12.000, 3.000, 0.500, - 0.300, 0.230, 0.250, 0.250, 0.350, - 0.400, 0.550, 0.800, 3.000, 18.000] - variables: [ObsError] - channels: *all_channels - tolerance: 1.0e-6 +observations: +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/amsua_n19_obsdiag_2018041500_m_qc.nc4 + obs function: + name: ObsErrorModelRamp@ObsFunction + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, GsiHofXBc] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + variables: [ObsError] + channels: *all_channels + tolerance: 1.0e-6 diff --git a/test/testinput/function_obserrmean_abi.yaml b/test/testinput/function_obserrmean_abi.yaml index 8581ef038..a12baff62 100644 --- a/test/testinput/function_obserrmean_abi.yaml +++ b/test/testinput/function_obserrmean_abi.yaml @@ -1,31 +1,32 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: abi_g16 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/abi_g16_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 7-11,13-16 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/abi_g16_geoval_2018041500_m_qc.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/abi_g16_obsdiag_2018041500_m_qc.nc4 -obs function: - name: ObsErrorModelRamp@ObsFunction - options: - channels: *all_channels - xvar: - name: SymmCldImpactIR@ObsFunction - channels: *all_channels - options: +observations: +- obs space: + name: abi_g16 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/abi_g16_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 7-11,13-16 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/abi_g16_geoval_2018041500_m_qc.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/abi_g16_obsdiag_2018041500_m_qc.nc4 + obs function: + name: ObsErrorModelRamp@ObsFunction + options: + channels: *all_channels + xvar: + name: SymmCldImpactIR@ObsFunction channels: *all_channels - x0: [1, 1, 1, 1, 1, 1, 1, 1, 1] - x1: [29.37, 12.54, 15.15, 17.18, 25.68, 28.04, 28.0, 27.13, 23.71] - err0: [3.48, 1.98, 2.05, 2.07, 2.24, 2.41, 2.44, 2.39, 2.18] - err1: [18.7, 16.04, 18.2, 19.14, 26.2, 28.7, 29.02, 28.69, 24.68] - variables: [ObsError] - channels: *all_channels - tolerance: 1.0e-6 + options: + channels: *all_channels + x0: [1, 1, 1, 1, 1, 1, 1, 1, 1] + x1: [29.37, 12.54, 15.15, 17.18, 25.68, 28.04, 28.0, 27.13, 23.71] + err0: [3.48, 1.98, 2.05, 2.07, 2.24, 2.41, 2.44, 2.39, 2.18] + err1: [18.7, 16.04, 18.2, 19.14, 26.2, 28.7, 29.02, 28.69, 24.68] + variables: [ObsError] + channels: *all_channels + tolerance: 1.0e-6 diff --git a/test/testinput/function_obserrmean_atms.yaml b/test/testinput/function_obserrmean_atms.yaml index ddb8244f0..4c160bbb3 100755 --- a/test/testinput/function_obserrmean_atms.yaml +++ b/test/testinput/function_obserrmean_atms.yaml @@ -1,50 +1,49 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: atms_npp - obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 1-22 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/atms_npp_obsdiag_2018041500_m_qc.nc4 -obs function: - name: ObsErrorModelRamp@ObsFunction - options: - channels: *all_channels - xvar: - name: CLWRetSymmetricMW@ObsFunction - options: - clwret_ch238: 1 - clwret_ch314: 2 - clwret_types: [ObsValue, GsiHofX] - test_bias: GsiObsBias - bias_application: GsiHofX - x0: [ 0.030, 0.030, 0.030, 0.020, 0.030, - 0.080, 0.150, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.000, - 0.020, 0.030, 0.030, 0.030, 0.030, - 0.050, 0.100] - x1: [ 0.350, 0.380, 0.400, 0.450, 0.500, - 1.000, 1.000, 0.000, 0.000, 0.000, - 0.000, 0.000, 0.000, 0.000, 0.000, - 0.350, 0.500, 0.500, 0.500, 0.500, - 0.500, 0.500] - err0: [ 4.500, 4.500, 4.500, 2.500, 0.550, - 0.300, 0.300, 0.400, 0.400, 0.400, - 0.450, 0.450, 0.550, 0.800, 3.000, - 4.000, 4.000, 3.500, 3.000, 3.000, - 3.000, 3.000] - err1: [20.000, 25.000, 12.000, 7.000, 3.500, - 3.000, 0.800, 0.400, 0.400, 0.400, - 0.450, 0.450, 0.550, 0.800, 3.000, - 19.000, 30.000, 25.000, 16.500, 12.000, - 9.000, 6.500] - variables: [ObsError] - channels: *all_channels - tolerance: 5.0e-7 +observations: +- obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-22 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/atms_npp_obsdiag_2018041500_m_qc.nc4 + obs function: + name: ObsErrorModelRamp@ObsFunction + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, GsiHofXBc] + x0: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + x1: [ 0.350, 0.380, 0.400, 0.450, 0.500, + 1.000, 1.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.350, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500] + err0: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + err1: [20.000, 25.000, 12.000, 7.000, 3.500, + 3.000, 0.800, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 19.000, 30.000, 25.000, 16.500, 12.000, + 9.000, 6.500] + variables: [ObsError] + channels: *all_channels + tolerance: 5.0e-7 diff --git a/test/testinput/function_obserrmean_mhs.yaml b/test/testinput/function_obserrmean_mhs.yaml index bb4b55383..e12554c8f 100644 --- a/test/testinput/function_obserrmean_mhs.yaml +++ b/test/testinput/function_obserrmean_mhs.yaml @@ -1,33 +1,34 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: mhs_n19 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/mhs_n19_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: &all_channels 1-5 -# can have GeoVaLs section like below -geovals: - filename: Data/ufo/testinput_tier_1/mhs_n19_geoval_2018041500_m_qc.nc4 -# can have ObsDiag section like below -obs diagnostics: - filename: Data/ufo/testinput_tier_1/mhs_n19_obsdiag_2018041500_m_qc.nc4 -obs function: - name: ObsErrorModelQuad@ObsFunction - options: - channels: *all_channels - xvar: - name: SIRetSymmetricMW@ObsFunction - options: - siret_ch90: 1 - siret_ch150: 2 - siret_types: [ObsValue, HofX] - bias_application: ObsValue - a: [ -1.50, -1.50, -1.50, -1.50, -1.50 ] - b: [ 0.03, 0.05, 0.01, 0.03, 0.04 ] - err0: [ 5.38, 4.52, 5.10, 3.42, 3.45] - err1: [ 15.15, 22.62, 7.64, 10.08, 15.85] - variables: [ObsError] - channels: *all_channels - tolerance: 1.0e-6 +observations: +- obs space: + name: mhs_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/mhs_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-5 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/mhs_n19_geoval_2018041500_m_qc.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/mhs_n19_obsdiag_2018041500_m_qc.nc4 + obs function: + name: ObsErrorModelQuad@ObsFunction + options: + channels: *all_channels + xvar: + name: SIRetSymmetricMW@ObsFunction + options: + siret_ch90: 1 + siret_ch150: 2 + siret_types: [ObsValue, HofX] + bias_application: ObsValue + a: [ -1.50, -1.50, -1.50, -1.50, -1.50 ] + b: [ 0.03, 0.05, 0.01, 0.03, 0.04 ] + err0: [ 5.38, 4.52, 5.10, 3.42, 3.45] + err1: [ 15.15, 22.62, 7.64, 10.08, 15.85] + variables: [ObsError] + channels: *all_channels + tolerance: 1.0e-6 diff --git a/test/testinput/function_obserrorfactorconv.yaml b/test/testinput/function_obserrorfactorconv.yaml new file mode 100644 index 000000000..863edb0fe --- /dev/null +++ b/test/testinput/function_obserrorfactorconv.yaml @@ -0,0 +1,26 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs operator: + name: VertInterp + obs space: + name: test_obserrorfactorconv + obsdatain: + obsfile: Data/ufo/testinput_tier_1/converr_vadwind_obs_2020120112_s.nc4 + obsgrouping: + group variables: ["station_id", "datetime"] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [eastward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/converr_vadwind_geoval_2020120112_s.nc4 + obs function: + name: ObsErrorFactorConventional@ObsFunction + variables: [eastward_wind] + tolerance: 1.e-6 + options: + inflate variables: [eastward_wind] # Required + test QCflag: PreQC # Optional. Default is from filter qcflags + test QCthreshold: 2 # Optonal only when using PreQC flags + # Default value = 3 for PreQC diff --git a/test/testinput/function_obserrorinflatesfcp.yaml b/test/testinput/function_obserrorinflatesfcp.yaml new file mode 100644 index 000000000..b4a76899b --- /dev/null +++ b/test/testinput/function_obserrorinflatesfcp.yaml @@ -0,0 +1,23 @@ +window begin: 2020-10-01T03:00:00Z +window end: 2020-10-01T09:00:00Z + +observations: +- obs space: + name: test_sfcp_inflation + obsdatain: + obsfile: Data/ufo/testinput_tier_1/2020100106_metars_small.nc + simulated variables: [surface_pressure] + geovals: + filename: Data/ufo/testinput_tier_1/sfcobs_geovals_gfsC48_2020100106_small.nc + obs operator: + name: SfcPCorrected + da_psfc_scheme: UKMO + obs function: + name: ObsErrorFactorSfcPressure@ObsFunction + options: + original_obserr: ObsError + error_min: 100 # 1 mb + error_max: 300 # 3 mb + error_gross: 360 # 3.6 mb + variables: [surface_pressure_obserr] + tolerance: 1.e-4 diff --git a/test/testinput/function_obserrorstepwiselinear.yaml b/test/testinput/function_obserrorstepwiselinear.yaml new file mode 100644 index 000000000..b7e6f5fb7 --- /dev/null +++ b/test/testinput/function_obserrorstepwiselinear.yaml @@ -0,0 +1,33 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: TestObsError + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small.nc + simulated variables: [eastward_wind] + obs function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: surface_pressure@ObsValue + xvals: [100000, 95000, 80000, 65000, 60000, 55000, 50000, 45000, 40000, 35000, 30000, 25000, 20000, 15000, 10000] #Pressure (Pa) + errors: [1.4, 1.5, 1.6, 1.8, 1.9, 2.0, 2.1, 2.3, 2.6, 2.8, 3.0, 3.2, 2.7, 2.4, 2.1] + variables: [eastward_wind_obserr] + tolerance: 1.e-6 + +#- obs space: +# name: TestObsError +# obsdatain: +# obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small.nc +# simulated variables: [air_temperature] +# obs function: +# name: ObsErrorModelStepwiseLinear@ObsFunction +# options: +# xvar: +# name: surface_pressure@ObsValue +# xvals: [10000, 20000, 25000, 30000, 50000, 85000, 97500, 100000] #Pressure (Pa), ascending order +# errors: [3.0, 2.0, 1.5, 1.5, 2.0, 2.5, 2.5, 3.0] +# variables: [air_temperature_obserr] +# tolerance: 1.e-6 diff --git a/test/testinput/function_reperr.yaml b/test/testinput/function_reperr.yaml new file mode 100644 index 000000000..28ce28a9e --- /dev/null +++ b/test/testinput/function_reperr.yaml @@ -0,0 +1,22 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: ADT + obsdatain: + obsfile: Data/ufo/testinput_tier_1/synthetic-adt-2018041500.nc + obsdataout: + obsfile: Data/synthetic-adt-2018041500_function_output.nc + simulated variables: [obs_absolute_dynamic_topography] + geovals: + filename: Data/ufo/testinput_tier_1/synthetic-adt-2018041500-geovals.nc + obs function: + name: LinearCombination@ObsFunction + options: + variables: [representation_error@GeoVaLs, + obs_absolute_dynamic_topography@ObsError] + coefs: [0.1, + 1.0] + variables: [ObsError] + tolerance: 1.0e-8 diff --git a/test/testinput/function_satwind_indiv_errors.yaml b/test/testinput/function_satwind_indiv_errors.yaml new file mode 100644 index 000000000..bc9c219c9 --- /dev/null +++ b/test/testinput/function_satwind_indiv_errors.yaml @@ -0,0 +1,56 @@ +window begin: 2020-10-01T03:00:00Z +window end: 2020-10-01T09:00:00Z + +observations: +- obs operator: + name: VertInterp + vertical coordinate: air_pressure_levels + obs space: + name: Satwind + obsdatain: + obsfile: Data/ufo/testinput_tier_1/satwind_obs_1d_2020100106_noinv.nc4 + obsdataout: + obsfile: Data/satwind_obs_1d_2020100106_noinv_errors.nc4 + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/satwind_geoval_20201001T0600Z_noinv.nc4 + obs filters: + - filter: Bounds Check + defer to post: true + filter variables: + - name: eastward_wind + action: + name: assign error + error function: + name: SatwindIndivErrors@ObsFunction + options: + default pressure error: 10000 + verror add: 7.5 + verror mult: -5.0 + wind component: eastward_wind + vertical coordinate: air_pressure_levels + - filter: Bounds Check + defer to post: true + filter variables: + - name: northward_wind + action: + name: assign error + error function: + name: SatwindIndivErrors@ObsFunction + options: + default pressure error: 10000 + verror add: 7.5 + verror mult: -5.0 + wind component: northward_wind + vertical coordinate: air_pressure_levels + compareVariables: # test output matches precalculated values + - reference: + name: eastward_wind@TestReference + test: + name: eastward_wind@EffectiveError + absTol: 1.0e-5 + - reference: + name: northward_wind@TestReference + test: + name: northward_wind@EffectiveError + absTol: 1.0e-5 diff --git a/test/testinput/function_scatret.yaml b/test/testinput/function_scatret.yaml index 271fd28d8..a177fe290 100644 --- a/test/testinput/function_scatret.yaml +++ b/test/testinput/function_scatret.yaml @@ -1,25 +1,26 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: amsua_n19 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: 1, 2, 15 -geovals: - filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 -obs function: - name: SCATRetMW@ObsFunction - options: - scatret_ch238: 1 - scatret_ch314: 2 - scatret_ch890: 15 - scatret_types: [ObsValue] - test_bias: GsiObsBias - bias_application: GsiHofX - variables: [scat_retrieved_from_observation] - tolerance: 1.0e-7 +observations: +- obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: 1, 2, 15 + geovals: + filename: Data/ufo/testinput_tier_1/amsua_n19_geoval_2018041500_m_qc.nc4 + obs function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 15 + scatret_types: [ObsValue] + test_bias: GsiObsBias + bias_application: GsiHofX + variables: [scat_retrieved_from_observation] + tolerance: 1.0e-7 diff --git a/test/testinput/function_scatret_atms.yaml b/test/testinput/function_scatret_atms.yaml index 6a103cb8d..16608189f 100644 --- a/test/testinput/function_scatret_atms.yaml +++ b/test/testinput/function_scatret_atms.yaml @@ -1,24 +1,25 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: atms_npp - obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 - simulated variables: [brightness_temperature] - channels: 1, 2, 16 -geovals: - filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 -obs function: - name: SCATRetMW@ObsFunction - options: - scatret_ch238: 1 - scatret_ch314: 2 - scatret_ch890: 16 - scatret_types: [ObsValue] - test_bias: GsiObsBias - variables: [scat_retrieved_from_observation] - tolerance: 1.0e-7 +observations: +- obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + simulated variables: [brightness_temperature] + channels: 1, 2, 16 + geovals: + filename: Data/ufo/testinput_tier_1/atms_npp_geoval_2018041500_m_qc.nc4 + obs function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 16 + scatret_types: [ObsValue] + test_bias: GsiObsBias + variables: [scat_retrieved_from_observation] + tolerance: 1.0e-7 diff --git a/test/testinput/function_velocity.yaml b/test/testinput/function_velocity.yaml index d2f67dfe1..26cad1d02 100644 --- a/test/testinput/function_velocity.yaml +++ b/test/testinput/function_velocity.yaml @@ -1,23 +1,24 @@ window begin: 2018-04-14T20:00:00Z window end: 2018-04-15T03:00:00Z -obs space: - name: Satwind - obsdatain: - obsfile: Data/ioda/testinput_tier_1/satwind_obs_2018041500_m.nc4 - obsdataout: - obsfile: Data/satwind_obs_2018041500_function_output.nc4 - simulated variables: [eastward_wind, northward_wind] -# can have GeoVaLs section like below -#geovals: -# filename: Data/ufo/testinput_tier_1/satwind_geoval_2018041500_m.nc4 -# -# can have ObsDiag section like below -#obs diagnostics: -# filename: Data/ufo/testinput_tier_1/satwind_obsdiag_2018041500_m.nc4 -obs function: - name: Velocity@ObsFunction - options: - type: ObsValue - variables: [wind_speed] - tolerance: 1.e-7 +observations: +- obs space: + name: Satwind + obsdatain: + obsfile: Data/ufo/testinput_tier_1/satwind_obs_2018041500_m.nc4 + obsdataout: + obsfile: Data/satwind_obs_2018041500_function_output.nc4 + simulated variables: [eastward_wind, northward_wind] + # can have GeoVaLs section like below + #geovals: + # filename: Data/ufo/testinput_tier_1/satwind_geoval_2018041500_m.nc4 + # + # can have ObsDiag section like below + #obs diagnostics: + # filename: Data/ufo/testinput_tier_1/satwind_obsdiag_2018041500_m.nc4 + obs function: + name: Velocity@ObsFunction + options: + type: ObsValue + variables: [wind_speed] + tolerance: 1.e-7 diff --git a/test/testinput/genericprof.yaml b/test/testinput/genericprof.yaml index 23c0a3648..fa788a3a5 100644 --- a/test/testinput/genericprof.yaml +++ b/test/testinput/genericprof.yaml @@ -7,7 +7,7 @@ observations: obs space: name: InsituSalinity obsdatain: - obsfile: Data/ioda/testinput_tier_1/profile_2018-04-15.nc + obsfile: Data/ufo/testinput_tier_1/profile_2018-04-15.nc simulated variables: [sea_water_salinity] geovals: filename: Data/ufo/testinput_tier_1/profile_2018-04-15_geovals.nc diff --git a/test/testinput/geos_aod.yaml b/test/testinput/geos_aod.yaml index 771756c66..33c865524 100644 --- a/test/testinput/geos_aod.yaml +++ b/test/testinput/geos_aod.yaml @@ -3,7 +3,7 @@ window end: 2018-04-15T03:00:00Z observations: - obs operator: - name: GeosAod + name: AodGeos tracer_geovals: [ "mass_fraction_of_dust001_in_air", "mass_fraction_of_dust002_in_air", "mass_fraction_of_dust003_in_air", "mass_fraction_of_dust004_in_air", "mass_fraction_of_dust005_in_air", "mass_fraction_of_sea_salt001_in_air", @@ -16,11 +16,11 @@ observations: RCFile: ["geosaod.rc"] wavelengths: [550.0] obs space: - name: GeosAod + name: AodGeos obsdatain: - obsfile: Data/ioda/testinput_tier_1/geos_aod_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/geos_aod_obs_2018041500_m.nc4 simulated variables: [Total_Aerosol_Optical_Depth_550] - obs bias: {} + obs bias: {} geovals: filename: Data/ufo/testinput_tier_1/geos_aod_geoval_2018041500_m.nc4 vector ref: HofX diff --git a/test/testinput/geovals.yaml b/test/testinput/geovals.yaml index 0ae836ab5..25d93e4e9 100644 --- a/test/testinput/geovals.yaml +++ b/test/testinput/geovals.yaml @@ -5,7 +5,7 @@ observations: - obs space: name: Satwind obsdatain: - obsfile: Data/ioda/testinput_tier_1/satwind_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/satwind_obs_2018041500_m.nc4 simulated variables: [eastward_wind, northward_wind] geovals: filename: Data/ufo/testinput_tier_1/satwind_geoval_2018041500_m.nc4 @@ -14,7 +14,7 @@ observations: - obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 1-10,15 geovals: diff --git a/test/testinput/geovals_spec.yaml b/test/testinput/geovals_spec.yaml index 3f0ea5a9b..603e12452 100644 --- a/test/testinput/geovals_spec.yaml +++ b/test/testinput/geovals_spec.yaml @@ -5,7 +5,7 @@ geovals test: - obs space: name: Satwind obsdatain: - obsfile: Data/ioda/testinput_tier_1/satwind_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/satwind_obs_2018041500_m.nc4 simulated variables: [eastward_wind, northward_wind] geovals: filename: Data/ufo/testinput_tier_1/satwind_geoval_2018041500_m.nc4 @@ -14,7 +14,7 @@ geovals test: - obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 1-10,15 geovals: @@ -42,7 +42,7 @@ geovals test: - obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/sbuv2_n19_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sbuv2_n19_obs_2018041500_m.nc4 simulated variables: [integrated_layer_ozone_in_air] geovals: filename: Data/ufo/testinput_tier_1/sbuv2_n19_geoval_2018041500_m.nc4 @@ -53,3 +53,14 @@ geovals test: reorderzdir check: direction: top2bottom tolerance: 1.0e-12 + +# section for testGeoVaLsAllocatePutGet +geovals get test: + window begin: 2018-04-14T21:00:00Z # required for initializing Locations from the obs space below + window end: 2018-04-15T03:00:00Z # required for initializing Locations from the obs space below + obs space: # required for initializing Locations to locations from this file + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-10,15 diff --git a/test/testinput/gmi_clw_ret.yaml b/test/testinput/gmi_clw_ret.yaml new file mode 100644 index 000000000..d9397bb69 --- /dev/null +++ b/test/testinput/gmi_clw_ret.yaml @@ -0,0 +1,56 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv + linear obs operator: + Absorbers: [H2O,O3,CO2] + Clouds: [Water] + obs options: + Sensor_ID: gmi_gpm + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: gmi_gpm + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gmi_gpm_obs_2018041500_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-13 + geovals: + filename: Data/ufo/testinput_tier_1/gmi_gpm_geoval_2018041500_m.nc4 + obs filters: + # Ckeck CLW retrievals from observations + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-13 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch37v: 6 + clwret_ch37h: 7 + clwret_types: [ObsValue] + maxvalue: 999.0 + action: + name: reject +# passedBenchmark: 156 + # Ckeck CLW retrievals from Hofx + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-13 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch37v: 6 + clwret_ch37h: 7 + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject + passedBenchmark: 156 diff --git a/test/testinput/gmi_crtm.yaml b/test/testinput/gmi_crtm.yaml index fc8d635c4..a5a83bd9f 100644 --- a/test/testinput/gmi_crtm.yaml +++ b/test/testinput/gmi_crtm.yaml @@ -7,6 +7,7 @@ observations: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv linear obs operator: Absorbers: [H2O,O3,CO2] Clouds: [Water] @@ -17,7 +18,7 @@ observations: obs space: name: gmi_gpm obsdatain: - obsfile: Data/ioda/testinput_tier_1/gmi_gpm_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/gmi_gpm_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 1-13 geovals: diff --git a/test/testinput/gmi_qc_filters.yaml b/test/testinput/gmi_qc_filters.yaml new file mode 100644 index 000000000..2f7ac023d --- /dev/null +++ b/test/testinput/gmi_qc_filters.yaml @@ -0,0 +1,110 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv + linear obs operator: + Absorbers: [H2O,O3,CO2] + Clouds: [Water] + obs options: + Sensor_ID: gmi_gpm + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: gmi_gpm + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gmi_gpm_obs_2018041500_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-13 + geovals: + filename: Data/ufo/testinput_tier_1/gmi_gpm_geoval_2018041500_m.nc4 + obs filters: + # Ckeck CLW retrievals from observations + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-9 + minvalue: 50.0 + maxvalue: 320.0 + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 10,11,12,13 + minvalue: 70.0 + maxvalue: 320.0 + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-13 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch37v: 6 + clwret_ch37h: 7 + clwret_types: [ObsValue] + maxvalue: 999.0 + action: + name: reject +# passedBenchmark: 156 + # Ckeck CLW retrievals from Hofx + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-13 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch37v: 6 + clwret_ch37h: 7 + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject +# - filter: Background Check +# apply at iterations: 0, 1 +# filter variables: +# - name: brightness_temperature +# channels: 5,6,7,10,12,13 +# threshold: 2.0 +# passedBenchmark: 138 + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 5,6,7,10,12,13 + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: 5,6,7,10,12,13 + options: + channels: 5,6,7,10,12,13 + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch37v: 6 + clwret_ch37h: 7 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.050, 0.050, + 0.050, 0.050, 0.050] + x1: [ 0.200, 0.200, 0.200, + 0.200, 0.300, 0.300] + x2: [ 0.500, 0.500, 0.500, + 0.500, 0.500, 0.500] + err0: [ 4.000, 3.800,300.000, + 5.000, 2.500, 3.000] + err1: [11.000, 13.300, 23.000, + 20.000, 8.000, 13.000] + err2: [35.000, 25.300,500.000, + 50.000, 30.000, 40.000] + - filter: Background Check + apply at iterations: 0, 1 + filter variables: + - name: brightness_temperature + channels: 5,6,7,10,12,13 + threshold: 2.0 + passedBenchmark: 149 diff --git a/test/testinput/gmi_scan_angles.yaml b/test/testinput/gmi_scan_angles.yaml new file mode 100644 index 000000000..a209feecc --- /dev/null +++ b/test/testinput/gmi_scan_angles.yaml @@ -0,0 +1,35 @@ +window begin: 2020-11-14T21:00:00Z +window end: 2020-11-15T03:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + linear obs operator: + Absorbers: [H2O,O3,CO2] + Clouds: [Water] + obs options: + Sensor_ID: gmi_gpm + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: gmi_gpm + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gmi_gpm_obs_2020111500.nc4 + obsdataout: + obsfile: Data/gmi_gpm_obs_2020111500_out.nc4 + simulated variables: [brightness_temperature] + channels: 1-13 + geovals: + filename: Data/ufo/testinput_tier_1/gmi_gpm_geoval_2020111500.nc4 +# One scan angle +# rms ref: 218.10222468021257 +# Two scan angles (ch 1-9 & 10-13) + rms ref: 218.19693688899761 + tolerance: 1.e-6 + linear obs operator test: + coef TL: 1.e-3 + tolerance TL: 5.0e-3 + tolerance AD: 1.0e-11 diff --git a/test/testinput/gnssro_domain_check.yaml b/test/testinput/gnssro_domain_check.yaml index 0baf7fa00..725dffb2e 100644 --- a/test/testinput/gnssro_domain_check.yaml +++ b/test/testinput/gnssro_domain_check.yaml @@ -5,7 +5,7 @@ observations: - obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_m.nc4 obsdataout: obsfile: Data/gnssro_bndnbam_2018041500_m_output.nc4 simulated variables: [bending_angle] @@ -18,18 +18,18 @@ observations: # note values in the raw BUFR data files is in km - variable: name: earth_radius_of_curvature@MetaData - minvalue: 6250000. - maxvalue: 6450000. + minvalue: 6250000.0 + maxvalue: 6450000.0 - variable: name: geoid_height_above_reference_ellipsoid@MetaData - minvalue: -200. - maxvalue: 200. + minvalue: -200.0 + maxvalue: 200.0 - variable: name: latitude@MetaData - minvalue: -90. - maxvalue: 90. + minvalue: -90.0 + maxvalue: 90.0 - variable: name: longitude@MetaData - minvalue: -180. - maxvalue: 360. + minvalue: -180.0 + maxvalue: 360.0 passedBenchmark: 150 diff --git a/test/testinput/gnssro_obs_error.yaml b/test/testinput/gnssro_obs_error.yaml index 33a9e205a..387408876 100644 --- a/test/testinput/gnssro_obs_error.yaml +++ b/test/testinput/gnssro_obs_error.yaml @@ -10,7 +10,7 @@ observations: obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 obsdataout: obsfile: Data/gnssro_bndnbam_2018041500_3prof_errNBAM.nc4 simulated variables: [bending_angle] @@ -28,7 +28,7 @@ observations: obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 obsdataout: obsfile: Data/gnssro_bndnbam_2018041500_3prof_errECMWF.nc4 simulated variables: [bending_angle] @@ -48,7 +48,7 @@ observations: obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 obsdataout: obsfile: Data/gnssro_bndnbam_2018041500_3prof_errNRL.nc4 simulated variables: [bending_angle] @@ -66,7 +66,7 @@ observations: obs space: name: GnssroRef obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_s.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_s.nc4 obsdataout: obsfile: Data/gnssro_ref_2018041500_s_errNBAM.nc4 simulated variables: [refractivity] diff --git a/test/testinput/gnssro_obs_error_halo.yaml b/test/testinput/gnssro_obs_error_halo.yaml new file mode 100644 index 000000000..ce1082922 --- /dev/null +++ b/test/testinput/gnssro_obs_error_halo.yaml @@ -0,0 +1,84 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs operator: + name: GnssroBndNBAM + obs options: + use_compress: 1 + vertlayer: full + obs space: + name: GnssroBnd + distribution: Halo + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsdataout: + obsfile: Data/gnssro_bndnbam_2018041500_3prof_errNBAM.nc4 + simulated variables: [bending_angle] + geovals: + filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_3prof.nc4 + obs filters: + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: NBAM + passedBenchmark: 668 +- obs operator: + name: GnssroBndNBAM + obs options: + obs space: + name: GnssroBnd + distribution: Halo + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsdataout: + obsfile: Data/gnssro_bndnbam_2018041500_3prof_errECMWF.nc4 + simulated variables: [bending_angle] + geovals: + filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_3prof.nc4 + obs filters: + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: ECMWF + passedBenchmark: 668 +- obs operator: + name: GnssroBndNBAM + obs options: + use_compress: 1 + vertlayer: full + obs space: + name: GnssroBnd + distribution: Halo + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsdataout: + obsfile: Data/gnssro_bndnbam_2018041500_3prof_errNRL.nc4 + simulated variables: [bending_angle] + geovals: + filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_3prof.nc4 + obs filters: + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: NRL + passedBenchmark: 668 +- obs operator: + name: GnssroRef + obs options: + obs space: + name: GnssroRef + distribution: Halo + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_s.nc4 + obsdataout: + obsfile: Data/gnssro_ref_2018041500_s_errNBAM.nc4 + simulated variables: [refractivity] + geovals: + filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_s.nc4 + obs filters: + - filter: ROobserror + filter variables: + - name: refractivity + errmodel: NBAM + passedBenchmark: 20 diff --git a/test/testinput/gnssro_obs_error_ropp.yaml b/test/testinput/gnssro_obs_error_ropp.yaml new file mode 100644 index 000000000..242a9c476 --- /dev/null +++ b/test/testinput/gnssro_obs_error_ropp.yaml @@ -0,0 +1,43 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs operator: + name: GnssroBndROPP1D + obs options: + obs space: + name: ROPP1D + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + simulated variables: [bending_angle] + geovals: + filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_3prof.nc4 + obs filters: + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: NRL + variable: bending_angle + passedBenchmark: 744 +- obs operator: + name: GnssroBndROPP2D + obs options: + n_horiz: 3 + res: 40.0 + top_2d: 50.0 + obs space: + name: ROPP2D + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_s.nc4 + simulated variables: [bending_angle] + geovals: + filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_s_2d.nc4 + loc_multiplier: 3 + obs filters: + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: NRL + n_horiz: 3 + variable: bending_angle + passedBenchmark: 18 diff --git a/test/testinput/gnssrobendmetoffice.yaml b/test/testinput/gnssrobendmetoffice.yaml index 6614ded1f..f0947e0b3 100644 --- a/test/testinput/gnssrobendmetoffice.yaml +++ b/test/testinput/gnssrobendmetoffice.yaml @@ -7,10 +7,11 @@ observations: obs options: vert_interp_ops: true pseudo_ops: true + min_temp_grad: 1.0e-6 obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2019050700_1obs.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2019050700_1obs.nc4 simulated variables: [bending_angle] geovals: filename: Data/ufo/testinput_tier_1/gnssro_geoval_2019050700_1obs.nc4 @@ -24,5 +25,5 @@ observations: linear obs operator test: coef TL: 1.0e-4 iterations TL: 10 - tolerance TL: 1.0e-14 - tolerance AD: 1.0e-14 + tolerance TL: 1.5e-14 + tolerance AD: 1.5e-14 diff --git a/test/testinput/gnssrobendmetoffice_nopseudo.yaml b/test/testinput/gnssrobendmetoffice_nopseudo.yaml index c62fc49e7..35e51bbde 100644 --- a/test/testinput/gnssrobendmetoffice_nopseudo.yaml +++ b/test/testinput/gnssrobendmetoffice_nopseudo.yaml @@ -7,10 +7,11 @@ observations: obs options: vert_interp_ops: false pseudo_ops: false + min_temp_grad: 1.0e-6 obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2020050106_nopseudo.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2020050106_nopseudo.nc4 simulated variables: [bending_angle] geovals: filename: Data/ufo/testinput_tier_1/gnssro_geoval_2020050106_nopseudo.nc4 @@ -19,5 +20,5 @@ observations: linear obs operator test: coef TL: 1.0e-4 iterations TL: 10 - tolerance TL: 1.0e-14 - tolerance AD: 1.0e-14 + tolerance TL: 1.5e-14 + tolerance AD: 1.5e-14 diff --git a/test/testinput/gnssrobendmetoffice_obserror.yaml b/test/testinput/gnssrobendmetoffice_obserror.yaml new file mode 100644 index 000000000..1b5628876 --- /dev/null +++ b/test/testinput/gnssrobendmetoffice_obserror.yaml @@ -0,0 +1,32 @@ +window begin: 2020-05-01T03:00:00Z +window end: 2020-05-01T09:00:00Z + +observations: +- obs operator: + name: GnssroBendMetOffice + obs options: + vert_interp_ops: true + pseudo_ops: true + min_temp_grad: 1.0e-6 + obs space: + name: GnssroBnd + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2020050106_1dvar.nc4 + simulated variables: [bending_angle] + geovals: + filename: Data/ufo/testinput_tier_1/gnssro_geoval_2020050106_1dvar.nc4 + obs filters: + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: MetOffice + err_variable: latitude + rmatrix_filename: ../resources/rmatrix/gnssro/gnssro_ba_rmatrix_latitude.nl + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: MetOffice + err_variable: average_temperature + rmatrix_filename: ../resources/rmatrix/gnssro/gnssro_ba_rmatrix_avtemp.nl + passedBenchmark: 471 + diff --git a/test/testinput/gnssrobendmetoffice_qc.yaml b/test/testinput/gnssrobendmetoffice_qc.yaml new file mode 100644 index 000000000..c07d02245 --- /dev/null +++ b/test/testinput/gnssrobendmetoffice_qc.yaml @@ -0,0 +1,162 @@ +window begin: 2020-05-01T03:00:00Z +window end: 2020-05-01T09:00:00Z + +observations: +- obs operator: + name: GnssroBendMetOffice + obs options: + vert_interp_ops: true + pseudo_ops: true + min_temp_grad: 1.0e-6 + obs space: + name: GnssroBnd + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2020050106_1dvar.nc4 + obsgrouping: + group variables: ["record_number"] + simulated variables: [bending_angle] + geovals: + filename: Data/ufo/testinput_tier_1/gnssro_geoval_2020050106_1dvar.nc4 + obs filters: + - filter: Domain Check + where: + - variable: + name: occulting_sat_id@MetaData + is_in: 3, 4, 5, 42, 43, 44, 522, 523, 750, 751, 752, 753, 754, 755, 825 + - variable: + name: ImpactHeight@ObsFunction + minvalue: 0 + maxvalue: 60000 + - filter: BlackList + where: + - variable: + name: occulting_sat_id@MetaData + is_in: 523 # Apply for FY-3D + - variable: + name: ImpactHeight@ObsFunction + minvalue: 40000 # Remove above 40km + - variable: + name: quality_flags@MetaData + any_bit_set_of: 13 # Apply to rising occultations + - filter: BlackList + where: + - variable: + name: occulting_sat_id@MetaData + is_in: 42 # Apply for TerraSAR-X + - variable: + name: ImpactHeight@ObsFunction + minvalue: 40000 # Remove above 40km + - filter: GNSSRO Impact Height Check + filter variables: + - name: bending_angle + gradient threshold: -0.08 + sharp gradient offset: 600 + surface offset: 500 + - filter: Profile Few Observations Check + filter variables: + - name: bending_angle + threshold: 10 + defer to post: true + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: MetOffice + err_variable: latitude + rmatrix_filename: ../resources/rmatrix/gnssro/gnssro_ba_rmatrix_latitude.nl + - filter: GNSS-RO 1DVar Check + defer to post: true + vert_interp_ops: true + pseudo_ops: true + min_temp_grad: 1.0e-6 + capsupersat: false + cost_funct_test: 2 + Delta_ct2: 1 + Delta_factor: 0.01 + n_iteration_test: 20 + OB_test: 2.5 + y_test: 5 + Zmin: 0 + Zmax: 60000 + bmatrix_filename: ../resources/bmatrix/gnssro/gnssro_bmatrix.txt + filter variables: + - name: bending_angle + passedBenchmark: 229 +- obs operator: + name: GnssroBendMetOffice + obs options: + vert_interp_ops: false + pseudo_ops: false + min_temp_grad: 1.0e-6 + obs space: + name: GnssroBnd + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2020050106_1dvar.nc4 + obsgrouping: + group variables: ["record_number"] + simulated variables: [bending_angle] + geovals: + filename: Data/ufo/testinput_tier_1/gnssro_geoval_2020050106_1dvar.nc4 + obs filters: + - filter: Domain Check + where: + - variable: + name: occulting_sat_id@MetaData + is_in: 3, 4, 5, 42, 43, 44, 522, 523, 750, 751, 752, 753, 754, 755, 825 + - variable: + name: ImpactHeight@ObsFunction + minvalue: 0 + maxvalue: 60000 + - filter: BlackList + where: + - variable: + name: occulting_sat_id@MetaData + is_in: 523 # Apply for FY-3D + - variable: + name: ImpactHeight@ObsFunction + minvalue: 40000 # Remove above 40km + - variable: + name: quality_flags@MetaData + any_bit_set_of: 13 # Apply to rising occultations + - filter: BlackList + where: + - variable: + name: occulting_sat_id@MetaData + is_in: 42 # Apply for TerraSAR-X + - variable: + name: ImpactHeight@ObsFunction + minvalue: 40000 # Remove above 40km + - filter: GNSSRO Impact Height Check + filter variables: + - name: bending_angle + gradient threshold: -0.08 + sharp gradient offset: 600 + surface offset: 500 + - filter: Profile Few Observations Check + filter variables: + - name: bending_angle + threshold: 10 + defer to post: true + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: MetOffice + err_variable: latitude + rmatrix_filename: ../resources/rmatrix/gnssro/gnssro_ba_rmatrix_latitude.nl + - filter: GNSS-RO 1DVar Check + defer to post: true + vert_interp_ops: false + pseudo_ops: false + min_temp_grad: 1.0e-6 + capsupersat: false + cost_funct_test: 2 + Delta_ct2: 1 + Delta_factor: 0.01 + n_iteration_test: 20 + OB_test: 2.5 + y_test: 5 + Zmin: 0 + Zmax: 60000 + bmatrix_filename: ../resources/bmatrix/gnssro/gnssro_bmatrix.txt + filter variables: + - name: bending_angle + passedBenchmark: 229 diff --git a/test/testinput/gnssrobndnbam.yaml b/test/testinput/gnssrobndnbam.yaml index 664f2e9bb..9a6ac4742 100644 --- a/test/testinput/gnssrobndnbam.yaml +++ b/test/testinput/gnssrobndnbam.yaml @@ -10,9 +10,9 @@ observations: obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 obsgrouping: - group variable: "record_number" + group variables: [ "record_number" ] sort variable: "impact_height" sort order: "ascending" obsdataout: @@ -34,9 +34,9 @@ observations: obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 obsgrouping: - group variable: "record_number" + group variables: [ "record_number" ] sort variable: "impact_height" sort order: "ascending" obsdataout: diff --git a/test/testinput/gnssrobndnbam_0obs.yaml b/test/testinput/gnssrobndnbam_0obs.yaml index 9af6efc5c..0e10694b5 100644 --- a/test/testinput/gnssrobndnbam_0obs.yaml +++ b/test/testinput/gnssrobndnbam_0obs.yaml @@ -9,7 +9,7 @@ observations: obs space: name: GnssroBndNBAM obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_1obs_bending_angle.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_1obs_bending_angle.nc4 simulated variables: [bending_angle] geovals: filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_1obs_bending_angle.nc4 diff --git a/test/testinput/gnssrobndnbam_1obs.yaml b/test/testinput/gnssrobndnbam_1obs.yaml index 108fe4f02..7dfe9f786 100644 --- a/test/testinput/gnssrobndnbam_1obs.yaml +++ b/test/testinput/gnssrobndnbam_1obs.yaml @@ -8,7 +8,7 @@ observations: obs space: name: GnssroBndNBAM obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_1obs_bending_angle.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_1obs_bending_angle.nc4 simulated variables: [bending_angle] geovals: filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_1obs_bending_angle.nc4 diff --git a/test/testinput/gnssrobndnbam_backgroundcheck_qc.yaml b/test/testinput/gnssrobndnbam_backgroundcheck_qc.yaml index 39ad71b35..01405d920 100644 --- a/test/testinput/gnssrobndnbam_backgroundcheck_qc.yaml +++ b/test/testinput/gnssrobndnbam_backgroundcheck_qc.yaml @@ -10,7 +10,7 @@ observations: obs space: name: GnssroBndNBAM obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 obsdataout: obsfile: Data/gnssro_bndnbam_2018041500_3prof_backgroundcheck.nc4 simulated variables: [bending_angle] @@ -34,7 +34,7 @@ observations: obs space: name: GnssroBndNBAM obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 obsdataout: obsfile: Data/gnssro_bndnbam_2018041500_3prof_backgroundcheckronbam.nc4 simulated variables: [bending_angle] diff --git a/test/testinput/gnssrobndnbam_qc_action_obserr_inflation.yaml b/test/testinput/gnssrobndnbam_qc_action_obserr_inflation.yaml index 8095a7311..7f8ee671e 100644 --- a/test/testinput/gnssrobndnbam_qc_action_obserr_inflation.yaml +++ b/test/testinput/gnssrobndnbam_qc_action_obserr_inflation.yaml @@ -10,9 +10,9 @@ observations: obs space: name: GnssroBndNBAM obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_m.nc4 obsgrouping: - group variable: "record_number" + group variables: [ "record_number" ] sort variable: "impact_height" sort order: "descending" obsdataout: diff --git a/test/testinput/gnssrobndnbam_super_refraction.yaml b/test/testinput/gnssrobndnbam_super_refraction.yaml index 87f70be98..1f45457e2 100644 --- a/test/testinput/gnssrobndnbam_super_refraction.yaml +++ b/test/testinput/gnssrobndnbam_super_refraction.yaml @@ -10,9 +10,9 @@ observations: obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 obsgrouping: - group variable: "record_number" + group variables: [ "record_number" ] sort variable: "impact_height" sort order: "descending" obsdataout: @@ -30,9 +30,9 @@ observations: obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 obsgrouping: - group variable: "record_number" + group variables: [ "record_number" ] sort variable: "impact_height" sort order: "descending" obsdataout: @@ -49,9 +49,9 @@ observations: obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_3prof.nc4 obsgrouping: - group variable: "record_number" + group variables: [ "record_number" ] sort variable: "impact_height" sort order: "descending" obsdataout: diff --git a/test/testinput/gnssrobndropp1d.yaml b/test/testinput/gnssrobndropp1d.yaml index b5202f8dc..0406867aa 100644 --- a/test/testinput/gnssrobndropp1d.yaml +++ b/test/testinput/gnssrobndropp1d.yaml @@ -8,17 +8,11 @@ observations: obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_m.nc4 simulated variables: [bending_angle] geovals: filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_m.nc4 - obs filters: - - filter: Background Check - filter variables: - - name: bending_angle - threshold: 3.0 rms ref: 0.008096593619323458 - passedBenchmark: 149 tolerance: 1.0e-13 linear obs operator test: iterations TL: 10 diff --git a/test/testinput/gnssrobndropp1d_qc.yaml b/test/testinput/gnssrobndropp1d_qc.yaml new file mode 100644 index 000000000..313bc6904 --- /dev/null +++ b/test/testinput/gnssrobndropp1d_qc.yaml @@ -0,0 +1,20 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs operator: + name: GnssroBndROPP1D + obs options: + obs space: + name: GnssroBnd + obsdatain: + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_m.nc4 + simulated variables: [bending_angle] + geovals: + filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_m.nc4 + obs filters: + - filter: Background Check + filter variables: + - name: bending_angle + threshold: 3.0 + passedBenchmark: 149 diff --git a/test/testinput/gnssrobndropp2d.yaml b/test/testinput/gnssrobndropp2d.yaml index 111b4a707..30275d7f3 100644 --- a/test/testinput/gnssrobndropp2d.yaml +++ b/test/testinput/gnssrobndropp2d.yaml @@ -11,13 +11,13 @@ observations: obs space: name: GnssroBnd obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_s.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_s.nc4 simulated variables: [bending_angle] geovals: filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_s_2d.nc4 loc_multiplier: 3 rms ref: 0.009216235643012125 - tolerance: 1.0e-12 + tolerance: 1.0e-11 linear obs operator test: iterations TL: 10 tolerance TL: 1.0e-12 diff --git a/test/testinput/gnssroref.yaml b/test/testinput/gnssroref.yaml index a47052263..d31397771 100644 --- a/test/testinput/gnssroref.yaml +++ b/test/testinput/gnssroref.yaml @@ -8,7 +8,7 @@ observations: obs space: name: GnssroRef obsdatain: - obsfile: Data/ioda/testinput_tier_1/gnssro_obs_2018041500_s.nc4 + obsfile: Data/ufo/testinput_tier_1/gnssro_obs_2018041500_s.nc4 simulated variables: [refractivity] geovals: filename: Data/ufo/testinput_tier_1/gnssro_geoval_2018041500_s.nc4 diff --git a/test/testinput/gome_metop-a.yaml b/test/testinput/gome_metop-a.yaml index 436f2e0fa..e004523ae 100644 --- a/test/testinput/gome_metop-a.yaml +++ b/test/testinput/gome_metop-a.yaml @@ -10,7 +10,7 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/gome_metop-a_obs_2019101700_m.nc4 + obsfile: Data/ufo/testinput_tier_1/gome_metop-a_obs_2019101700_m.nc4 obsdataout: obsfile: Data/gome_metop-a_obs_2019101700_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/gome_metop-a_flipz.yaml b/test/testinput/gome_metop-a_flipz.yaml index 281b29241..c1a335a7c 100644 --- a/test/testinput/gome_metop-a_flipz.yaml +++ b/test/testinput/gome_metop-a_flipz.yaml @@ -10,7 +10,7 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/gome_metop-a_obs_2019101700_m.nc4 + obsfile: Data/ufo/testinput_tier_1/gome_metop-a_obs_2019101700_m.nc4 obsdataout: obsfile: Data/gome_metop-a_obs_flipz_2019101700_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/groundgnssmetoffice.yaml b/test/testinput/groundgnssmetoffice.yaml index 1a6ccd899..28c116ce3 100644 --- a/test/testinput/groundgnssmetoffice.yaml +++ b/test/testinput/groundgnssmetoffice.yaml @@ -5,12 +5,18 @@ observations: - obs operator: name: GroundgnssMetOffice obs options: + min_temp_grad: 1.0e-6 obs space: name: Groundgnss obsdatain: - obsfile: Data/ioda/testinput_tier_1/groundgnss_obs_2019123006_obs.nc + obsfile: Data/ufo/testinput_tier_1/groundgnss_obs_2019123006_obs.nc simulated variables: [ZTD] geovals: filename: Data/ufo/testinput_tier_1/groundgnss_geovals_20191230T0600Z.nc4 norm ref: HofX tolerance: 1.0e-5 + linear obs operator test: + coef TL: 1.0e-4 + iterations TL: 10 + tolerance TL: 1.0e-14 + tolerance AD: 1.0e-14 diff --git a/test/testinput/groundgnssropp.yaml b/test/testinput/groundgnssropp.yaml new file mode 100644 index 000000000..25e2a6416 --- /dev/null +++ b/test/testinput/groundgnssropp.yaml @@ -0,0 +1,16 @@ +window begin: 2019-12-30T03:00:00Z +window end: 2019-12-30T09:00:00Z + +observations: +- obs operator: + name: GroundgnssROPP + obs options: + obs space: + name: Groundgnss + obsdatain: + obsfile: Data/ufo/testinput_tier_1/groundgnss_obs_2019123006_obs.nc + simulated variables: [ZTD] + geovals: + filename: Data/ufo/testinput_tier_1/groundgnss_geovals_20191230T0600Z.nc4 + norm ref: HofX + tolerance: 1.0e-3 diff --git a/test/testinput/gsisfcmodel.yaml b/test/testinput/gsisfcmodel.yaml index 40f7f0fd1..7b93cb030 100644 --- a/test/testinput/gsisfcmodel.yaml +++ b/test/testinput/gsisfcmodel.yaml @@ -8,7 +8,7 @@ observations: obs space: name: GSISfcModel obsdatain: - obsfile: Data/ioda/testinput_tier_1/gsisfc_tsen_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/gsisfc_tsen_obs_2018041500_m.nc4 obsdataout: obsfile: Data/gsisfc_tsen_2018041500_m_out.nc4 simulated variables: [air_temperature] @@ -22,7 +22,7 @@ observations: obs space: name: GSISfcModel obsdatain: - obsfile: Data/ioda/testinput_tier_1/gsisfc_uv_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/gsisfc_uv_obs_2018041500_m.nc4 obsdataout: obsfile: Data/gsisfc_uv_fact10_2018041500_m_out.nc4 simulated variables: [eastward_wind, northward_wind] @@ -36,7 +36,7 @@ observations: obs space: name: GSISfcModel obsdatain: - obsfile: Data/ioda/testinput_tier_1/gsisfc_uv_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/gsisfc_uv_obs_2018041500_m.nc4 obsdataout: obsfile: Data/gsisfc_uv_2018041500_m_out.nc4 simulated variables: [eastward_wind, northward_wind] diff --git a/test/testinput/hirs4_crtm.yaml b/test/testinput/hirs4_crtm.yaml index 9c6efce40..f158f8e2f 100644 --- a/test/testinput/hirs4_crtm.yaml +++ b/test/testinput/hirs4_crtm.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv linear obs operator: Absorbers: [H2O,O3,CO2] obs options: @@ -14,7 +15,7 @@ observations: obs space: name: hirs4_metop-a obsdatain: - obsfile: Data/ioda/testinput_tier_1/hirs4_metop-a_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/hirs4_metop-a_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 1-15 geovals: diff --git a/test/testinput/iasi_crtm.yaml b/test/testinput/iasi_crtm.yaml index 7e726d6da..32b6a72b3 100644 --- a/test/testinput/iasi_crtm.yaml +++ b/test/testinput/iasi_crtm.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: iasi_metop-a EndianType: little_endian @@ -12,7 +13,7 @@ observations: obs space: name: iasi_metop-a obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 simulated variables: [brightness_temperature] channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, diff --git a/test/testinput/iasi_crtm_bc.yaml b/test/testinput/iasi_crtm_bc.yaml deleted file mode 100644 index 751e9d617..000000000 --- a/test/testinput/iasi_crtm_bc.yaml +++ /dev/null @@ -1,114 +0,0 @@ -window begin: 2018-04-14T21:00:00Z -window end: 2018-04-15T03:00:00Z - -observations: -- obs operator: - name: CRTM - Absorbers: [H2O,O3,CO2] - linear obs operator: - Absorbers: [H2O,O3,CO2] - obs options: - inspectProfile: 1 - Sensor_ID: &Sensor_ID iasi_metop-a - EndianType: little_endian - CoefficientPath: Data/ - obs space: - name: iasi_metop-a - obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 -# obsdataout: -# obsfile: Data/iasi_metop-a_obs_2018041500_m_unittest_out.nc4 - simulated variables: [brightness_temperature] - channels: &channels - 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, - 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, - 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, - 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, - 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, - 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, - 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, - 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, - 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, - 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, - 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, - 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, - 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, - 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, - 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, - 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, - 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, - 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, - 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, - 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, - 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, - 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, - 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, - 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, - 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, - 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, - 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, - 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, - 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, - 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, - 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, - 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, - 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, - 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, - 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, - 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, - 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, - 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, - 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, - 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, - 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, - 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, - 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, - 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, - 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, - 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, - 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, - 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, - 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 - geovals: - filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_unittest.nc4 - obs bias: - name: LinearCombination - abias_in: Data/ufo/testinput_tier_1/satbias_crtm_in - abias_out: Data/ufo/testinput_tier_1/satbias_crtm_out - sensor: *Sensor_ID - jobs: *channels - predictors: - - predictor: - name: constant - - predictor: - name: lapse_rate - options: - order: 2 - tlapse: &iasi_metop-a_tlap Data/ufo/testinput_tier_1/iasi_metop-a_tlapmean.txt - - predictor: - name: lapse_rate - options: - tlapse: *iasi_metop-a_tlap - - predictor: - name: emissivity - - predictor: - name: scan_angle - options: - order: 4 - - predictor: - name: scan_angle - options: - order: 3 - - predictor: - name: scan_angle - options: - order: 2 - - predictor: - name: scan_angle - vector ref: GsiHofXBc - tolerance: 1.e-8 - linear obs operator test: - iterations TL: 2 - coef TL: 1.e-3 - tolerance TL: 1.0e-3 - tolerance AD: 1.0e-11 diff --git a/test/testinput/iasi_qc.yaml b/test/testinput/iasi_qc.yaml index 705f8f8e1..cc13e38ae 100644 --- a/test/testinput/iasi_qc.yaml +++ b/test/testinput/iasi_qc.yaml @@ -5,9 +5,9 @@ observations: - obs space: name: iasi_metop-a obsdatain: -# obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_qc.nc4 -# obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m.nc4 +# obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_qc.nc4 +# obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, diff --git a/test/testinput/iasi_qc_filters.yaml b/test/testinput/iasi_qc_filters.yaml index 15d42a80c..1e19c7000 100755 --- a/test/testinput/iasi_qc_filters.yaml +++ b/test/testinput/iasi_qc_filters.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: &Sensor_ID iasi_metop-a EndianType: little_endian @@ -12,9 +13,9 @@ observations: obs space: name: iasi_metop-a obsdatain: -# obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m.nc4 -# obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_qc.nc4 - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 +# obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m.nc4 +# obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 # obsdataout: # obsfile: Data/iasi_metop-a_obs_2018041500_m_out.nc4 # obsfile: Data/iasi_metop-a_obs_2018041500_m_qc_out.nc4 @@ -87,37 +88,28 @@ observations: # filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m_qc.nc4 # filename: Data/ufo/testinput_tier_1/iasi_metop-a_geoval_2018041500_m.nc4 obs bias: - abias_in: Data/ufo/testinput_tier_1/satbias_crtm_in - sensor: *Sensor_ID - jobs: *all_channels - predictors: - - predictor: - name: constant - - predictor: - name: lapse_rate + input file: Data/ufo/testinput_tier_1/satbias_iasi_metop-a.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate options: order: 2 tlapse: &iasi_metop-atlap Data/ufo/testinput_tier_1/iasi_metop-a_tlapmean.txt - - predictor: - name: lapse_rate + - name: lapse_rate options: tlapse: *iasi_metop-atlap - - predictor: - name: emissivity - - predictor: - name: scan_angle + - name: emissivity + - name: scan_angle options: order: 4 - - predictor: - name: scan_angle + - name: scan_angle options: order: 3 - - predictor: - name: scan_angle + - name: scan_angle options: order: 2 - - predictor: - name: scan_angle + - name: scan_angle obs filters: # Wavenumber Check - filter: BlackList diff --git a/test/testinput/instrumentTests/Aircraft_obs/CMakeLists.txt b/test/testinput/instrumentTests/Aircraft_obs/CMakeLists.txt new file mode 100644 index 000000000..680575f8c --- /dev/null +++ b/test/testinput/instrumentTests/Aircraft_obs/CMakeLists.txt @@ -0,0 +1,31 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for Aircraft observations (winds, temperature, some with moisture) + + +# H(x) test +ufo_add_test( NAME instrument_aircraft_gfs_HofX + TIER 1 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/aircraft_gfs_HofX.yaml" + LIBS ufo + LABELS aircraft instrument HofX + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + TEST_DEPENDS ufo_get_ufo_test_data ) + +# H(x) with QC +#ufo_add_test( TARGET test_ufo_aircraft_gfs_qc +# TIER 1 +# ECBUILD +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/aircraft_gfs_qc.yaml" +# LABELS aircraft instrument QC +# ENVIRONMENT OOPS_TRAPFPE=1 +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS test_ufo_aircraft_gfs_HofX ) + diff --git a/test/testinput/instrumentTests/Aircraft_obs/aircraft_gfs_HofX.yaml b/test/testinput/instrumentTests/Aircraft_obs/aircraft_gfs_HofX.yaml new file mode 100644 index 000000000..6e53e30d7 --- /dev/null +++ b/test/testinput/instrumentTests/Aircraft_obs/aircraft_gfs_HofX.yaml @@ -0,0 +1,37 @@ +window begin: '2020-11-01T09:00:00Z' +window end: '2020-11-01T15:00:00Z' + +observations: +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_obs_2020110112_m.nc4 + simulated variables: [air_temperature] + obs operator: + name: VertInterp + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.0e-5 +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_obs_2020110112_m.nc4 + simulated variables: [specific_humidity] + obs operator: + name: VertInterp + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.0e-5 +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_obs_2020110112_m.nc4 + simulated variables: [eastward_wind,northward_wind] + obs operator: + name: VertInterp + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 0.01 diff --git a/test/testinput/instrumentTests/Aircraft_obs/aircraft_gfs_qc.yaml b/test/testinput/instrumentTests/Aircraft_obs/aircraft_gfs_qc.yaml new file mode 100644 index 000000000..ac5825514 --- /dev/null +++ b/test/testinput/instrumentTests/Aircraft_obs/aircraft_gfs_qc.yaml @@ -0,0 +1,423 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs space: + name: aircraft_QC_wind + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_obs_2020110112_m.nc4 +# obsgrouping: +# group variables: ["tail_number"] # Does not exist yet in IODA obs file. +# sort variable: "air_pressure" +# sort order: "descending" + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_geoval_2020110112_m.nc4 + obs filters: +#-------------------------------------------------------------------------------------------------------------------- +# WINDS +#-------------------------------------------------------------------------------------------------------------------- +# +# Begin by assigning all ObsError to a constant value. These will get overwritten (as needed) for specific types. + - filter: BlackList + filter variables: + - name: eastward_wind + - name: northward_wind + action: + name: assign error + error parameter: 2.0 # 2.0 m/s +# +# Assign intial ObsError specific to AIREP/ACARS + - filter: BlackList + filter variables: + - name: eastward_wind + - name: northward_wind + action: + name: assign error + error parameter: 3.6 # 3.6 m/s + where: + - variable: + name: eastward_wind@ObsType + is_in: 230 +# +# Assign intial ObsError specific to AMDAR + - filter: BlackList + filter variables: + - name: eastward_wind + - name: northward_wind + action: + name: assign error + error parameter: 3.0 # 3.0 m/s + where: + - variable: + name: eastward_wind@ObsType + is_in: 231 +# +# Assign intial ObsError specific to MDCRS + - filter: BlackList + filter variables: + - name: eastward_wind + - name: northward_wind + action: + name: assign error + error parameter: 2.5 # 2.5 m/s + where: + - variable: + name: eastward_wind@ObsType + is_in: 233 +# +# Assign the initial ObsError, based on height/pressure for RECON aircraft + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [70000, 65000, 60000, 55000, 50000, 45000, 40000, 35000, 30000, 25000, 20000, 15000, 10000, 7500, 5000] + errors: [2.4, 2.5, 2.6, 2.7, 2.8, 2.95, 3.1, 3.25, 3.4, 3.175, 2.95, 2.725, 2.5, 2.6, 2.7] + where: + - variable: + name: eastward_wind@ObsType + is_in: 232 +# +# Reject all obs with PreQC mark already set above 3 + - filter: PreQC + maxvalue: 3 + action: + name: reject +# +# Observation Range Sanity Check: either wind component or velocity exceeds 135 m/s + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: reject + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: Velocity@ObsFunction + maxvalue: 135.0 + action: + name: reject +# +# Reject when pressure is less than 126 mb. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + minvalue: 12600 + action: + name: reject +# +# Reject when difference of wind direction is more than 50 degrees. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: WindDirAngleDiff@ObsFunction + options: + test_hofx: GsiHofX + maxvalue: 50.0 + action: + name: reject +# +# When multiple obs exist within a single vertical model level, inflate ObsError + - filter: BlackList + filter variables: + - name: eastward_wind + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [eastward_wind] + defer to post: true +# + - filter: BlackList + filter variables: + - name: northward_wind + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [northward_wind] + defer to post: true +# +# If background check is largely different than obs, inflate ObsError + - filter: Background Check + filter variables: + - name: eastward_wind + - name: northward_wind + absolute threshold: 7.5 + test_hofx: GsiHofX # Remove this line when running UFO H(x) + action: + name: inflate error + inflation factor: 3.0 + defer to post: true +# + - filter: Bounds Check + filter variables: + - name: eastward_wind + action: + name: reject + maxvalue: 7.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: eastward_wind@ObsErrorData # After inflation step + denominator: + name: eastward_wind@ObsError + defer to post: true +# +# If ObsError inflation factor is larger than threshold, reject obs + - filter: Bounds Check + filter variables: + - name: northward_wind + action: + name: reject + maxvalue: 7.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: northward_wind@ObsErrorData # After inflation step + denominator: + name: northward_wind@ObsError + defer to post: true +# + passedBenchmark: 480 +#-------------------------------------------------------------------------------------------------------------------- +# TEMPERATURE +#-------------------------------------------------------------------------------------------------------------------- +- obs space: + name: aircraft_QC_temp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_obs_2020110112_m.nc4 + simulated variables: [air_temperature] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_geoval_2020110112_m.nc4 + obs filters: +# +# Begin by assigning all ObsError to a constant value. These will get overwritten for specific types. + - filter: BlackList + filter variables: + - name: air_temperature + action: + name: assign error + error parameter: 2.0 # 2.0 K +# +# Assign the initial observation error, based on pressure (for AIREP/ACARS; itype=130) + - filter: Bounds Check + filter variables: + - name: air_temperature + minvalue: 195 + maxvalue: 327 + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [100000, 95000, 90000, 85000, 80000] + errors: [2.5, 2.3, 2.1, 1.9, 1.7] + where: + - variable: + name: air_temperature@ObsType + is_in: 130 +# +# Assign the initial observation error, based on pressure (for AMDAR and MDCRS; itype=131,133) + - filter: Bounds Check + filter variables: + - name: air_temperature + minvalue: 195 + maxvalue: 327 + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [100000, 95000, 90000, 85000, 80000] + errors: [1.4706, 1.3529, 1.2353, 1.1176, 1.0] + where: + - variable: + name: air_temperature@ObsType + is_in: 131,133 +# +# Assign the initial observation error, based on pressure (for RECON aircraft; itype=132) + - filter: Bounds Check + filter variables: + - name: air_temperature + minvalue: 195 + maxvalue: 327 + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [100000, 95000, 90000, 85000, 35000, 30000, 25000, 20000, 15000, 10000, 7500, 5000, 4000, 3200, 2000, 1000] + errors: [1.2, 1.1, 0.9, 0.8, 0.8, 0.9, 1.2, 1.2, 1.0, 0.8, 0.8, 0.9, 0.95, 1.0, 1.25, 1.5] + where: + - variable: + name: air_temperature@ObsType + is_in: 132 +# +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: air_temperature + minvalue: 195 + maxvalue: 327 + action: + name: reject +# +# Reject all obs with PreQC mark already set above 3 + - filter: PreQC + maxvalue: 3 + action: + name: reject +# +# When multiple obs exist within a single vertical model level, inflate ObsError + - filter: BlackList + filter variables: + - name: air_temperature + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [air_temperature] + defer to post: true +# +# If background check is largely different than obs, inflate ObsError + - filter: Background Check + filter variables: + - name: temperature + absolute threshold: 4.0 + test_hofx: GsiHofX # Remove this line when running UFO H(x) + action: + name: inflate error + inflation factor: 3.0 + defer to post: true +# +# If ObsError inflation factor is larger than threshold, reject obs + - filter: Bounds Check + filter variables: + - name: air_temperature + action: + name: reject + maxvalue: 7.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: air_temperature@ObsErrorData # After inflation step + denominator: + name: air_temperature@ObsError + defer to post: true +# + passedBenchmark: 188 +#-------------------------------------------------------------------------------------------------------------------- +# MOISTURE +#-------------------------------------------------------------------------------------------------------------------- +- obs space: + name: aircraft_QC_moisture + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_obs_2020110112_m.nc4 + simulated variables: [specific_humidity] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/aircraft_geoval_2020110112_m.nc4 + obs filters: +# +# Assign the initial observation error, based on height/pressure ONLY MDCRS + - filter: Bounds Check + filter variables: + - name: specific_humidity + minvalue: 1.0E-7 + maxvalue: 0.34999999 + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [110000, 105000, 100000, 95000, 90000, 85000, 80000, 75000, 70000, 65000, 60000, 55000, + 50000, 45000, 40000, 35000, 30000, 25000, 20000, 15000, 10000, 7500, 5000, 4000, 3000] + errors: [.19455, .19062, .18488, .17877, .17342, .16976, .16777, .16696, .16605, .16522, .16637, .17086, + .17791, .18492, .18996, .19294, .19447, .19597, .19748, .19866, .19941, .19979, .19994, .19999, .2] + scale_factor_var: specific_humidity@ObsValue + where: + - variable: + name: specific_humidity@ObsType + is_in: 133 +# +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: specific_humidity + minvalue: 1.0E-7 + maxvalue: 0.34999999 + action: + name: reject +# +# Reject all obs with PreQC mark already set above 3 + - filter: PreQC + maxvalue: 3 + action: + name: reject +# +# When multiple obs exist within a single vertical model level, inflate ObsError + - filter: BlackList + filter variables: + - name: specific_humidity + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [specific_humidity] + defer to post: true +# +# If ObsError inflation factor is larger than threshold, reject obs + - filter: Bounds Check + filter variables: + - name: specific_humidity + action: + name: reject + maxvalue: 8.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: specific_humidity@ObsErrorData # After inflation step + denominator: + name: specific_humidity@ObsError + defer to post: true +# + passedBenchmark: 48 diff --git a/test/testinput/instrumentTests/CMakeLists.txt b/test/testinput/instrumentTests/CMakeLists.txt new file mode 100644 index 000000000..d195d3ddf --- /dev/null +++ b/test/testinput/instrumentTests/CMakeLists.txt @@ -0,0 +1,23 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +add_subdirectory(Aircraft_obs) +add_subdirectory(airs) +add_subdirectory(amsua) +add_subdirectory(atms) +add_subdirectory(avhrr) +add_subdirectory(cris) +add_subdirectory(iasi) +add_subdirectory(mhs) +add_subdirectory(radarVAD) +add_subdirectory(RadioAcousticSounder) +add_subdirectory(SatWinds_AMV) +add_subdirectory(Scatterometer) +add_subdirectory(Sonde) +add_subdirectory(seviri) +add_subdirectory(ssmis) +add_subdirectory(Surface_land_obs) +add_subdirectory(Surface_marine_obs) + diff --git a/test/testinput/instrumentTests/RadioAcousticSounder/CMakeLists.txt b/test/testinput/instrumentTests/RadioAcousticSounder/CMakeLists.txt new file mode 100644 index 000000000..e472f5085 --- /dev/null +++ b/test/testinput/instrumentTests/RadioAcousticSounder/CMakeLists.txt @@ -0,0 +1,19 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for Radio Acoustic Sounding Systems (RASS) + + +# H(x) test +ecbuild_add_test( TARGET test_ufo_instrument_rass_gfs_HofX + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/rass_gfs_HofX.yaml" + ENVIRONMENT OOPS_TRAPFPE=1 + MPI 1 + LABELS rass HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ) + diff --git a/test/testinput/instrumentTests/RadioAcousticSounder/rass_gfs_HofX.yaml b/test/testinput/instrumentTests/RadioAcousticSounder/rass_gfs_HofX.yaml new file mode 100644 index 000000000..4a85c39ca --- /dev/null +++ b/test/testinput/instrumentTests/RadioAcousticSounder/rass_gfs_HofX.yaml @@ -0,0 +1,15 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: VertInterp + obs space: + name: RASS-tv + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/rass_obs_2020110112_m.nc4 + simulated variables: [virtual_temperature] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/rass_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.0e-06 diff --git a/test/testinput/instrumentTests/SatWinds_AMV/CMakeLists.txt b/test/testinput/instrumentTests/SatWinds_AMV/CMakeLists.txt new file mode 100644 index 000000000..90cd1db21 --- /dev/null +++ b/test/testinput/instrumentTests/SatWinds_AMV/CMakeLists.txt @@ -0,0 +1,28 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for Satellite-derived atmospheric motion vectors (AMV) + + +# H(x) test +ecbuild_add_test( TARGET test_ufo_satwinds_gfs_HofX + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/satwinds_gfs_HofX.yaml" + LABELS satwinds HofX instrument + ENVIRONMENT OOPS_TRAPFPE=1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ) + +# H(x) with QC +#ecbuild_add_test( TARGET test_ufo_satwinds_gfs_qc +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/satwinds_gfs_qc.yaml" +# LABELS satwinds QC instrument +# ENVIRONMENT OOPS_TRAPFPE=1 +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS test_ufo_satwinds_gfs_HofX ) + diff --git a/test/testinput/instrumentTests/SatWinds_AMV/satwinds_gfs_HofX.yaml b/test/testinput/instrumentTests/SatWinds_AMV/satwinds_gfs_HofX.yaml new file mode 100644 index 000000000..3c4678329 --- /dev/null +++ b/test/testinput/instrumentTests/SatWinds_AMV/satwinds_gfs_HofX.yaml @@ -0,0 +1,15 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: VertInterp + obs space: + name: Satwind + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/satwind_obs_2020110112_m.nc4 + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/satwind_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.0e-05 diff --git a/test/testinput/instrumentTests/SatWinds_AMV/satwinds_gfs_qc.yaml b/test/testinput/instrumentTests/SatWinds_AMV/satwinds_gfs_qc.yaml new file mode 100644 index 000000000..ea46aac45 --- /dev/null +++ b/test/testinput/instrumentTests/SatWinds_AMV/satwinds_gfs_qc.yaml @@ -0,0 +1,325 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs space: + name: satwinds_QC + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/satwind_obs_2020110112_m.nc4 + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/satwind_geoval_2020110112_m.nc4 + obs filters: +# +# Reject all obs with PreQC mark already set above 3 + - filter: PreQC + maxvalue: 3 + action: + name: reject +# +# Assign the initial observation error, based on height/pressure + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [100000, 95000, 80000, 65000, 60000, 55000, 50000, 45000, 40000, 35000, 30000, 25000, 20000, 15000, 10000] #Pressure (Pa) + errors: [1.4, 1.5, 1.6, 1.8, 1.9, 2.0, 2.1, 2.3, 2.6, 2.8, 3.0, 3.2, 2.7, 2.4, 2.1] +# +# Observation Range Sanity Check: either wind component or velocity exceeds 135 m/s + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: reject + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: Velocity@ObsFunction + maxvalue: 135.0 + action: + name: reject +# +# All satellite platforms, reject when pressure greater than 950 mb. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + maxvalue: 95000 + action: + name: reject +# +# Difference check surface_pressure and air_pressure@ObsValue, if less than 100 hPa, reject. +# Starting with 730029 values, 338418 missing (half?), 50883 rejected by difference check, leaving 340728 + - filter: Difference Check + filter variables: + - name: eastward_wind + - name: northward_wind + reference: surface_pressure@GeoVaLs + value: air_pressure@MetaData + maxvalue: -10000 +# +# Multiple satellite platforms, reject when pressure is more than 50 mb above tropopause. + - filter: Difference Check + filter variables: + - name: eastward_wind + - name: northward_wind + reference: TropopauseEstimate@ObsFunction + value: air_pressure@MetaData + minvalue: -5000 # 50 hPa above tropopause level, negative p-diff + action: + name: reject +# +# GOES WV (non-cloudy; itype=247) reject when difference of wind direction is more than 50 degrees. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: WindDirAngleDiff@ObsFunction + options: + test_hofx: GsiHofX + maxvalue: 50.0 + action: + name: reject +# +# GOES IR (245), EUMET IR (253), JMA IR (252) reject when pressure between 400 and 800 mb. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + minvalue: 40000 + maxvalue: 80000 + where: + - variable: + name: eastward_wind@ObsType + is_in: 245, 252, 253 + action: + name: reject +# +# GOES WV (246, 250, 254), reject when pressure greater than 400 mb. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + maxvalue: 40000 + where: + - variable: + name: eastward_wind@ObsType + is_in: 246, 250, 254 + action: + name: reject +# +# EUMET (242) and JMA (243) vis, reject when pressure less than 700 mb. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + minvalue: 70000 + where: + - variable: + name: eastward_wind@ObsType + is_in: 242, 243 + action: + name: reject +# +# MODIS-Aqua/Terra (257) and (259), reject when pressure less than 250 mb. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + minvalue: 25000 + where: + - variable: + name: eastward_wind@ObsType + is_in: 257-259 + action: + name: reject +# +# MODIS-Aqua/Terra (258) and (259), reject when pressure greater than 600 mb. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + maxvalue: 60000 + where: + - variable: + name: eastward_wind@ObsType + is_in: 258, 259 + action: + name: reject +# +# AVHRR (244), MODIS (257,258,259), VIIRS (260), GOES (247) use a LNVD check. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: SatWindsLNVDCheck@ObsFunction + options: + testHofX: GsiHofX + maxvalue: 3 + where: + - variable: + name: eastward_wind@ObsType + is_in: 244, 247, 257-260 + action: + name: reject +# +# AVHRR and MODIS (ObsType=244,257,258,259) use a SPDB check. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: SatWindsSPDBCheck@ObsFunction + options: + error_min: 1.4 + error_max: 20.0 + testHofX: GsiHofX + maxvalue: 1.75 + where: + - variable: + name: eastward_wind@ObsType + is_in: 244, 257, 258, 259 + action: + name: reject +# +# GOES (ObsType=245,246,253,254) use a SPDB check only between 300-400 mb. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: SatWindsSPDBCheck@ObsFunction + options: + error_min: 1.4 + error_max: 20.0 + testHofX: GsiHofX + maxvalue: 1.75 + where: + - variable: + name: eastward_wind@ObsType + is_in: 244, 257, 258, 259 + - variable: + name: air_pressure@MetaData + minvalue: 30000 + maxvalue: 40000 + action: + name: reject +# + - filter: Background Check + filter variables: + - name: eastward_wind + - name: northward_wind + absolute threshold: 7.5 + test_hofx: GsiHofX # Remove this line when running UFO H(x) + action: + name: inflate error + inflation factor: 3.0 + defer to post: true +# +# If the total inflation factor is too big, reject. + - filter: Bounds Check + filter variables: + - name: eastward_wind + action: + name: reject + maxvalue: 2.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: eastward_wind@ObsErrorData # After inflation step + denominator: + name: eastward_wind@ObsError + where: + - variable: + name: eastward_wind@ObsType + is_in: 240, 241, 242, 244, 247, 248, 249, 250, 252, 255-260 + defer to post: true +# + - filter: Bounds Check + filter variables: + - name: northward_wind + action: + name: reject + maxvalue: 2.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: northward_wind@ObsErrorData # After inflation step + denominator: + name: northward_wind@ObsError + where: + - variable: + name: northward_wind@ObsType + is_in: 240, 241, 242, 244, 247, 248, 249, 250, 252, 255-260 + defer to post: true +# +# Some satellite platforms have a lower threshold of inflation factor of 1.5 + - filter: Bounds Check + filter variables: + - name: eastward_wind + action: + name: reject + maxvalue: 1.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: eastward_wind@ObsErrorData # After inflation step + denominator: + name: eastward_wind@ObsError + where: + - variable: + name: eastward_wind@ObsType + is_in: 243, 245, 246, 251, 253, 254 + defer to post: true +# + - filter: Bounds Check + filter variables: + - name: northward_wind + action: + name: reject + maxvalue: 1.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: northward_wind@ObsErrorData # After inflation step + denominator: + name: northward_wind@ObsError + where: + - variable: + name: eastward_wind@ObsType + is_in: 243, 245, 246, 251, 253, 254 + defer to post: true +# + passedBenchmark: 18 diff --git a/test/testinput/instrumentTests/Scatterometer/CMakeLists.txt b/test/testinput/instrumentTests/Scatterometer/CMakeLists.txt new file mode 100644 index 000000000..4ab870412 --- /dev/null +++ b/test/testinput/instrumentTests/Scatterometer/CMakeLists.txt @@ -0,0 +1,28 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for Scatterometer surface winds + + +# H(x) test +ecbuild_add_test( TARGET test_ufo_scatwinds_gfs_HofX + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/scatwinds_gfs_HofX.yaml" + LABELS scatwinds HofX instrument + ENVIRONMENT OOPS_TRAPFPE=1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data ) + +# H(x) with QC +#ecbuild_add_test( TARGET test_ufo_scatwinds_gfs_qc +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/scatwinds_gfs_qc.yaml" +# LABELS scatwinds QC instrument +# ENVIRONMENT OOPS_TRAPFPE=1 +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS test_ufo_scatwinds_HofX ) + diff --git a/test/testinput/instrumentTests/Scatterometer/scatwinds_gfs_HofX.yaml b/test/testinput/instrumentTests/Scatterometer/scatwinds_gfs_HofX.yaml new file mode 100644 index 000000000..f670f864f --- /dev/null +++ b/test/testinput/instrumentTests/Scatterometer/scatwinds_gfs_HofX.yaml @@ -0,0 +1,15 @@ +window begin: 2020-11-01T11:00:00Z +window end: 2020-11-01T13:00:00Z + +observations: +- obs operator: + name: GSISfcModel + obs space: + name: Scatwinds + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/scatwind_obs_2020110112_m.nc4 + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/scatwind_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.0e-03 diff --git a/test/testinput/instrumentTests/Scatterometer/scatwinds_gfs_qc.yaml b/test/testinput/instrumentTests/Scatterometer/scatwinds_gfs_qc.yaml new file mode 100644 index 000000000..4b969f7fb --- /dev/null +++ b/test/testinput/instrumentTests/Scatterometer/scatwinds_gfs_qc.yaml @@ -0,0 +1,127 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs space: + name: scatwinds_QC + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/scatwind_obs_2020110112_m.nc4 + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/scatwind_geoval_2020110112_m.nc4 + obs filters: +# +# Starting out with 446038 total wind obs and zero are marked missing. +# +# Reject all obs with PreQC mark already set above 3 + - filter: PreQC + maxvalue: 3 + action: + name: reject +# +# WindSat (289), ASCAT (290), or OSCAT (291) either wind component or velocity greater than 20 m/s, then reject. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -20 + maxvalue: 20 +# + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: Velocity@ObsFunction + maxvalue: 20.0 + where: + - variable: + name: eastward_wind@ObsType + is_in: 289, 290, 291 + action: + name: reject +# The combination of both filters above rejects 8338 observations. + +# Similar to satellite winds AMV, reject when obs wind direction is more than 50 degrees different than model. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: WindDirAngleDiff@ObsFunction + options: + test_hofx: GsiHofX + maxvalue: 50.0 + action: + name: reject +# Another 14402 observations get rejected by this filter. + +# ASCAT (290), RAPIDSCAT (296) use a LNVD check. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: SatWindsLNVDCheck@ObsFunction + options: + testHofX: GsiHofX + maxvalue: 3 + where: + - variable: + name: eastward_wind@ObsType + is_in: 290, 296 + action: + name: reject + +# Assign the initial observation error (constant value, 3.5 m/s right now). + - filter: BlackList + filter variables: + - name: eastward_wind + - name: northward_wind + action: + name: assign error + error parameter: 3.5 +# +# For OSCAT, ASCAT, RAPIDSCAT, or WindSat, reject when either wind component differs by at least 6 m/s from model. + - filter: Background Check + filter variables: + - name: eastward_wind + - name: northward_wind + absolute threshold: 6.0 + test_hofx: GsiHofX + where: + - variable: + name: eastward_wind@ObsType + is_in: 289, 290, 291, 296 + action: + name: reject +# + - filter: Bounds Check + filter variables: + - name: eastward_wind + action: + name: reject + maxvalue: 5.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: eastward_wind@ObsErrorData # After inflation step + denominator: + name: eastward_wind@ObsError +# + - filter: Bounds Check + filter variables: + - name: northward_wind + action: + name: reject + maxvalue: 5.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: northward_wind@ObsErrorData # After inflation step + denominator: + name: northward_wind@ObsError +# + passedBenchmark: 20 diff --git a/test/testinput/instrumentTests/Sonde/CMakeLists.txt b/test/testinput/instrumentTests/Sonde/CMakeLists.txt new file mode 100644 index 000000000..fad233ba2 --- /dev/null +++ b/test/testinput/instrumentTests/Sonde/CMakeLists.txt @@ -0,0 +1,28 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for rawinsonde and dropsonde observations + + +# H(x) test +ecbuild_add_test( TARGET test_ufo_sonde_gfs_HofX + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/sonde_gfs_HofX.yaml" + LABELS sonde HofX instrument + ENVIRONMENT OOPS_TRAPFPE=1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ) + +# H(x) with QC +#ecbuild_add_test( TARGET test_ufo_sonde_gfs_qc +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/sonde_gfs_qc.yaml" +# LABELS sonde QC instrument +# ENVIRONMENT OOPS_TRAPFPE=1 +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS test_ufo_sonde_gfs_HofX ) + diff --git a/test/testinput/instrumentTests/Sonde/sonde_gfs_HofX.yaml b/test/testinput/instrumentTests/Sonde/sonde_gfs_HofX.yaml new file mode 100644 index 000000000..2ae421063 --- /dev/null +++ b/test/testinput/instrumentTests/Sonde/sonde_gfs_HofX.yaml @@ -0,0 +1,42 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: VertInterp + obs space: + name: Sonde_hofx_tq_test + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/sondes_obs_2020110112_m.nc4 + simulated variables: [air_temperature, specific_humidity] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/sondes_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.0e-05 +# Seperate test for winds that need to set a lower tolerance +- obs operator: + name: VertInterp + obs space: + name: Sonde_hofx_uv_test + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/sondes_obs_2020110112_m.nc4 + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/sondes_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 0.001 +# Surface pressure requires a different HofX obs operator +- obs operator: + name: SfcPCorrected + da_psfc_scheme: UKMO + geovar_geomz: geopotential_height # height (default, delete option) + geovar_sfc_geomz: surface_geopotential_height # surface_altitude (default, delete option) + obs space: + name: Sonde_hofx_ps_test + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/sondes_obs_2020110112_m.nc4 + simulated variables: [surface_pressure] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/sondes_geoval_2020110112_m.nc4 + rms ref: 97199.05489 + tolerance: 1.0e-04 diff --git a/test/testinput/instrumentTests/Sonde/sonde_gfs_qc.yaml b/test/testinput/instrumentTests/Sonde/sonde_gfs_qc.yaml new file mode 100644 index 000000000..1b1720d8e --- /dev/null +++ b/test/testinput/instrumentTests/Sonde/sonde_gfs_qc.yaml @@ -0,0 +1,290 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs space: + name: sonde_QC + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/sondes_obs_2020110112_m.nc4 + obsgrouping: + group variables: ["station_id", "LaunchTime"] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [air_temperature, specific_humidity, surface_pressure, eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/sondes_geoval_2020110112_m.nc4 + obs filters: +# Reject all obs with PreQC mark already set above 3 + - filter: PreQC + maxvalue: 3 + action: + name: reject +# +# Observation Range Sanity Check: temperature, surface_pressure, moisture, winds + - filter: Bounds Check + filter variables: + - name: air_temperature + minvalue: 185 + maxvalue: 327 + action: + name: reject +# + - filter: Bounds Check + filter variables: + - name: surface_pressure + minvalue: 37499 + maxvalue: 106999 + action: + name: reject +# + - filter: Bounds Check + filter variables: + - name: specific_humidity + minvalue: 1.0E-8 + maxvalue: 0.034999999 + action: + name: reject +# + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: reject + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: Velocity@ObsFunction + maxvalue: 135.0 + action: + name: reject +# +# Reject when difference of wind direction is more than 50 degrees. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: WindDirAngleDiff@ObsFunction + options: + test_hofx: GsiHofX + maxvalue: 50.0 + action: + name: reject +# +# Assign the initial observation error, based on height/pressure + - filter: BlackList + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [100000, 95000, 90000, 85000, 35000, 30000, 25000, 20000, 15000, 10000, 7500, 5000, 4000, 3000, 2000, 1000] + errors: [1.2, 1.1, 0.9, 0.8, 0.8, 0.9, 1.2, 1.2, 1.0, 0.8, 0.8, 0.9, 0.95, 1.0, 1.25, 1.5] +# + - filter: BlackList + filter variables: + - name: surface_pressure + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: surface_pressure@ObsValue + xvals: [80000, 75000] + errors: [110, 120] # 1.1 mb below 800 mb and 1.2 mb agove 750 mb +# + - filter: BlackList + filter variables: + - name: surface_pressure + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSfcPressure@ObsFunction + options: + error_min: 100 # 1 mb + error_max: 300 # 3 mb + geovar_sfc_geomz: surface_geopotential_height +# + - filter: BlackList + filter variables: + - name: specific_humidity + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [25000, 20000, 10] + errors: [0.2, 0.4, 0.8] # 20% RH up to 250 mb, then increased rapidly above + scale_factor_var: specific_humidity@ObsValue +# + - filter: BlackList + filter variables: + - name: eastward_wind + - name: northward_wind + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [100000, 95000, 80000, 65000, 60000, 55000, 50000, 45000, 40000, 35000, 30000, 25000, 20000, 15000, 10000] #Pressure (Pa) + errors: [1.4, 1.5, 1.6, 1.8, 1.9, 2.0, 2.1, 2.3, 2.6, 2.8, 3.0, 3.2, 2.7, 2.4, 2.1] +# +# Inflate obserror when multiple obs exist inside vertical model layers. + - filter: BlackList + filter variables: + - name: specific_humidity + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [specific_humidity] + defer to post: true + - filter: BlackList + filter variables: + - name: air_temperature + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [air_temperature] + defer to post: true + - filter: BlackList + filter variables: + - name: eastward_wind + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [eastward_wind] + defer to post: true +# + - filter: BlackList + filter variables: + - name: northward_wind + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [northward_wind] + defer to post: true +# + - filter: Background Check + filter variables: + - name: air_temperature + absolute threshold: 4.0 + test_hofx: GsiHofX # Remove this line when running UFO H(x) + action: + name: inflate error + inflation factor: 3.0 + defer to post: true +# + - filter: Background Check + filter variables: + - name: eastward_wind + - name: northward_wind + absolute threshold: 7.5 + test_hofx: GsiHofX # Remove this line when running UFO H(x) + action: + name: inflate error + inflation factor: 3.0 + defer to post: true +# +# If the total inflation factor is too big, reject. + - filter: Bounds Check + filter variables: + - name: air_temperature + action: + name: reject + maxvalue: 8.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: air_temperature@ObsErrorData # After inflation step + denominator: + name: air_temperature@ObsError + defer to post: true +# + - filter: Bounds Check + filter variables: + - name: specific_humidity + action: + name: reject + maxvalue: 8.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: specific_humidity@ObsErrorData # After inflation step + denominator: + name: specific_humidity@ObsError + defer to post: true +# + - filter: Bounds Check + filter variables: + - name: eastward_wind + action: + name: reject + maxvalue: 8.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: eastward_wind@ObsErrorData # After inflation step + denominator: + name: eastward_wind@ObsError + defer to post: true +# + - filter: Bounds Check + filter variables: + - name: northward_wind + action: + name: reject + maxvalue: 8.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: northward_wind@ObsErrorData # After inflation step + denominator: + name: northward_wind@ObsError + defer to post: true +# + - filter: Bounds Check + filter variables: + - name: surface_pressure + action: + name: reject + maxvalue: 4.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: surface_pressure@ObsErrorData # After inflation step + denominator: + name: surface_pressure@ObsError + defer to post: true +# + passedBenchmark: 1908 diff --git a/test/testinput/instrumentTests/Surface_land_obs/CMakeLists.txt b/test/testinput/instrumentTests/Surface_land_obs/CMakeLists.txt new file mode 100644 index 000000000..c26a1e03d --- /dev/null +++ b/test/testinput/instrumentTests/Surface_land_obs/CMakeLists.txt @@ -0,0 +1,28 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for Surface-based land observations (METAR, SYNOP, mesonet) + + +# H(x) test +ecbuild_add_test( TARGET test_ufo_sfcLand_gfs_HofX + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/sfcLand_gfs_HofX.yaml" + LABELS sfcLand HofX instrument + ENVIRONMENT OOPS_TRAPFPE=1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ) + +# H(x) with QC +#ecbuild_add_test( TARGET test_ufo_sfcLand_gfs_qc +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/sfcLand_gfs_qc.yaml" +# LABELS sfcLand QC instrument +# ENVIRONMENT OOPS_TRAPFPE=1 +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS test_ufo_sfcLand_gfs_HofX ) + diff --git a/test/testinput/instrumentTests/Surface_land_obs/sfcLand_gfs_HofX.yaml b/test/testinput/instrumentTests/Surface_land_obs/sfcLand_gfs_HofX.yaml new file mode 100644 index 000000000..c4caf429d --- /dev/null +++ b/test/testinput/instrumentTests/Surface_land_obs/sfcLand_gfs_HofX.yaml @@ -0,0 +1,18 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: SfcPCorrected + da_psfc_scheme: UKMO + geovar_geomz: geopotential_height + geovar_sfc_geomz: surface_geopotential_height + obs space: + name: SfcPCorrected + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/sfc_obs_2020110112_m.nc4 + simulated variables: [surface_pressure] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/sfc_geoval_2020110112_m.nc4 + rms ref: 97083.96551 + tolerance: 1.e-06 diff --git a/test/testinput/instrumentTests/Surface_land_obs/sfcLand_gfs_qc.yaml b/test/testinput/instrumentTests/Surface_land_obs/sfcLand_gfs_qc.yaml new file mode 100644 index 000000000..6d422bd4f --- /dev/null +++ b/test/testinput/instrumentTests/Surface_land_obs/sfcLand_gfs_qc.yaml @@ -0,0 +1,123 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs space: + name: surface_pressure_QC + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/sfc_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/sfc_obs_2020120112_out.nc + simulated variables: [surface_pressure, air_temperature] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/sfc_geoval_2020110112_m.nc4 + obs filters: +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: surface_pressure + minvalue: 37499 + maxvalue: 106999 + action: + name: reject +# Reject all obs with PreQC mark already set above 3 + - filter: PreQC + maxvalue: 3 + action: + name: reject +# Assign obsError. + - filter: BlackList + filter variables: + - name: surface_pressure + action: + name: assign error + error parameter: 120 # 120 Pa (1.2mb) +# + - filter: BlackList + filter variables: + - name: surface_pressure + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: surface_pressure@ObsValue + xvals: [80000, 75000] + errors: [110, 120] + where: + - variable: + name: surface_pressure@ObsType + is_in: 181 # Type is SYNOP +# + - filter: BlackList + filter variables: + - name: surface_pressure + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: surface_pressure@ObsValue + xvals: [85000, 80000] + errors: [120, 140] + where: + - variable: + name: surface_pressure@ObsType + is_in: 187 # Type is METAR +# +# Inflate ObsError as it is done with GSI + - filter: BlackList + filter variables: + - name: surface_pressure + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSfcPressure@ObsFunction + options: +# original_obserr: ObsError + error_min: 100 # 1 mb + error_max: 300 # 3 mb + geovar_sfc_geomz: surface_geopotential_height +# +# If the ObsError is inflated too much, reject the obs + - filter: Bounds Check + filter variables: + - name: surface_pressure + action: + name: reject + maxvalue: 3.6 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: surface_pressure@ObsErrorData # After inflation step + denominator: + name: surface_pressure@ObsError + defer to post: true + where: + - variable: + name: surface_pressure@ObsType + is_in: 181 # Type is SYNOP +# + - filter: Bounds Check + filter variables: + - name: surface_pressure + action: + name: reject + maxvalue: 4.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: surface_pressure@ObsErrorData # After inflation step + denominator: + name: surface_pressure@ObsError + defer to post: true + where: + - variable: + name: surface_pressure@ObsType + is_in: 187 # Type is METAR +# + passedBenchmark: 53 diff --git a/test/testinput/instrumentTests/Surface_marine_obs/CMakeLists.txt b/test/testinput/instrumentTests/Surface_marine_obs/CMakeLists.txt new file mode 100644 index 000000000..e48064174 --- /dev/null +++ b/test/testinput/instrumentTests/Surface_marine_obs/CMakeLists.txt @@ -0,0 +1,28 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for Surface-based marine observations (ship, buoy, C-MAN) + + +# H(x) test +ecbuild_add_test( TARGET test_ufo_sfcMarine_gfs_HofX + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/sfcMarine_gfs_HofX.yaml" + LABELS sfcMarine HofX instrument + ENVIRONMENT OOPS_TRAPFPE=1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ) + +# H(x) with QC +#ecbuild_add_test( TARGET test_ufo_sfcMarine_gfs_qc +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/sfcMarine_gfs_qc.yaml" +# LABELS sfcMarine QC instrument +# ENVIRONMENT OOPS_TRAPFPE=1 +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS test_ufo_sfcMarine_gfs_HofX ) + diff --git a/test/testinput/instrumentTests/Surface_marine_obs/sfcMarine_gfs_HofX.yaml b/test/testinput/instrumentTests/Surface_marine_obs/sfcMarine_gfs_HofX.yaml new file mode 100644 index 000000000..7eceb8158 --- /dev/null +++ b/test/testinput/instrumentTests/Surface_marine_obs/sfcMarine_gfs_HofX.yaml @@ -0,0 +1,20 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: SfcPCorrected + da_psfc_scheme: UKMO + geovar_geomz: geopotential_height + geovar_sfc_geomz: surface_geopotential_height + obs space: + name: SfcPCorrected + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/sfcship_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/sfcship_obs_2020110112_out.nc4 + simulated variables: [surface_pressure] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/sfcship_geoval_2020110112_m.nc4 + rms ref: 99819.83844 + tolerance: 1.e-07 diff --git a/test/testinput/instrumentTests/Surface_marine_obs/sfcMarine_gfs_qc.yaml b/test/testinput/instrumentTests/Surface_marine_obs/sfcMarine_gfs_qc.yaml new file mode 100644 index 000000000..0ffcc63e5 --- /dev/null +++ b/test/testinput/instrumentTests/Surface_marine_obs/sfcMarine_gfs_qc.yaml @@ -0,0 +1,136 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs space: + name: surface_pressure_QC + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/sfcship_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/sfcship_obs_2020120112_out.nc + simulated variables: [surface_pressure, air_temperature, specific_humidity] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/sfcship_geoval_2020110112_m.nc4 + obs filters: +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: air_temperature + minvalue: 195 + maxvalue: 327 + action: + name: reject +# + - filter: Bounds Check + filter variables: + - name: surface_pressure + minvalue: 37499 + maxvalue: 106999 + action: + name: reject +# + - filter: Bounds Check + filter variables: + - name: specific_humidity + minvalue: 1.0E-7 + maxvalue: 0.034999999 + action: + name: reject +# Reject all obs with PreQC mark already set above 3 + - filter: PreQC + maxvalue: 3 + action: + name: reject +# Assign obsError, first temperature + - filter: BlackList + filter variables: + - name: air_temperature + action: + name: assign error + error parameter: 2.5 +# Assign obsError, next moisture + - filter: BlackList + filter variables: + - name: specific_humidity + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: surface_pressure@ObsValue + xvals: [110000, 10] + errors: [.2, .2] + scale_factor_var: specific_humidity@ObsValue +# Assign obsError, last surface pressure + - filter: BlackList + filter variables: + - name: surface_pressure + action: + name: assign error + error parameter: 130 # 130 Pa (1.3mb) +# +# Inflate ObsError as it is done with GSI + - filter: BlackList + filter variables: + - name: surface_pressure + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSfcPressure@ObsFunction + options: + error_min: 100 # 1 mb + error_max: 300 # 3 mb + geovar_sfc_geomz: surface_geopotential_height +# + - filter: Background Check + filter variables: + - name: surface_pressure + absolute threshold: 500.0 # 5 mb + test_hofx: GsiHofX # Remove this line when running UFO H(x) + action: + name: inflate error + inflation factor: 3.0 + defer to post: true +# + - filter: Background Check + filter variables: + - name: air_temperature + absolute threshold: 4.0 + test_hofx: GsiHofX # Remove this line when running UFO H(x) + action: + name: inflate error + inflation factor: 3.0 + defer to post: true +# + - filter: Bounds Check + filter variables: + - name: air_temperature + action: + name: reject + maxvalue: 4.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: air_temperature@ObsErrorData # After inflation step + denominator: + name: air_temperature@ObsError + defer to post: true +# + - filter: Bounds Check + filter variables: + - name: surface_pressure + action: + name: reject + maxvalue: 4.0 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: surface_pressure@ObsErrorData # After inflation step + denominator: + name: surface_pressure@ObsError + defer to post: true +# + passedBenchmark: 71 diff --git a/test/testinput/instrumentTests/airs/CMakeLists.txt b/test/testinput/instrumentTests/airs/CMakeLists.txt new file mode 100644 index 000000000..309a706c3 --- /dev/null +++ b/test/testinput/instrumentTests/airs/CMakeLists.txt @@ -0,0 +1,45 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for AIRS + + +if( crtm_FOUND ) + # H(x) test + ufo_add_test( NAME instrument_airs_aqua_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/airs_aqua_gfs_HofX.yaml" + MPI 1 + LABELS airs aqua HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with Bias Correction (bc) + ufo_add_test( NAME instrument_airs_aqua_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/airs_aqua_gfs_HofX_bc.yaml" + MPI 1 + LABELS airs aqua bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with QC + ufo_add_test( NAME instrument_airs_aqua_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/airs_aqua_gfs_HofX_qc.yaml" + MPI 1 + LABELS airs aqua qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) +endif( crtm_FOUND ) diff --git a/test/testinput/instrumentTests/airs/airs_aqua_gfs_HofX.yaml b/test/testinput/instrumentTests/airs/airs_aqua_gfs_HofX.yaml new file mode 100644 index 000000000..8126cdc78 --- /dev/null +++ b/test/testinput/instrumentTests/airs/airs_aqua_gfs_HofX.yaml @@ -0,0 +1,43 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: airs_aqua + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: airs_aqua + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/airs_aqua_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1, 6, 7, 10, 11, 15, 16, 17, 20, 21, 22, 24, + 27, 28, 30, 36, 39, 40, 42, 51, 52, 54, 55, 56, 59, 62, 63, 68, 69, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 82, 83, 84, 86, 92, 93, 98, 99, 101, + 104, 105, 108, 110, 111, 113, 116, 117, 123, 124, 128, 129, 138, 139, + 144, 145, 150, 151, 156, 157, 159, 162, 165, 168, 169, 170, 172, 173, + 174, 175, 177, 179, 180, 182, 185, 186, 190, 192, 198, 201, 204, 207, + 210, 215, 216, 221, 226, 227, 232, 252, 253, 256, 257, 261, 262, 267, + 272, 295, 299, 300, 305, 310, 321, 325, 333, 338, 355, 362, 375, 453, + 475, 484, 497, 528, 587, 672, 787, 791, 843, 870, 914, 950, 1003, 1012, + 1019, 1024, 1030, 1038, 1048, 1069, 1079, 1082, 1083, 1088, 1090, 1092, + 1095, 1104, 1111, 1115, 1116, 1119, 1120, 1123, 1130, 1138, 1142, 1178, + 1199, 1206, 1221, 1237, 1252, 1260, 1263, 1266, 1285, 1301, 1304, 1329, + 1371, 1382, 1415, 1424, 1449, 1455, 1466, 1477, 1500, 1519, 1538, 1545, + 1565, 1574, 1583, 1593, 1614, 1627, 1636, 1644, 1652, 1669, 1674, 1681, + 1694, 1708, 1717, 1723, 1740, 1748, 1751, 1756, 1763, 1766, 1771, 1777, + 1780, 1783, 1794, 1800, 1803, 1806, 1812, 1826, 1843, 1852, 1865, 1866, + 1868, 1869, 1872, 1873, 1876, 1881, 1882, 1883, 1911, 1917, 1918, 1924, + 1928, 1937, 1941, 2099, 2100, 2101, 2103, 2104, 2106, 2107, 2108, 2109, + 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, + 2122, 2123, 2128, 2134, 2141, 2145, 2149, 2153, 2164, 2189, 2197, 2209, + 2226, 2234, 2280, 2318, 2321, 2325, 2328, 2333, 2339, 2348, 2353, 2355, + 2357, 2363, 2370, 2371, 2377 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/airs_aqua_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/airs_crtm_bc.yaml b/test/testinput/instrumentTests/airs/airs_aqua_gfs_HofX_bc.yaml similarity index 66% rename from test/testinput/airs_crtm_bc.yaml rename to test/testinput/instrumentTests/airs/airs_aqua_gfs_HofX_bc.yaml index cf5239703..c67b72b8e 100644 --- a/test/testinput/airs_crtm_bc.yaml +++ b/test/testinput/instrumentTests/airs/airs_aqua_gfs_HofX_bc.yaml @@ -1,25 +1,20 @@ -window begin: 2018-04-14T21:00:00Z -window end: 2018-04-15T03:00:00Z +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] - linear obs operator: - Absorbers: [H2O,O3,CO2] obs options: - inspectProfile: 1 Sensor_ID: &Sensor_ID airs_aqua EndianType: little_endian CoefficientPath: Data/ obs space: name: airs_aqua obsdatain: - obsfile: Data/ioda/testinput_tier_1/airs_aqua_obs_2018041500_m_unittest.nc4 -# obsdataout: -# obsfile: Data/airs_aqua_obs_2018041500_m_unittest_out.nc4 + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/airs_aqua_obs_2020110112_m.nc4 simulated variables: [brightness_temperature] - channels: &channels + channels: &all_channels 1, 6, 7, 10, 11, 15, 16, 17, 20, 21, 22, 24, 27, 28, 30, 36, 39, 40, 42, 51, 52, 54, 55, 56, 59, 62, 63, 68, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 82, 83, 84, 86, 92, 93, 98, 99, 101, @@ -43,45 +38,29 @@ observations: 2226, 2234, 2280, 2318, 2321, 2325, 2328, 2333, 2339, 2348, 2353, 2355, 2357, 2363, 2370, 2371, 2377 geovals: - filename: Data/ufo/testinput_tier_1/airs_aqua_geoval_2018041500_m_unittest.nc4 + filename: Data/ufo/testinput_tier_1/instruments/radiance/airs_aqua_geoval_2020110112_m.nc4 obs bias: - name: LinearCombination - abias_in: Data/ufo/testinput_tier_1/satbias_crtm_in - abias_out: Data/ufo/testinput_tier_1/satbias_crtm_out - sensor: *Sensor_ID - jobs: *channels - predictors: - - predictor: - name: constant - - predictor: - name: lapse_rate + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_airs_aqua.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate options: order: 2 - tlapse: &airs_aqua_tlap Data/ufo/testinput_tier_1/airs_aqua_tlapmean.txt - - predictor: - name: lapse_rate + tlapse: &airs_aqua_tlap Data/ufo/testinput_tier_1/instruments/radiance/airs_aqua_tlapmean.txt + - name: lapse_rate options: tlapse: *airs_aqua_tlap - - predictor: - name: emissivity - - predictor: - name: scan_angle + - name: emissivity + - name: scan_angle options: order: 4 - - predictor: - name: scan_angle + - name: scan_angle options: order: 3 - - predictor: - name: scan_angle + - name: scan_angle options: order: 2 - - predictor: - name: scan_angle + - name: scan_angle vector ref: GsiHofXBc - tolerance: 1.e-8 - linear obs operator test: - iterations TL: 2 - coef TL: 1.e-3 - tolerance TL: 1.0e-3 - tolerance AD: 1.0e-11 + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/airs/airs_aqua_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/airs/airs_aqua_gfs_HofX_qc.yaml new file mode 100644 index 000000000..8c97ff2f2 --- /dev/null +++ b/test/testinput/instrumentTests/airs/airs_aqua_gfs_HofX_qc.yaml @@ -0,0 +1,349 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID airs_aqua + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: airs_aqua + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/airs_aqua_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 1, 6, 7, 10, 11, 15, 16, 17, 20, 21, 22, 24, + 27, 28, 30, 36, 39, 40, 42, 51, 52, 54, 55, 56, 59, 62, 63, 68, 69, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 82, 83, 84, 86, 92, 93, 98, 99, 101, + 104, 105, 108, 110, 111, 113, 116, 117, 123, 124, 128, 129, 138, 139, + 144, 145, 150, 151, 156, 157, 159, 162, 165, 168, 169, 170, 172, 173, + 174, 175, 177, 179, 180, 182, 185, 186, 190, 192, 198, 201, 204, 207, + 210, 215, 216, 221, 226, 227, 232, 252, 253, 256, 257, 261, 262, 267, + 272, 295, 299, 300, 305, 310, 321, 325, 333, 338, 355, 362, 375, 453, + 475, 484, 497, 528, 587, 672, 787, 791, 843, 870, 914, 950, 1003, 1012, + 1019, 1024, 1030, 1038, 1048, 1069, 1079, 1082, 1083, 1088, 1090, 1092, + 1095, 1104, 1111, 1115, 1116, 1119, 1120, 1123, 1130, 1138, 1142, 1178, + 1199, 1206, 1221, 1237, 1252, 1260, 1263, 1266, 1285, 1301, 1304, 1329, + 1371, 1382, 1415, 1424, 1449, 1455, 1466, 1477, 1500, 1519, 1538, 1545, + 1565, 1574, 1583, 1593, 1614, 1627, 1636, 1644, 1652, 1669, 1674, 1681, + 1694, 1708, 1717, 1723, 1740, 1748, 1751, 1756, 1763, 1766, 1771, 1777, + 1780, 1783, 1794, 1800, 1803, 1806, 1812, 1826, 1843, 1852, 1865, 1866, + 1868, 1869, 1872, 1873, 1876, 1881, 1882, 1883, 1911, 1917, 1918, 1924, + 1928, 1937, 1941, 2099, 2100, 2101, 2103, 2104, 2106, 2107, 2108, 2109, + 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, + 2122, 2123, 2128, 2134, 2141, 2145, 2149, 2153, 2164, 2189, 2197, 2209, + 2226, 2234, 2280, 2318, 2321, 2325, 2328, 2333, 2339, 2348, 2353, 2355, + 2357, 2363, 2370, 2371, 2377 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/airs_aqua_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_airs_aqua.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &airs_aqua_tlap Data/ufo/testinput_tier_1/instruments/radiance/airs_aqua_tlapmean.txt + - name: lapse_rate + options: + tlapse: *airs_aqua_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: +# Wavenumber Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 2122, 2123, 2128, 2134, 2141, 2145, 2149, 2153, 2164, 2189, 2197, 2209, + 2226, 2234, 2280, 2318, 2321, 2325, 2328, 2333, 2339, 2348, 2353, 2355, + 2357, 2363, 2370, 2371, 2377 + where: + - variable: + name: solar_zenith_angle@MetaData + maxvalue: 88.9999 + - variable: + name: water_area_fraction@GeoVaLs + minvalue: 1.0e-12 + action: + name: reject + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorWavenumIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + minvalue: 50.00001 + maxvalue: 449.99999 + action: + name: reject +# Topography Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID +# Transmittance Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Cloud Detection Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: CloudDetectMinResidualIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, + 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, + 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, + 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + use_flag_clddet: [ -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, + 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, + 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, + 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] + maxvalue: 1.0e-12 + action: + name: reject +# NSST Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: NearSSTRetCheckIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, + 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, + 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, + 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] + maxvalue: 1.0e-12 + action: + name: reject +# Surface Jacobians Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_max: [ 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, + 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, + 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, + 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, + 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, + 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, + 3.5, 3.5, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, 3.5, 3.5, 3.5, 3.0, 3.0, 3.5, + 3.5, 3.0, 3.0, 3.0, 3.5, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.5, 3.0, 3.5, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.5, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.5, 3.5, 3.5, 3.5, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0, 3.5, 3.0, 3.5, 3.0, 3.0, 3.0, 3.0, 3.5, 3.0, + 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, + 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, + 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, + 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, + 4.5, 4.5, 4.5, 4.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, + 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 3.5, + 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0 ] + action: + name: reject +# Useflag Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ -1, -1, 1, -1, -1, 1, -1, -1, 1, 1, + 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, + 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, -1, 1, 1, 1, -1, -1, + 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 805 diff --git a/test/testinput/instrumentTests/amsua/CMakeLists.txt b/test/testinput/instrumentTests/amsua/CMakeLists.txt new file mode 100644 index 000000000..2ceb1a423 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/CMakeLists.txt @@ -0,0 +1,243 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for AMSU-A + + +if( crtm_FOUND ) + # H(x) test + ufo_add_test( NAME instrument_amsua_aqua_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_aqua_gfs_HofX.yaml" + MPI 1 + LABELS amsua aqua HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_metop-a_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_metop-a_gfs_HofX.yaml" + MPI 1 + LABELS amsua metop-a HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_metop-b_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_metop-b_gfs_HofX.yaml" + MPI 1 + LABELS amsua metop-b HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_metop-c_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_metop-c_gfs_HofX.yaml" + MPI 1 + LABELS amsua metop-c HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_n15_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_n15_gfs_HofX.yaml" + MPI 1 + LABELS amsua n15 HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_n18_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_n18_gfs_HofX.yaml" + MPI 1 + LABELS amsua n18 HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_n19_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_n19_gfs_HofX.yaml" + MPI 1 + LABELS amsua n19 HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with Bias Correction (bc) + ufo_add_test( NAME instrument_amsua_aqua_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_aqua_gfs_HofX_bc.yaml" + MPI 1 + LABELS amsua aqua bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_metop-a_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_metop-a_gfs_HofX_bc.yaml" + MPI 1 + LABELS amsua metop-a bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_metop-b_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_metop-b_gfs_HofX_bc.yaml" + MPI 1 + LABELS amsua metop-b bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_metop-c_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_metop-c_gfs_HofX_bc.yaml" + MPI 1 + LABELS amsua metop-c bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_n15_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_n15_gfs_HofX_bc.yaml" + MPI 1 + LABELS amsua n15 bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + +# ufo_add_test( NAME instrument_amsua_n18_gfs_HofX_bc +# TIER 2 +# ECBUILD +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_n18_gfs_HofX_bc.yaml" +# MPI 1 +# LABELS amsua n18 bc instrument +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_n19_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_n19_gfs_HofX_bc.yaml" + MPI 1 + LABELS amsua n19 bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with QC +# ufo_add_test( NAME instrument_amsua_aqua_gfs_HofX_qc +# TIER 2 +# ECBUILD +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_aqua_gfs_HofX_qc.yaml" +# MPI 1 +# LABELS amsua aqua qc instrument +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_metop-a_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_metop-a_gfs_HofX_qc.yaml" + MPI 1 + LABELS amsua metop-a qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + +# ufo_add_test( NAME instrument_amsua_metop-b_gfs_HofX_qc +# TIER 2 +# ECBUILD +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_metop-b_gfs_HofX_qc.yaml" +# MPI 1 +# LABELS amsua metop-b qc instrument +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_metop-c_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_metop-c_gfs_HofX_qc.yaml" + MPI 1 + LABELS amsua metop-c qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_n15_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_n15_gfs_HofX_qc.yaml" + MPI 1 + LABELS amsua n15 qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_n18_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_n18_gfs_HofX_qc.yaml" + MPI 1 + LABELS amsua n18 qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_amsua_n19_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/amsua_n19_gfs_HofX_qc.yaml" + MPI 1 + LABELS amsua n19 qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) +endif( crtm_FOUND ) diff --git a/test/testinput/instrumentTests/amsua/amsua_aqua_gfs_HofX.yaml b/test/testinput/instrumentTests/amsua/amsua_aqua_gfs_HofX.yaml new file mode 100644 index 000000000..8a3d516e5 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_aqua_gfs_HofX.yaml @@ -0,0 +1,25 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + inspectProfile: 1 + Sensor_ID: amsua_aqua + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_aqua + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_aqua_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_aqua_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/instrumentTests/amsua/amsua_aqua_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/amsua/amsua_aqua_gfs_HofX_bc.yaml new file mode 100644 index 000000000..8157aa836 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_aqua_gfs_HofX_bc.yaml @@ -0,0 +1,50 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_aqua + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_aqua + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_aqua_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_aqua_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_aqua.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_aqua_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_aqua_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_aqua_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/amsua/amsua_aqua_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/amsua/amsua_aqua_gfs_HofX_qc.yaml new file mode 100644 index 000000000..1ad30391d --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_aqua_gfs_HofX_qc.yaml @@ -0,0 +1,339 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_aqua + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_aqua + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_aqua_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/amsua_aqua_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_aqua_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_aqua.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_aqua_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_aqua_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_aqua_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.000, 2.000, 0.500, 0.400, + 0.400, 0.500, 0.300, 0.350, 0.350, + 0.450, 1.000, 1.500, 2.500, 2.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.550, + 0.400, 0.500, 0.300, 0.350, 0.350, + 0.450, 1.000, 1.500, 2.500, 18.000] +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + maxvalue: 999.0 + action: + name: reject +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject +# Hydrometeor Check (cloud/precipitation affected chanels) + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: HydrometeorCheckAMSUA@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_clearsky: [ 2.500, 2.000, 2.000, 0.500, 0.400, + 0.400, 0.500, 0.300, 0.350, 0.350, + 0.450, 1.000, 1.500, 2.500, 2.500] + clwret_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.000, 2.000, 0.500, 0.400, + 0.400, 0.500, 0.300, 0.350, 0.350, + 0.450, 1.000, 1.500, 2.500, 2.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.550, + 0.400, 0.500, 0.300, 0.350, 0.350, + 0.450, 1.000, 1.500, 2.500, 18.000] + maxvalue: 0.0 + action: + name: reject +# Topography check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels +# Transmittnace Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Surface Jacobian check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.010, 0.020, 0.015, 0.020, 0.200] + obserr_dtempf: [0.500, 2.000, 1.000, 2.000, 4.500] +# Situation dependent Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSituDependMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + scatobs_function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 15 + scatret_types: [ObsValue] + bias_application: HofX + clwmatchidx_function: + name: CLWMatchIndexMW@ObsFunction + channels: *all_channels + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + clwret_clearsky: [0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + obserr_clearsky: [2.500, 2.000, 2.000, 0.500, 0.400, + 0.400, 0.500, 0.300, 0.350, 0.350, + 0.450, 1.000, 1.500, 2.500, 2.500] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.25, 0.04, 3.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_topo: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + bias_application: HofX + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.000, 2.000, 0.500, 0.400, + 0.400, 0.500, 0.300, 0.350, 0.350, + 0.450, 1.000, 1.500, 2.500, 2.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.550, + 0.400, 0.500, 0.300, 0.350, 0.350, + 0.450, 1.000, 1.500, 2.500, 18.000] + obserr_bound_max: [4.5, 4.5, 4.5, 3.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, + 3.0, 3.5, 4.5, 4.5, 4.5] + action: + name: reject +# Inter-channel check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: InterChannelConsistencyCheck@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + use_flag: [ -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, + 1, 1, 1, -1, -1 ] + maxvalue: 1.0e-12 + action: + name: reject +# Useflag check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, + 1, 1, 1, -1, -1 ] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 60 diff --git a/test/testinput/instrumentTests/amsua/amsua_metop-a_gfs_HofX.yaml b/test/testinput/instrumentTests/amsua/amsua_metop-a_gfs_HofX.yaml new file mode 100644 index 000000000..b14260d60 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_metop-a_gfs_HofX.yaml @@ -0,0 +1,25 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + inspectProfile: 1 + Sensor_ID: amsua_metop-a + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-a_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-a_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/instrumentTests/amsua/amsua_metop-a_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/amsua/amsua_metop-a_gfs_HofX_bc.yaml new file mode 100644 index 000000000..984b4e46c --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_metop-a_gfs_HofX_bc.yaml @@ -0,0 +1,50 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_metop-a + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-a_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-a_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_metop-a.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_metop-a_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-a_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_metop-a_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/amsua/amsua_metop-a_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/amsua/amsua_metop-a_gfs_HofX_qc.yaml new file mode 100644 index 000000000..cf17e7507 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_metop-a_gfs_HofX_qc.yaml @@ -0,0 +1,339 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_metop-a + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-a_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/amsua_metop-a_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-a_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_metop-a.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_metop-a_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-a_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_metop-a_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + maxvalue: 999.0 + action: + name: reject +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject +# Hydrometeor Check (cloud/precipitation affected chanels) + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: HydrometeorCheckAMSUA@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_clearsky: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + clwret_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + maxvalue: 0.0 + action: + name: reject +# Topography check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels +# Transmittnace Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Surface Jacobian check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.010, 0.020, 0.015, 0.020, 0.200] + obserr_dtempf: [0.500, 2.000, 1.000, 2.000, 4.500] +# Situation dependent Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSituDependMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + scatobs_function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 15 + scatret_types: [ObsValue] + bias_application: HofX + clwmatchidx_function: + name: CLWMatchIndexMW@ObsFunction + channels: *all_channels + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + clwret_clearsky: [0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + obserr_clearsky: [2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.25, 0.04, 3.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_topo: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + bias_application: HofX + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + obserr_bound_max: [4.5, 4.5, 4.5, 2.5, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, + 2.5, 3.5, 4.5, 4.5, 4.5] + action: + name: reject +# Inter-channel check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: InterChannelConsistencyCheck@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + use_flag: [ 1, 1, 1, 1, 1, + 1, -1, -1, 1, 1, + 1, 1, 1, -1, 1 ] + maxvalue: 1.0e-12 + action: + name: reject +# Useflag check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1, 1, 1, + 1, -1, -1, 1, 1, + 1, 1, 1, -1, 1 ] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 107 diff --git a/test/testinput/instrumentTests/amsua/amsua_metop-b_gfs_HofX.yaml b/test/testinput/instrumentTests/amsua/amsua_metop-b_gfs_HofX.yaml new file mode 100644 index 000000000..c402a3382 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_metop-b_gfs_HofX.yaml @@ -0,0 +1,25 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + inspectProfile: 1 + Sensor_ID: amsua_metop-b + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_metop-b + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-b_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-b_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/instrumentTests/amsua/amsua_metop-b_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/amsua/amsua_metop-b_gfs_HofX_bc.yaml new file mode 100644 index 000000000..acfbba0c2 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_metop-b_gfs_HofX_bc.yaml @@ -0,0 +1,50 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_metop-b + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_metop-b + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-b_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-b_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_metop-b.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_metop-b_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-b_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_metop-b_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/amsua/amsua_metop-b_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/amsua/amsua_metop-b_gfs_HofX_qc.yaml new file mode 100644 index 000000000..58c65d7dd --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_metop-b_gfs_HofX_qc.yaml @@ -0,0 +1,339 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_metop-b + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_metop-b + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-b_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/amsua_metop-b_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-b_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_metop-b.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_metop-b_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-b_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_metop-b_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + maxvalue: 999.0 + action: + name: reject +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject +# Hydrometeor Check (cloud/precipitation affected chanels) + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: HydrometeorCheckAMSUA@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_clearsky: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + clwret_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + maxvalue: 0.0 + action: + name: reject +# Topography check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels +# Transmittnace Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Surface Jacobian check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.010, 0.020, 0.015, 0.020, 0.200] + obserr_dtempf: [0.500, 2.000, 1.000, 2.000, 4.500] +# Situation dependent Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSituDependMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + scatobs_function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 15 + scatret_types: [ObsValue] + bias_application: HofX + clwmatchidx_function: + name: CLWMatchIndexMW@ObsFunction + channels: *all_channels + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + clwret_clearsky: [0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + obserr_clearsky: [2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.25, 0.04, 3.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_topo: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + bias_application: HofX + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + obserr_bound_max: [4.5, 4.5, 4.5, 2.5, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, + 2.5, 3.5, 4.5, 4.5, 4.5] + action: + name: reject +# Inter-channel check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: InterChannelConsistencyCheck@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + use_flag: [-1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, + 1, 1, 1, -1, -1] + maxvalue: 1.0e-12 + action: + name: reject +# Useflag check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [-1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, + 1, 1, 1, -1, -1] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 60 diff --git a/test/testinput/instrumentTests/amsua/amsua_metop-c_gfs_HofX.yaml b/test/testinput/instrumentTests/amsua/amsua_metop-c_gfs_HofX.yaml new file mode 100644 index 000000000..4e86ece9b --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_metop-c_gfs_HofX.yaml @@ -0,0 +1,25 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + inspectProfile: 1 + Sensor_ID: amsua_metop-c + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_metop-c + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-c_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-c_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/instrumentTests/amsua/amsua_metop-c_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/amsua/amsua_metop-c_gfs_HofX_bc.yaml new file mode 100644 index 000000000..c6e75f183 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_metop-c_gfs_HofX_bc.yaml @@ -0,0 +1,50 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_metop-c + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_metop-c + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-c_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-c_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_metop-c.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_metop-c_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-c_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_metop-c_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/amsua/amsua_metop-c_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/amsua/amsua_metop-c_gfs_HofX_qc.yaml new file mode 100644 index 000000000..1fddfcd08 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_metop-c_gfs_HofX_qc.yaml @@ -0,0 +1,339 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_metop-c + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_metop-c + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-c_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/amsua_metop-c_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-c_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_metop-c.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_metop-c_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_metop-c_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_metop-c_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + maxvalue: 999.0 + action: + name: reject +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject +# Hydrometeor Check (cloud/precipitation affected chanels) + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: HydrometeorCheckAMSUA@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_clearsky: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + clwret_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + maxvalue: 0.0 + action: + name: reject +# Topography check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels +# Transmittnace Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Surface Jacobian check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.010, 0.020, 0.015, 0.020, 0.200] + obserr_dtempf: [0.500, 2.000, 1.000, 2.000, 4.500] +# Situation dependent Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSituDependMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + scatobs_function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 15 + scatret_types: [ObsValue] + bias_application: HofX + clwmatchidx_function: + name: CLWMatchIndexMW@ObsFunction + channels: *all_channels + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + clwret_clearsky: [0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + obserr_clearsky: [2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.25, 0.04, 3.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_topo: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + bias_application: HofX + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + obserr_bound_max: [4.5, 4.5, 4.5, 2.5, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, + 2.5, 3.5, 4.5, 4.5, 4.5] + action: + name: reject +# Inter-channel check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: InterChannelConsistencyCheck@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + use_flag: [-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1] + maxvalue: 1.0e-12 + action: + name: reject +# Useflag check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 0 diff --git a/test/testinput/instrumentTests/amsua/amsua_n15_gfs_HofX.yaml b/test/testinput/instrumentTests/amsua/amsua_n15_gfs_HofX.yaml new file mode 100644 index 000000000..5d830fb62 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_n15_gfs_HofX.yaml @@ -0,0 +1,25 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + inspectProfile: 1 + Sensor_ID: amsua_n15 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_n15 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n15_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n15_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/instrumentTests/amsua/amsua_n15_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/amsua/amsua_n15_gfs_HofX_bc.yaml new file mode 100644 index 000000000..c133087ec --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_n15_gfs_HofX_bc.yaml @@ -0,0 +1,50 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_n15 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_n15 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n15_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n15_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_n15.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_n15_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_n15_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_n15_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/amsua/amsua_n15_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/amsua/amsua_n15_gfs_HofX_qc.yaml new file mode 100644 index 000000000..ee3643f40 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_n15_gfs_HofX_qc.yaml @@ -0,0 +1,339 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_n15 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_n15 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n15_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/amsua_n15_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n15_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_n15.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_n15_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_n15_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_n15_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 3.000, 2.200, 2.000, 0.600, 0.300, + 0.230, 0.250, 0.275, 0.340, 0.400, + 0.600, 1.000, 1.500, 2.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.250, 0.275, 0.340, 0.400, + 0.600, 1.000, 1.500, 2.000, 18.000] +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + maxvalue: 999.0 + action: + name: reject +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject +# Hydrometeor Check (cloud/precipitation affected chanels) + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: HydrometeorCheckAMSUA@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_clearsky: [ 3.000, 2.200, 2.000, 0.600, 0.300, + 0.230, 0.250, 0.275, 0.340, 0.400, + 0.600, 1.000, 1.500, 2.000, 3.500] + clwret_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 3.000, 2.200, 2.000, 0.600, 0.300, + 0.230, 0.250, 0.275, 0.340, 0.400, + 0.600, 1.000, 1.500, 2.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.250, 0.275, 0.340, 0.400, + 0.600, 1.000, 1.500, 2.000, 18.000] + maxvalue: 0.0 + action: + name: reject +# Topography check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels +# Transmittnace Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Surface Jacobian check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.010, 0.020, 0.015, 0.020, 0.200] + obserr_dtempf: [0.500, 2.000, 1.000, 2.000, 4.500] +# Situation dependent Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSituDependMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + scatobs_function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 15 + scatret_types: [ObsValue] + bias_application: HofX + clwmatchidx_function: + name: CLWMatchIndexMW@ObsFunction + channels: *all_channels + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + clwret_clearsky: [0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + obserr_clearsky: [3.000, 2.200, 2.000, 0.600, 0.300, + 0.230, 0.250, 0.275, 0.340, 0.400, + 0.600, 1.000, 1.500, 2.000, 3.500] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.25, 0.04, 3.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_topo: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + bias_application: HofX + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 3.000, 2.200, 2.000, 0.600, 0.300, + 0.230, 0.250, 0.275, 0.340, 0.400, + 0.600, 1.000, 1.500, 2.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.250, 0.275, 0.340, 0.400, + 0.600, 1.000, 1.500, 2.000, 18.000] + obserr_bound_max: [4.5, 4.5, 4.5, 2.5, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, + 2.5, 3.5, 4.5, 4.5, 4.5] + action: + name: reject +# Inter-channel check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: InterChannelConsistencyCheck@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + use_flag: [ 1, 1, 1, 1, 1, + -1, 1, 1, 1, 1, + -1, 1, 1, -1, 1 ] + maxvalue: 1.0e-12 + action: + name: reject +# Useflag check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1, 1, 1, + -1, 1, 1, 1, 1, + -1, 1, 1, -1, 1 ] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 90 diff --git a/test/testinput/instrumentTests/amsua/amsua_n18_gfs_HofX.yaml b/test/testinput/instrumentTests/amsua/amsua_n18_gfs_HofX.yaml new file mode 100644 index 000000000..a9f0272bd --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_n18_gfs_HofX.yaml @@ -0,0 +1,25 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + inspectProfile: 1 + Sensor_ID: amsua_n18 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_n18 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n18_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n18_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/instrumentTests/amsua/amsua_n18_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/amsua/amsua_n18_gfs_HofX_bc.yaml new file mode 100644 index 000000000..ec914e473 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_n18_gfs_HofX_bc.yaml @@ -0,0 +1,50 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_n18 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_n18 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n18_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n18_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_n18.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_n18_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_n18_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_n18_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/amsua/amsua_n18_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/amsua/amsua_n18_gfs_HofX_qc.yaml new file mode 100644 index 000000000..06194e396 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_n18_gfs_HofX_qc.yaml @@ -0,0 +1,339 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_n18 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_n18 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n18_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/amsua_n18_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n18_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_n18.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_n18_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_n18_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_n18_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + maxvalue: 999.0 + action: + name: reject +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject +# Hydrometeor Check (cloud/precipitation affected chanels) + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: HydrometeorCheckAMSUA@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_clearsky: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + clwret_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + maxvalue: 0.0 + action: + name: reject +# Topography check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels +# Transmittnace Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Surface Jacobian check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.010, 0.020, 0.015, 0.020, 0.200] + obserr_dtempf: [0.500, 2.000, 1.000, 2.000, 4.500] +# Situation dependent Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSituDependMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + scatobs_function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 15 + scatret_types: [ObsValue] + bias_application: HofX + clwmatchidx_function: + name: CLWMatchIndexMW@ObsFunction + channels: *all_channels + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + clwret_clearsky: [0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + obserr_clearsky: [2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.25, 0.04, 3.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_topo: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + bias_application: HofX + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + obserr_bound_max: [4.5, 4.5, 4.5, 2.5, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, + 2.5, 3.5, 4.5, 4.5, 4.5] + action: + name: reject +# Inter-channel check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: InterChannelConsistencyCheck@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + use_flag: [ 1, 1, 1, 1, -1, + 1, 1, -1, -1, 1, + 1, 1, 1, -1, 1 ] + maxvalue: 1.0e-12 + action: + name: reject +# Useflag check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1, 1, -1, + 1, 1, -1, -1, 1, + 1, 1, 1, -1, 1 ] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 81 diff --git a/test/testinput/instrumentTests/amsua/amsua_n19_gfs_HofX.yaml b/test/testinput/instrumentTests/amsua/amsua_n19_gfs_HofX.yaml new file mode 100644 index 000000000..411cd710c --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_n19_gfs_HofX.yaml @@ -0,0 +1,25 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + inspectProfile: 1 + Sensor_ID: amsua_n19 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n19_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n19_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/instrumentTests/amsua/amsua_n19_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/amsua/amsua_n19_gfs_HofX_bc.yaml new file mode 100644 index 000000000..6e10b4bd8 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_n19_gfs_HofX_bc.yaml @@ -0,0 +1,50 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_n19 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n19_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n19_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_n19.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_n19_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_n19_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_n19_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/amsua/amsua_n19_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/amsua/amsua_n19_gfs_HofX_qc.yaml new file mode 100644 index 000000000..11b5353e6 --- /dev/null +++ b/test/testinput/instrumentTests/amsua/amsua_n19_gfs_HofX_qc.yaml @@ -0,0 +1,339 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID amsua_n19 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: amsua_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n19_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/amsua_n19_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-15 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/amsua_n19_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_amsua_n19.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &amsua_n19_tlap Data/ufo/testinput_tier_1/instruments/radiance/amsua_n19_tlapmean.txt + - name: lapse_rate + options: + tlapse: *amsua_n19_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + maxvalue: 999.0 + action: + name: reject +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-6, 15 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject +# Hydrometeor Check (cloud/precipitation affected chanels) + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: HydrometeorCheckAMSUA@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_clearsky: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + clwret_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + maxvalue: 0.0 + action: + name: reject +# Topography check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels +# Transmittnace Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Surface Jacobian check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.010, 0.020, 0.015, 0.020, 0.200] + obserr_dtempf: [0.500, 2.000, 1.000, 2.000, 4.500] +# Situation dependent Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSituDependMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + scatobs_function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 15 + scatret_types: [ObsValue] + bias_application: HofX + clwmatchidx_function: + name: CLWMatchIndexMW@ObsFunction + channels: *all_channels + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + bias_application: HofX + clwret_clearsky: [0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + obserr_clearsky: [2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.25, 0.04, 3.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_topo: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + bias_application: HofX + x0: [ 0.050, 0.030, 0.030, 0.020, 0.000, + 0.100, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.030] + x1: [ 0.600, 0.450, 0.400, 0.450, 1.000, + 1.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.200] + err0: [ 2.500, 2.200, 2.000, 0.550, 0.300, + 0.230, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 3.500] + err1: [20.000, 18.000, 12.000, 3.000, 0.500, + 0.300, 0.230, 0.250, 0.250, 0.350, + 0.400, 0.550, 0.800, 3.000, 18.000] + obserr_bound_max: [4.5, 4.5, 4.5, 2.5, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, + 2.5, 3.5, 4.5, 4.5, 4.5] + action: + name: reject +# Inter-channel check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: InterChannelConsistencyCheck@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + use_flag: [ 1, 1, 1, 1, 1, + 1, -1, -1, 1, 1, + 1, 1, 1, -1, 1 ] + maxvalue: 1.0e-12 + action: + name: reject +# Useflag check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1, 1, 1, + 1, -1, -1, 1, 1, + 1, 1, 1, -1, 1 ] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 76 diff --git a/test/testinput/instrumentTests/atms/CMakeLists.txt b/test/testinput/instrumentTests/atms/CMakeLists.txt new file mode 100644 index 000000000..44a3ee088 --- /dev/null +++ b/test/testinput/instrumentTests/atms/CMakeLists.txt @@ -0,0 +1,78 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for ATMS + + +if( crtm_FOUND ) + # H(x) test + ufo_add_test( NAME instrument_atms_n20_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/atms_n20_gfs_HofX.yaml" + MPI 1 + LABELS atms n20 HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_atms_npp_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/atms_npp_gfs_HofX.yaml" + MPI 1 + LABELS atms npp HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with Bias Correction (bc) + ufo_add_test( NAME instrument_atms_n20_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/atms_n20_gfs_HofX_bc.yaml" + MPI 1 + LABELS atms n20 bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_atms_npp_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/atms_npp_gfs_HofX_bc.yaml" + MPI 1 + LABELS atms npp bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with QC + ufo_add_test( NAME instrument_atms_n20_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/atms_n20_gfs_HofX_qc.yaml" + MPI 1 + LABELS atms n20 qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_atms_npp_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/atms_npp_gfs_HofX_qc.yaml" + MPI 1 + LABELS atms npp qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) +endif( crtm_FOUND ) diff --git a/test/testinput/instrumentTests/atms/atms_n20_gfs_HofX.yaml b/test/testinput/instrumentTests/atms/atms_n20_gfs_HofX.yaml new file mode 100644 index 000000000..bcfd8f1f7 --- /dev/null +++ b/test/testinput/instrumentTests/atms/atms_n20_gfs_HofX.yaml @@ -0,0 +1,24 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: atms_n20 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/atms_n20_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/atms_n20_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/instrumentTests/atms/atms_n20_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/atms/atms_n20_gfs_HofX_bc.yaml new file mode 100644 index 000000000..d57020507 --- /dev/null +++ b/test/testinput/instrumentTests/atms/atms_n20_gfs_HofX_bc.yaml @@ -0,0 +1,50 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID atms_n20 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/atms_n20_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/atms_n20_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_atms_n20.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &atms_n20_tlap Data/ufo/testinput_tier_1/instruments/radiance/atms_n20_tlapmean.txt + - name: lapse_rate + options: + tlapse: *atms_n20_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/atms/atms_n20_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/atms/atms_n20_gfs_HofX_qc.yaml new file mode 100644 index 000000000..a2d181c9a --- /dev/null +++ b/test/testinput/instrumentTests/atms/atms_n20_gfs_HofX_qc.yaml @@ -0,0 +1,371 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID atms_n20 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/atms_n20_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/atms_n20_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/atms_n20_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_atms_n20.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &atms_n20_tlap Data/ufo/testinput_tier_1/instruments/radiance/atms_n20_tlapmean.txt + - name: lapse_rate + options: + tlapse: *atms_n20_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + x1: [ 0.350, 0.380, 0.400, 0.450, 0.500, + 1.000, 1.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.350, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500] + err0: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + err1: [20.000, 25.000, 12.000, 7.000, 3.500, + 3.000, 0.800, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 19.000, 30.000, 25.000, 16.500, 12.000, + 9.000, 6.500] +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-7, 16-22 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + maxvalue: 999.0 + action: + name: reject +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-7, 16-22 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject +# Hydrometeor Check (cloud/precipitation affected chanels) + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: HydrometeorCheckATMS@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_clearsky: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + clwret_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + x1: [ 0.350, 0.380, 0.400, 0.450, 0.500, + 1.000, 1.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.350, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500] + err0: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + err1: [20.000, 25.000, 12.000, 7.000, 3.500, + 3.000, 0.800, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 19.000, 30.000, 25.000, 16.500, 12.000, + 9.000, 6.500] + maxvalue: 0.0 + action: + name: reject +# Topography check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels +# Transmittnace Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Surface Jacobian check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.010, 0.020, 0.015, 0.020, 0.200] + obserr_dtempf: [0.500, 2.000, 1.000, 2.000, 4.500] +# Situation dependent Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSituDependMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + scatobs_function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 16 + scatret_types: [ObsValue] + clwmatchidx_function: + name: CLWMatchIndexMW@ObsFunction + channels: *all_channels + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + clwret_clearsky: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + obserr_clearsky: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.25, 0.04, 3.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_topo: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + x1: [ 0.350, 0.380, 0.400, 0.450, 0.500, + 1.000, 1.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.350, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500] + err0: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + err1: [20.000, 25.000, 12.000, 7.000, 3.500, + 3.000, 0.800, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 19.000, 30.000, 25.000, 16.500, 12.000, + 9.000, 6.500] + obserr_bound_max: [4.5, 4.5, 3.0, 3.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 2.0, 4.5, + 4.5, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0] + action: + name: reject +# Inter-channel check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: InterChannelConsistencyCheck@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + use_flag: [ 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, -1, + 1, 1, 1, 1, 1, + 1, 1] + maxvalue: 1.0e-12 + action: + name: reject +# Useflag check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, -1, + 1, 1, 1, 1, 1, + 1, 1] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 149 diff --git a/test/testinput/instrumentTests/atms/atms_npp_gfs_HofX.yaml b/test/testinput/instrumentTests/atms/atms_npp_gfs_HofX.yaml new file mode 100644 index 000000000..90b0c9fdf --- /dev/null +++ b/test/testinput/instrumentTests/atms/atms_npp_gfs_HofX.yaml @@ -0,0 +1,24 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: atms_npp + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/atms_npp_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/atms_npp_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/instrumentTests/atms/atms_npp_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/atms/atms_npp_gfs_HofX_bc.yaml new file mode 100644 index 000000000..d9baa9015 --- /dev/null +++ b/test/testinput/instrumentTests/atms/atms_npp_gfs_HofX_bc.yaml @@ -0,0 +1,50 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID atms_npp + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/atms_npp_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/atms_npp_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_atms_npp.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &atms_npp_tlap Data/ufo/testinput_tier_1/instruments/radiance/atms_npp_tlapmean.txt + - name: lapse_rate + options: + tlapse: *atms_npp_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/atms/atms_npp_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/atms/atms_npp_gfs_HofX_qc.yaml new file mode 100644 index 000000000..f308ed0a9 --- /dev/null +++ b/test/testinput/instrumentTests/atms/atms_npp_gfs_HofX_qc.yaml @@ -0,0 +1,371 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + Clouds: [Water, Ice] + Cloud_Fraction: 1.0 + obs options: + Sensor_ID: &Sensor_ID atms_npp + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: atms_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/atms_npp_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/atms_npp_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/atms_npp_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_atms_npp.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &atms_npp_tlap Data/ufo/testinput_tier_1/instruments/radiance/atms_npp_tlapmean.txt + - name: lapse_rate + options: + tlapse: *atms_npp_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: assign error + error function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + x1: [ 0.350, 0.380, 0.400, 0.450, 0.500, + 1.000, 1.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.350, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500] + err0: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + err1: [20.000, 25.000, 12.000, 7.000, 3.500, + 3.000, 0.800, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 19.000, 30.000, 25.000, 16.500, 12.000, + 9.000, 6.500] +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-7, 16-22 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + maxvalue: 999.0 + action: + name: reject +# CLW Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: 1-7, 16-22 + test variables: + - name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + maxvalue: 999.0 + action: + name: reject +# Hydrometeor Check (cloud/precipitation affected chanels) + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: HydrometeorCheckATMS@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_clearsky: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + clwret_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + x1: [ 0.350, 0.380, 0.400, 0.450, 0.500, + 1.000, 1.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.350, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500] + err0: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + err1: [20.000, 25.000, 12.000, 7.000, 3.500, + 3.000, 0.800, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 19.000, 30.000, 25.000, 16.500, 12.000, + 9.000, 6.500] + maxvalue: 0.0 + action: + name: reject +# Topography check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels +# Transmittnace Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Surface Jacobian check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.010, 0.020, 0.015, 0.020, 0.200] + obserr_dtempf: [0.500, 2.000, 1.000, 2.000, 4.500] +# Situation dependent Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSituDependMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + scatobs_function: + name: SCATRetMW@ObsFunction + options: + scatret_ch238: 1 + scatret_ch314: 2 + scatret_ch890: 16 + scatret_types: [ObsValue] + clwmatchidx_function: + name: CLWMatchIndexMW@ObsFunction + channels: *all_channels + options: + channels: *all_channels + clwobs_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue] + clwbkg_function: + name: CLWRetMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [HofX] + clwret_clearsky: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + obserr_clearsky: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundMW@ObsFunction + channels: *all_channels + options: + sensor: *Sensor_ID + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.25, 0.04, 3.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_topo: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + obserr_function: + name: ObsErrorModelRamp@ObsFunction + channels: *all_channels + options: + channels: *all_channels + xvar: + name: CLWRetSymmetricMW@ObsFunction + options: + clwret_ch238: 1 + clwret_ch314: 2 + clwret_types: [ObsValue, HofX] + x0: [ 0.030, 0.030, 0.030, 0.020, 0.030, + 0.080, 0.150, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.020, 0.030, 0.030, 0.030, 0.030, + 0.050, 0.100] + x1: [ 0.350, 0.380, 0.400, 0.450, 0.500, + 1.000, 1.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.350, 0.500, 0.500, 0.500, 0.500, + 0.500, 0.500] + err0: [ 4.500, 4.500, 4.500, 2.500, 0.550, + 0.300, 0.300, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 4.000, 4.000, 3.500, 3.000, 3.000, + 3.000, 3.000] + err1: [20.000, 25.000, 12.000, 7.000, 3.500, + 3.000, 0.800, 0.400, 0.400, 0.400, + 0.450, 0.450, 0.550, 0.800, 3.000, + 19.000, 30.000, 25.000, 16.500, 12.000, + 9.000, 6.500] + obserr_bound_max: [4.5, 4.5, 3.0, 3.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 2.0, 4.5, + 4.5, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0] + action: + name: reject +# Inter-channel check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: InterChannelConsistencyCheck@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID + use_flag: [ 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, -1, + 1, 1, 1, 1, 1, + 1, 1] + maxvalue: 1.0e-12 + action: + name: reject +# Useflag check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, -1, + 1, 1, 1, 1, 1, + 1, 1] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 142 diff --git a/test/testinput/instrumentTests/avhrr/CMakeLists.txt b/test/testinput/instrumentTests/avhrr/CMakeLists.txt new file mode 100644 index 000000000..6f7cff83a --- /dev/null +++ b/test/testinput/instrumentTests/avhrr/CMakeLists.txt @@ -0,0 +1,78 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for AVHRR + + +if( crtm_FOUND ) + # H(x) test + ufo_add_test( NAME instrument_avhrr_metop-a_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/avhrr_metop-a_gfs_HofX.yaml" + MPI 1 + LABELS avhrr metop-a HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_avhrr_n18_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/avhrr_n18_gfs_HofX.yaml" + MPI 1 + LABELS avhrr n18 HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with Bias Correction (bc) + ufo_add_test( NAME instrument_avhrr_metop-a_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/avhrr_metop-a_gfs_HofX_bc.yaml" + MPI 1 + LABELS avhrr metop-a HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_avhrr_n18_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/avhrr_n18_gfs_HofX_bc.yaml" + MPI 1 + LABELS avhrr n18 HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with QC + ufo_add_test( NAME instrument_avhrr_metop-a_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/avhrr_metop-a_gfs_HofX_qc.yaml" + MPI 1 + LABELS avhrr metop-a qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_avhrr_n18_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/avhrr_n18_gfs_HofX_qc.yaml" + MPI 1 + LABELS avhrr n18 qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) +endif( crtm_FOUND ) diff --git a/test/testinput/instrumentTests/avhrr/avhrr_metop-a_gfs_HofX.yaml b/test/testinput/instrumentTests/avhrr/avhrr_metop-a_gfs_HofX.yaml new file mode 100644 index 000000000..9f240c16c --- /dev/null +++ b/test/testinput/instrumentTests/avhrr/avhrr_metop-a_gfs_HofX.yaml @@ -0,0 +1,21 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: avhrr3_metop-a + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: avhrr3_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_metop-a_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 3,4,5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_metop-a_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-5 diff --git a/test/testinput/instrumentTests/avhrr/avhrr_metop-a_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/avhrr/avhrr_metop-a_gfs_HofX_bc.yaml new file mode 100644 index 000000000..44b3b0a6a --- /dev/null +++ b/test/testinput/instrumentTests/avhrr/avhrr_metop-a_gfs_HofX_bc.yaml @@ -0,0 +1,44 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID avhrr3_metop-a + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: avhrr3_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_metop-a_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 3,4,5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_metop-a_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_avhrr3_metop-a.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &avhrr_metop-a_tlap Data/ufo/testinput_tier_1/instruments/radiance/avhrr_metop-a_tlapmean.txt + - name: lapse_rate + options: + tlapse: *avhrr_metop-a_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-6 diff --git a/test/testinput/instrumentTests/avhrr/avhrr_metop-a_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/avhrr/avhrr_metop-a_gfs_HofX_qc.yaml new file mode 100644 index 000000000..bf2cfbb46 --- /dev/null +++ b/test/testinput/instrumentTests/avhrr/avhrr_metop-a_gfs_HofX_qc.yaml @@ -0,0 +1,187 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID avhrr3_metop-a + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: avhrr3_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_metop-a_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/avhrr_metop-a_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 3,4,5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_metop-a_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_avhrr3_metop-a.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &avhrr_metop-a_tlap Data/ufo/testinput_tier_1/instruments/radiance/avhrr_metop-a_tlapmean.txt + - name: lapse_rate + options: + tlapse: *avhrr_metop-a_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: +# Wavenumber Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 3 + where: + - variable: + name: solar_zenith_angle@MetaData + maxvalue: 88.9999 + - variable: + name: water_area_fraction@GeoVaLs + minvalue: 1.0e-12 + action: + name: reject + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorWavenumIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Topography Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + minvalue: 0.00001 + maxvalue: 1000.0 + action: + name: reject +# Transmittance Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Cloud Detection Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: CloudDetectMinResidualAVHRR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1 ] + use_flag_clddet: [ 1, 1, 1 ] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# NSST Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: NearSSTRetCheckIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1 ] + obserr_demisf: [0.01,0.02,0.03,0.02,0.03] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# Surface Jacobians Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_max: [ 6.0, 6.0, 6.0 ] + action: + name: reject +# Useflag Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1 ] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 9 diff --git a/test/testinput/instrumentTests/avhrr/avhrr_n18_gfs_HofX.yaml b/test/testinput/instrumentTests/avhrr/avhrr_n18_gfs_HofX.yaml new file mode 100644 index 000000000..cbd4d4eba --- /dev/null +++ b/test/testinput/instrumentTests/avhrr/avhrr_n18_gfs_HofX.yaml @@ -0,0 +1,21 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: avhrr3_n18 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: avhrr3_n18 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_n18_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 3,4,5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_n18_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-5 diff --git a/test/testinput/instrumentTests/avhrr/avhrr_n18_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/avhrr/avhrr_n18_gfs_HofX_bc.yaml new file mode 100644 index 000000000..3d88da8b1 --- /dev/null +++ b/test/testinput/instrumentTests/avhrr/avhrr_n18_gfs_HofX_bc.yaml @@ -0,0 +1,44 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID avhrr3_n18 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: avhrr3_n18 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_n18_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 3,4,5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_n18_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_avhrr3_n18.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &avhrr_n18_tlap Data/ufo/testinput_tier_1/instruments/radiance/avhrr_n18_tlapmean.txt + - name: lapse_rate + options: + tlapse: *avhrr_n18_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-6 diff --git a/test/testinput/instrumentTests/avhrr/avhrr_n18_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/avhrr/avhrr_n18_gfs_HofX_qc.yaml new file mode 100644 index 000000000..744606766 --- /dev/null +++ b/test/testinput/instrumentTests/avhrr/avhrr_n18_gfs_HofX_qc.yaml @@ -0,0 +1,187 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID avhrr3_n18 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: avhrr3_n18 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_n18_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/avhrr_n18_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 3,4,5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/avhrr_n18_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_avhrr3_n18.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &avhrr_n18_tlap Data/ufo/testinput_tier_1/instruments/radiance/avhrr_n18_tlapmean.txt + - name: lapse_rate + options: + tlapse: *avhrr_n18_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: +# Wavenumber Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 3 + where: + - variable: + name: solar_zenith_angle@MetaData + maxvalue: 88.9999 + - variable: + name: water_area_fraction@GeoVaLs + minvalue: 1.0e-12 + action: + name: reject + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorWavenumIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Topography Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + minvalue: 0.00001 + maxvalue: 1000.0 + action: + name: reject +# Transmittance Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Cloud Detection Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: CloudDetectMinResidualAVHRR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1 ] + use_flag_clddet: [ 1, 1, 1 ] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# NSST Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: NearSSTRetCheckIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1 ] + obserr_demisf: [0.01,0.02,0.03,0.02,0.03] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# Surface Jacobians Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_max: [ 6.0, 6.0, 6.0 ] + action: + name: reject +# Useflag Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, 1, 1 ] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 4 diff --git a/test/testinput/instrumentTests/cris/CMakeLists.txt b/test/testinput/instrumentTests/cris/CMakeLists.txt new file mode 100644 index 000000000..e74257225 --- /dev/null +++ b/test/testinput/instrumentTests/cris/CMakeLists.txt @@ -0,0 +1,77 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for CrIS + +if( crtm_FOUND ) + # H(x) test + ufo_add_test( NAME instrument_cris-fsr_n20_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/cris-fsr_n20_gfs_HofX.yaml" + MPI 1 + LABELS cris n20 HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_cris-fsr_npp_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/cris-fsr_npp_gfs_HofX.yaml" + MPI 1 + LABELS cris npp HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with Bias Correction (bc) + ufo_add_test( NAME instrument_cris-fsr_n20_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/cris-fsr_n20_gfs_HofX_bc.yaml" + MPI 1 + LABELS cris-fsr n20 bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_cris-fsr_npp_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/cris-fsr_npp_gfs_HofX_bc.yaml" + MPI 1 + LABELS cris-fsr npp bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with QC + ufo_add_test( NAME instrument_cris-fsr_n20_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/cris-fsr_n20_gfs_HofX_qc.yaml" + MPI 1 + LABELS cris-fsr n20 qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_cris-fsr_npp_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/cris-fsr_npp_gfs_HofX_qc.yaml" + MPI 1 + LABELS cris-fsr npp qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) +endif( crtm_FOUND ) diff --git a/test/testinput/instrumentTests/cris/cris-fsr_n20_gfs_HofX.yaml b/test/testinput/instrumentTests/cris/cris-fsr_n20_gfs_HofX.yaml new file mode 100644 index 000000000..74f52258e --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_n20_gfs_HofX.yaml @@ -0,0 +1,54 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: cris-fsr_n20 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: cris-fsr_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_n20_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_n20_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/instrumentTests/cris/cris-fsr_n20_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/cris/cris-fsr_n20_gfs_HofX_bc.yaml new file mode 100644 index 000000000..e6b60fa71 --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_n20_gfs_HofX_bc.yaml @@ -0,0 +1,76 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID cris-fsr_n20 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: cris-fsr_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_n20_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_n20_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_cris-fsr_n20.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &cris-fsr_n20_tlap Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_n20_tlapmean.txt + - name: lapse_rate + options: + tlapse: *cris-fsr_n20_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/cris/cris-fsr_n20_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/cris/cris-fsr_n20_gfs_HofX_qc.yaml new file mode 100644 index 000000000..c34653560 --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_n20_gfs_HofX_qc.yaml @@ -0,0 +1,451 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID cris-fsr_n20 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: cris-fsr_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_n20_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/cris-fsr_n20_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_n20_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_cris-fsr_n20.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &cris-fsr_n20_tlap Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_n20_tlapmean.txt + - name: lapse_rate + options: + tlapse: *cris-fsr_n20_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: +# Wavenumber Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, + 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, + 2153, 2158, 2161, 2168, 2171, 2175, 2182 + where: + - variable: + name: solar_zenith_angle@MetaData + maxvalue: 88.9999 + - variable: + name: water_area_fraction@GeoVaLs + minvalue: 1.0e-12 + action: + name: reject + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorWavenumIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + minvalue: 50.00001 + maxvalue: 449.99999 + action: + name: reject +# Topography Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID +# Transmittance Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Cloud Detection Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: CloudDetectMinResidualIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + test_bias: GsiObsBias + use_flag: [ -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, + 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1] + use_flag_clddet: [ -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# Surface Temperature Jacobian Check over Land + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + where: + - variable: + name: water_area_fraction@GeoVaLs + maxvalue: 0.99 + test variables: + - name: brightness_temperature_jacobian_surface_temperature@ObsDiag + channels: *all_channels + maxvalue: 0.2 +# NSST Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: NearSSTRetCheckIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + test_bias: GsiObsBias + use_flag: [ -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, + 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + obserr_demisf: [0.01,0.02,0.03,0.02,0.03] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# Surface Jacobians Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_max: [ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0 ] + action: + name: reject +# Useflag Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, + 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 344 diff --git a/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_HofX.yaml b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_HofX.yaml new file mode 100644 index 000000000..6360bb345 --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_HofX.yaml @@ -0,0 +1,54 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: cris-fsr_npp + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: cris-fsr_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 + diff --git a/test/testinput/cris_crtm_bc.yaml b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_HofX_bc.yaml similarity index 74% rename from test/testinput/cris_crtm_bc.yaml rename to test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_HofX_bc.yaml index 38103b8b0..3660f2c72 100644 --- a/test/testinput/cris_crtm_bc.yaml +++ b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_HofX_bc.yaml @@ -1,23 +1,20 @@ -window begin: 2018-04-14T21:00:00Z -window end: 2018-04-15T03:00:00Z +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] - linear obs operator: - Absorbers: [H2O,O3,CO2] obs options: - inspectProfile: 1 Sensor_ID: &Sensor_ID cris-fsr_npp EndianType: little_endian CoefficientPath: Data/ obs space: name: cris-fsr_npp obsdatain: - obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obs_2020110112_m.nc4 simulated variables: [brightness_temperature] - channels: &channels + channels: &all_channels 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, @@ -51,46 +48,29 @@ observations: 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, 2158, 2161, 2168, 2171, 2175, 2182 geovals: - filename: Data/ufo/testinput_tier_1/cris-fsr_npp_geoval_2018041500_m_unittest.nc4 + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_geoval_2020110112_m.nc4 obs bias: - name: LinearCombination - abias_in: Data/ufo/testinput_tier_1/satbias_crtm_in - abias_out: Data/ufo/testinput_tier_1/satbias_crtm_out - sensor: *Sensor_ID - jobs: *channels - predictors: - - predictor: - name: constant - - predictor: - name: lapse_rate + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_cris-fsr_npp.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate options: order: 2 - tlapse: &cris-fsr_npp_tlap Data/ufo/testinput_tier_1/cris-fsr_npp_tlapmean.txt - - predictor: - name: lapse_rate + tlapse: &cris-fsr_npp_tlap Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_tlapmean.txt + - name: lapse_rate options: tlapse: *cris-fsr_npp_tlap - - predictor: - name: emissivity - - predictor: - name: scan_angle + - name: emissivity + - name: scan_angle options: order: 4 - - predictor: - name: scan_angle + - name: scan_angle options: order: 3 - - predictor: - name: scan_angle + - name: scan_angle options: order: 2 - - predictor: - name: scan_angle + - name: scan_angle vector ref: GsiHofXBc - tolerance: 1.e-8 - - linear obs operator test: - iterations TL: 2 - coef TL: 1.e-3 - tolerance TL: 1.0e-3 - tolerance AD: 1.0e-11 + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_HofX_qc.yaml new file mode 100644 index 000000000..e3c230ab2 --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_HofX_qc.yaml @@ -0,0 +1,451 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID cris-fsr_npp + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: cris-fsr_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/cris-fsr_npp_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_cris-fsr_npp.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &cris-fsr_npp_tlap Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_tlapmean.txt + - name: lapse_rate + options: + tlapse: *cris-fsr_npp_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: +# Wavenumber Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, + 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, + 2153, 2158, 2161, 2168, 2171, 2175, 2182 + where: + - variable: + name: solar_zenith_angle@MetaData + maxvalue: 88.9999 + - variable: + name: water_area_fraction@GeoVaLs + minvalue: 1.0e-12 + action: + name: reject + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorWavenumIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + minvalue: 50.00001 + maxvalue: 449.99999 + action: + name: reject +# Topography Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID +# Transmittance Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Cloud Detection Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: CloudDetectMinResidualIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + test_bias: GsiObsBias + use_flag: [ -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, + 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1] + use_flag_clddet: [ -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# Surface Temperature Jacobian Check over Land + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + where: + - variable: + name: water_area_fraction@GeoVaLs + maxvalue: 0.99 + test variables: + - name: brightness_temperature_jacobian_surface_temperature@ObsDiag + channels: *all_channels + maxvalue: 0.2 +# NSST Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: NearSSTRetCheckIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + test_bias: GsiObsBias + use_flag: [ -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, + 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + obserr_demisf: [0.01,0.02,0.03,0.02,0.03] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# Surface Jacobians Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_max: [ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0 ] + action: + name: reject +# Useflag Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, + 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 441 diff --git a/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errfgrosschk.yaml b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errfgrosschk.yaml new file mode 100644 index 000000000..d0021cf0e --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errfgrosschk.yaml @@ -0,0 +1,110 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: cris-fsr_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorBoundIR@ObsFunction + options: + channels: *all_channels + test_qcflag: PreQC + test_obserr: GsiObsError_grosschk + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_max: [ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0 ] + variables: [error_inflation_factor_grosschk] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errfjsfc.yaml b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errfjsfc.yaml new file mode 100644 index 000000000..a883cccc7 --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errfjsfc.yaml @@ -0,0 +1,59 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: cris-fsr_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + options: + channels: *all_channels + obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] + test_qcflag: PreQC + test_obserr: GsiObsError_jsfc + variables: [error_inflation_factor_jsfc] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errflat.yaml b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errflat.yaml new file mode 100644 index 000000000..8df939fa1 --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errflat.yaml @@ -0,0 +1,54 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: cris-fsr_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + variables: [error_inflation_factor_lat] + tolerance: 1.e-8 diff --git a/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errftopo.yaml b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errftopo.yaml new file mode 100644 index 000000000..4a01baf2a --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errftopo.yaml @@ -0,0 +1,56 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: cris-fsr_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorFactorTopoRad@ObsFunction + options: + channels: *all_channels + sensor: cris-fsr_npp + variables: [error_inflation_factor_topo] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errftransmittop.yaml b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errftransmittop.yaml new file mode 100644 index 000000000..474a32c54 --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errftransmittop.yaml @@ -0,0 +1,55 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: cris-fsr_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorFactorTransmitTopRad@ObsFunction + options: + channels: *all_channels + variables: [error_inflation_factor_transmittop] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errfwavenum.yaml b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errfwavenum.yaml new file mode 100644 index 000000000..bb533b9b7 --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_errfwavenum.yaml @@ -0,0 +1,55 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: cris-fsr_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorFactorWavenumIR@ObsFunction + options: + channels: *all_channels + variables: [error_inflation_factor_wavenum] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_nsstret.yaml b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_nsstret.yaml new file mode 100644 index 000000000..7cf5ad925 --- /dev/null +++ b/test/testinput/instrumentTests/cris/cris-fsr_npp_gfs_function_nsstret.yaml @@ -0,0 +1,103 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: cris-fsr_npp + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels + 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 208, + 211, 216, 224, 234, 236, 238, 239, 242, 246, 248, 255, 264, 266, 268, + 275, 279, 283, 285, 291, 295, 301, 305, 311, 332, 342, 389, 400, 402, + 404, 406, 410, 427, 439, 440, 441, 445, 449, 455, 458, 461, 464, 467, + 470, 473, 475, 482, 486, 487, 490, 493, 496, 499, 501, 503, 505, 511, + 513, 514, 518, 519, 520, 522, 529, 534, 563, 568, 575, 592, 594, 596, + 598, 600, 602, 604, 611, 614, 616, 618, 620, 622, 626, 631, 638, 646, + 648, 652, 659, 673, 675, 678, 684, 688, 694, 700, 707, 710, 713, 714, + 718, 720, 722, 725, 728, 735, 742, 748, 753, 762, 780, 784, 798, 849, + 860, 862, 866, 874, 882, 890, 898, 906, 907, 908, 914, 937, 972, 973, + 978, 980, 981, 988, 995, 998, 1000, 1003, 1008, 1009, 1010, 1014, 1017, + 1018, 1020, 1022, 1024, 1026, 1029, 1030, 1032, 1034, 1037, 1038, 1041, + 1042, 1044, 1046, 1049, 1050, 1053, 1054, 1058, 1060, 1062, 1064, 1066, + 1069, 1076, 1077, 1080, 1086, 1091, 1095, 1101, 1109, 1112, 1121, 1128, + 1133, 1163, 1172, 1187, 1189, 1205, 1211, 1219, 1231, 1245, 1271, 1289, + 1300, 1313, 1316, 1325, 1329, 1346, 1347, 1473, 1474, 1491, 1499, 1553, + 1570, 1596, 1602, 1619, 1624, 1635, 1939, 1940, 1941, 1942, 1943, 1944, + 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, + 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, + 1981, 1982, 1983, 1984, 1985, 1986, 1987, 2119, 2140, 2143, 2147, 2153, + 2158, 2161, 2168, 2171, 2175, 2182 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/cris-fsr_npp_obsdiag_2020120112.nc4 + obs function: + name: NearSSTRetCheckIR@ObsFunction + options: + test_qcflag: PreQC + test_obserr: GsiObsError_nsstret + test_hofx: GsiHofX + test_bias: GsiObsBias + channels: *all_channels + use_flag: [ -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, + -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, + 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1 ] + variables: [nsstret_check] + channels: *all_channels + tolerance: 1.0e-4 diff --git a/test/testinput/instrumentTests/iasi/CMakeLists.txt b/test/testinput/instrumentTests/iasi/CMakeLists.txt new file mode 100644 index 000000000..ddd29fbc7 --- /dev/null +++ b/test/testinput/instrumentTests/iasi/CMakeLists.txt @@ -0,0 +1,77 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for IASI + +if( crtm_FOUND ) + # H(x) test + ufo_add_test( NAME instrument_iasi_metop-a_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/iasi_metop-a_gfs_HofX.yaml" + MPI 1 + LABELS iasi metop-a HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_iasi_metop-b_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/iasi_metop-b_gfs_HofX.yaml" + MPI 1 + LABELS iasi metop-b HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with Bias Correction (bc) + ufo_add_test( NAME instrument_iasi_metop-a_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/iasi_metop-a_gfs_HofX_bc.yaml" + MPI 1 + LABELS iasi metop-a bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_iasi_metop-b_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/iasi_metop-b_gfs_HofX_bc.yaml" + MPI 1 + LABELS iasi metop-b bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with QC + ufo_add_test( NAME instrument_iasi_metop-a_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/iasi_metop-a_gfs_HofX_qc.yaml" + MPI 1 + LABELS iasi metop-a qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_iasi_metop-b_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/iasi_metop-b_gfs_HofX_qc.yaml" + MPI 1 + LABELS iasi metop-b qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) +endif( crtm_FOUND ) diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_HofX.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_HofX.yaml new file mode 100644 index 000000000..dbce6de39 --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_HofX.yaml @@ -0,0 +1,69 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: iasi_metop-a + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_HofX_bc.yaml new file mode 100644 index 000000000..83acd1fb5 --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_HofX_bc.yaml @@ -0,0 +1,92 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID iasi_metop-a + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_iasi_metop-a.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &iasi_metop-a_tlap Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_tlapmean.txt + - name: lapse_rate + options: + tlapse: *iasi_metop-a_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_HofX_qc.yaml new file mode 100644 index 000000000..fd4297d06 --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_HofX_qc.yaml @@ -0,0 +1,545 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID iasi_metop-a + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/iasi_metop-a_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_iasi_metop-a.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &iasi_metop-a_tlap Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_tlapmean.txt + - name: lapse_rate + options: + tlapse: *iasi_metop-a_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: +# Wavenumber Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, 7069, 7072, + 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, + 7436, 7444, 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, + 7865, 7885, 7888, 7912, 7950, 7972, 7980, 7995, 8007, 8015, + 8055, 8078 + where: + - variable: + name: solar_zenith_angle@MetaData + maxvalue: 88.9999 + - variable: + name: water_area_fraction@GeoVaLs + minvalue: 1.0e-12 + action: + name: reject + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorWavenumIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + minvalue: 50.00001 + maxvalue: 449.99999 + action: + name: reject +# Topography Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID +# Transmittance Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Cloud Detection Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: CloudDetectMinResidualIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, + 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, + 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, + 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, + -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1] + use_flag_clddet: [ 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, + 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, + 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# NSST Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: NearSSTRetCheckIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, + 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, + 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, + 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, + -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1] + obserr_demisf: [0.01,0.02,0.03,0.02,0.03] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# Surface Jacobians Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_max: [ 3.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 4.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 4.0, 4.0, + 3.5, 2.5, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 3.5, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 3.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.5, 2.0, 2.5, 2.5, 3.0, 2.5, + 2.5, 2.5, 2.5, 3.5, 2.5, 2.5, 3.0, 3.5, 3.0, 4.0, + 4.0, 4.0, 4.0, 4.0, 4.0, 4.5, 4.5, 4.5, 4.5, 4.5, + 4.0, 4.5, 4.0, 4.0, 4.5, 2.5, 3.0, 2.5, 3.0, 2.5, + 3.0, 2.0, 2.5, 2.5, 3.0, 3.0, 2.5, 3.0, 3.0, 3.0, + 2.5, 2.5, 4.0, 4.5, 4.5, 5.0, 4.0, 4.0, 5.0, 5.0, + 5.0, 5.0, 5.5, 5.5, 4.0, 5.0, 4.0, 4.5, 5.5, 5.5, + 6.0, 4.5, 4.5, 4.0, 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 5.5, 4.5, 6.0, + 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 5.0, 6.0, + 6.0, 6.0, 4.0, 6.0, 6.0, 6.0, 6.0, 4.5, 6.0, 6.0, + 4.5, 6.0, 6.0, 6.0, 6.0, 6.0, 5.0, 6.0, 6.0, 6.0, + 5.0, 6.0, 6.0, 5.0, 6.0, 5.0, 6.0, 6.0, 6.0, 5.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0] + action: + name: reject +# Useflag Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, + 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, + 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, + 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, + -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 1193 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errfgrosschk.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errfgrosschk.yaml new file mode 100644 index 000000000..7fd9dceef --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errfgrosschk.yaml @@ -0,0 +1,144 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorBoundIR@ObsFunction + options: + channels: *all_channels + test_qcflag: PreQC + test_obserr: GsiObsError_grosschk + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_max: [ 3.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 4.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 4.0, 4.0, + 3.5, 2.5, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 3.5, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 3.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.5, 2.0, 2.5, 2.5, 3.0, 2.5, + 2.5, 2.5, 2.5, 3.5, 2.5, 2.5, 3.0, 3.5, 3.0, 4.0, + 4.0, 4.0, 4.0, 4.0, 4.0, 4.5, 4.5, 4.5, 4.5, 4.5, + 4.0, 4.5, 4.0, 4.0, 4.5, 2.5, 3.0, 2.5, 3.0, 2.5, + 3.0, 2.0, 2.5, 2.5, 3.0, 3.0, 2.5, 3.0, 3.0, 3.0, + 2.5, 2.5, 4.0, 4.5, 4.5, 5.0, 4.0, 4.0, 5.0, 5.0, + 5.0, 5.0, 5.5, 5.5, 4.0, 5.0, 4.0, 4.5, 5.5, 5.5, + 6.0, 4.5, 4.5, 4.0, 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 5.5, 4.5, 6.0, + 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 5.0, 6.0, + 6.0, 6.0, 4.0, 6.0, 6.0, 6.0, 6.0, 4.5, 6.0, 6.0, + 4.5, 6.0, 6.0, 6.0, 6.0, 6.0, 5.0, 6.0, 6.0, 6.0, + 5.0, 6.0, 6.0, 5.0, 6.0, 5.0, 6.0, 6.0, 6.0, 5.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0] + variables: [error_inflation_factor_grosschk] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errfjsfc.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errfjsfc.yaml new file mode 100644 index 000000000..c8ab868ec --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errfjsfc.yaml @@ -0,0 +1,75 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + options: + channels: *all_channels + obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] + test_qcflag: PreQC + test_obserr: GsiObsError_jsfc + variables: [error_inflation_factor_jsfc] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errflat.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errflat.yaml new file mode 100644 index 000000000..c1e01fa0e --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errflat.yaml @@ -0,0 +1,70 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + variables: [error_inflation_factor_lat] + tolerance: 1.e-8 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errftopo.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errftopo.yaml new file mode 100644 index 000000000..ce0f2aed0 --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errftopo.yaml @@ -0,0 +1,72 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorFactorTopoRad@ObsFunction + options: + channels: *all_channels + sensor: iasi_metop-a + variables: [error_inflation_factor_topo] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errftransmittop.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errftransmittop.yaml new file mode 100644 index 000000000..c12d51d0e --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errftransmittop.yaml @@ -0,0 +1,71 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorFactorTransmitTopRad@ObsFunction + options: + channels: *all_channels + variables: [error_inflation_factor_transmittop] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errfwavenum.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errfwavenum.yaml new file mode 100644 index 000000000..1d3a7d6b2 --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_errfwavenum.yaml @@ -0,0 +1,70 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: iasi_metop-a + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obsdiag_2020120112.nc4 + obs function: + name: ObsErrorFactorWavenumIR@ObsFunction + options: + channels: *all_channels + variables: [error_inflation_factor_wavenum] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_nsstret.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_nsstret.yaml new file mode 100644 index 000000000..3d23f6c1a --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-a_gfs_function_nsstret.yaml @@ -0,0 +1,107 @@ +window begin: 2020-12-01T09:00:00Z +window end: 2020-12-01T15:00:00Z + +observations: +- obs space: + name: iasi_metop-a + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obs_2020120112.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + # can have GeoVaLs section like below + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_geoval_2020120112.nc4 + # can have ObsDiag section like below + obs diagnostics: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-a_obsdiag_2020120112.nc4 + obs function: + name: NearSSTRetCheckIR@ObsFunction + options: + test_qcflag: PreQC + test_obserr: GsiObsError_nsstret + test_hofx: GsiHofX + test_bias: GsiObsBias + channels: *all_channels + use_flag: [1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, + -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, + 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, + 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, + 1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, + -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, + 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, + -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, + -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] + variables: [nsstret_check] + channels: *all_channels + tolerance: 1.0e-8 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-b_gfs_HofX.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-b_gfs_HofX.yaml new file mode 100644 index 000000000..68d21fc53 --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-b_gfs_HofX.yaml @@ -0,0 +1,69 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: iasi_metop-b + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: iasi_metop-b + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-b_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-b_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-b_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-b_gfs_HofX_bc.yaml new file mode 100644 index 000000000..24885b056 --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-b_gfs_HofX_bc.yaml @@ -0,0 +1,92 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID iasi_metop-b + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: iasi_metop-b + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-b_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-b_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_iasi_metop-b.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &iasi_metop-b_tlap Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-b_tlapmean.txt + - name: lapse_rate + options: + tlapse: *iasi_metop-b_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/iasi/iasi_metop-b_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/iasi/iasi_metop-b_gfs_HofX_qc.yaml new file mode 100644 index 000000000..e80d9b2fa --- /dev/null +++ b/test/testinput/instrumentTests/iasi/iasi_metop-b_gfs_HofX_qc.yaml @@ -0,0 +1,545 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID iasi_metop-b + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: iasi_metop-b + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-b_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/iasi_metop-b_obs_2020110112_qc_filters_out.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, + 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, + 84, 85, 86, 87, 89, 92, 93, 95, 97, 99, 101, 103, 104, 106, 109, 110, + 111, 113, 116, 119, 122, 125, 128, 131, 133, 135, 138, 141, 144, 146, + 148, 150, 151, 154, 157, 159, 160, 161, 163, 167, 170, 173, 176, 179, + 180, 185, 187, 191, 193, 197, 199, 200, 202, 203, 205, 207, 210, 212, + 213, 214, 217, 218, 219, 222, 224, 225, 226, 228, 230, 231, 232, 236, + 237, 239, 243, 246, 249, 252, 254, 259, 260, 262, 265, 267, 269, 275, + 279, 282, 285, 294, 296, 299, 300, 303, 306, 309, 313, 320, 323, 326, + 327, 329, 332, 335, 345, 347, 350, 354, 356, 360, 363, 366, 371, 372, + 373, 375, 377, 379, 381, 383, 386, 389, 398, 401, 404, 405, 407, 408, + 410, 411, 414, 416, 418, 423, 426, 428, 432, 433, 434, 439, 442, 445, + 450, 457, 459, 472, 477, 483, 509, 515, 546, 552, 559, 566, 571, 573, + 578, 584, 594, 625, 646, 662, 668, 705, 739, 756, 797, 867, 906, 921, + 1027, 1046, 1090, 1098, 1121, 1133, 1173, 1191, 1194, 1222, 1271, 1283, + 1338, 1409, 1414, 1420, 1424, 1427, 1430, 1434, 1440, 1442, 1445, 1450, + 1454, 1460, 1463, 1469, 1474, 1479, 1483, 1487, 1494, 1496, 1502, 1505, + 1509, 1510, 1513, 1518, 1521, 1526, 1529, 1532, 1536, 1537, 1541, 1545, + 1548, 1553, 1560, 1568, 1574, 1579, 1583, 1585, 1587, 1606, 1626, 1639, + 1643, 1652, 1658, 1659, 1666, 1671, 1675, 1681, 1694, 1697, 1710, 1786, + 1791, 1805, 1839, 1884, 1913, 1946, 1947, 1991, 2019, 2094, 2119, 2213, + 2239, 2271, 2289, 2321, 2333, 2346, 2349, 2352, 2359, 2367, 2374, 2398, + 2426, 2562, 2701, 2741, 2745, 2760, 2819, 2889, 2907, 2910, 2919, 2921, + 2939, 2944, 2945, 2948, 2951, 2958, 2971, 2977, 2985, 2988, 2990, 2991, + 2993, 3002, 3008, 3014, 3027, 3029, 3030, 3036, 3047, 3049, 3052, 3053, + 3055, 3058, 3064, 3069, 3087, 3093, 3098, 3105, 3107, 3110, 3116, 3127, + 3129, 3136, 3146, 3151, 3160, 3165, 3168, 3175, 3178, 3189, 3207, 3228, + 3244, 3248, 3252, 3256, 3263, 3281, 3295, 3303, 3309, 3312, 3322, 3326, + 3354, 3366, 3375, 3378, 3411, 3416, 3432, 3438, 3440, 3442, 3444, 3446, + 3448, 3450, 3452, 3454, 3458, 3467, 3476, 3484, 3491, 3497, 3499, 3504, + 3506, 3509, 3518, 3527, 3555, 3575, 3577, 3580, 3582, 3586, 3589, 3599, + 3610, 3626, 3638, 3646, 3653, 3658, 3661, 3673, 3689, 3700, 3710, 3726, + 3763, 3814, 3841, 3888, 4032, 4059, 4068, 4082, 4095, 4160, 4234, 4257, + 4411, 4498, 4520, 4552, 4567, 4608, 4646, 4698, 4808, 4849, 4920, 4939, + 4947, 4967, 4991, 4996, 5015, 5028, 5056, 5128, 5130, 5144, 5170, 5178, + 5183, 5188, 5191, 5368, 5371, 5379, 5381, 5383, 5397, 5399, 5401, 5403, + 5405, 5446, 5455, 5472, 5480, 5483, 5485, 5492, 5497, 5502, 5507, 5509, + 5517, 5528, 5558, 5697, 5714, 5749, 5766, 5785, 5798, 5799, 5801, 5817, + 5833, 5834, 5836, 5849, 5851, 5852, 5865, 5869, 5881, 5884, 5897, 5900, + 5916, 5932, 5948, 5963, 5968, 5978, 5988, 5992, 5994, 5997, 6003, 6008, + 6023, 6026, 6039, 6053, 6056, 6067, 6071, 6082, 6085, 6098, 6112, 6126, + 6135, 6140, 6149, 6154, 6158, 6161, 6168, 6174, 6182, 6187, 6205, 6209, + 6213, 6317, 6339, 6342, 6366, 6381, 6391, 6489, 6962, 6966, 6970, 6975, + 6977, 6982, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7000, 7004, + 7008, 7013, 7016, 7021, 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, + 7069, 7072, 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, 7436, 7444, + 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, 7865, 7885, 7888, 7912, + 7950, 7972, 7980, 7995, 8007, 8015, 8055, 8078 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-b_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_iasi_metop-b.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &iasi_metop-b_tlap Data/ufo/testinput_tier_1/instruments/radiance/iasi_metop-b_tlapmean.txt + - name: lapse_rate + options: + tlapse: *iasi_metop-b_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: +# Wavenumber Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 7024, 7027, 7029, 7032, 7038, 7043, 7046, 7049, 7069, 7072, + 7076, 7081, 7084, 7089, 7099, 7209, 7222, 7231, 7235, 7247, + 7267, 7269, 7284, 7389, 7419, 7423, 7424, 7426, 7428, 7431, + 7436, 7444, 7475, 7549, 7584, 7665, 7666, 7831, 7836, 7853, + 7865, 7885, 7888, 7912, 7950, 7972, 7980, 7995, 8007, 8015, + 8055, 8078 + where: + - variable: + name: solar_zenith_angle@MetaData + maxvalue: 88.9999 + - variable: + name: water_area_fraction@GeoVaLs + minvalue: 1.0e-12 + action: + name: reject + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorWavenumIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + minvalue: 50.00001 + maxvalue: 449.99999 + action: + name: reject +# Topography Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTopoRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + sensor: *Sensor_ID +# Transmittance Top Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels +# Cloud Detection Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: CloudDetectMinResidualIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, + 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, + 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, + 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, + -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1] + use_flag_clddet: [ 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, + 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, + 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# NSST Retrieval Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: NearSSTRetCheckIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, + 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, + 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, + 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, + -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1] + obserr_demisf: [0.01,0.02,0.03,0.02,0.03] + obserr_dtempf: [0.50,2.00,4.00,2.00,4.00] + maxvalue: 1.0e-12 + action: + name: reject +# Surface Jacobians Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.01, 0.02, 0.03, 0.02, 0.03] + obserr_dtempf: [0.50, 2.00, 4.00, 2.00, 4.00] +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + function absolute threshold: + - name: ObsErrorBoundIR@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_latitude: + name: ObsErrorFactorLatRad@ObsFunction + options: + latitude_parameters: [25.0, 0.5, 0.04, 1.0] + obserr_bound_transmittop: + name: ObsErrorFactorTransmitTopRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_bound_max: [ 3.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 4.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 4.0, 4.0, + 3.5, 2.5, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 3.5, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 3.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + 2.0, 2.0, 2.0, 2.0, 2.5, 2.0, 2.5, 2.5, 3.0, 2.5, + 2.5, 2.5, 2.5, 3.5, 2.5, 2.5, 3.0, 3.5, 3.0, 4.0, + 4.0, 4.0, 4.0, 4.0, 4.0, 4.5, 4.5, 4.5, 4.5, 4.5, + 4.0, 4.5, 4.0, 4.0, 4.5, 2.5, 3.0, 2.5, 3.0, 2.5, + 3.0, 2.0, 2.5, 2.5, 3.0, 3.0, 2.5, 3.0, 3.0, 3.0, + 2.5, 2.5, 4.0, 4.5, 4.5, 5.0, 4.0, 4.0, 5.0, 5.0, + 5.0, 5.0, 5.5, 5.5, 4.0, 5.0, 4.0, 4.5, 5.5, 5.5, + 6.0, 4.5, 4.5, 4.0, 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 5.5, 4.5, 6.0, + 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 5.0, 6.0, + 6.0, 6.0, 4.0, 6.0, 6.0, 6.0, 6.0, 4.5, 6.0, 6.0, + 4.5, 6.0, 6.0, 6.0, 6.0, 6.0, 5.0, 6.0, 6.0, 6.0, + 5.0, 6.0, 6.0, 5.0, 6.0, 5.0, 6.0, 6.0, 6.0, 5.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, + 6.0, 6.0, 6.0, 6.0, 6.0, 6.0] + action: + name: reject +# Useflag Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, + 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, + 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, + 1, -1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, + 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, + 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, + 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, + -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, -1, 1, -1, 1, -1, + 1, 1, -1, -1, 1, 1, 1, -1, 1, 1, + -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, + 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, + 1, -1, -1, -1, 1, -1, -1, -1, -1, -1, + -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, + 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, + -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, + 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, + -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, + -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 792 diff --git a/test/testinput/instrumentTests/mhs/CMakeLists.txt b/test/testinput/instrumentTests/mhs/CMakeLists.txt new file mode 100644 index 000000000..da0719706 --- /dev/null +++ b/test/testinput/instrumentTests/mhs/CMakeLists.txt @@ -0,0 +1,111 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for MHS + + +if( crtm_FOUND ) + # H(x) test + ufo_add_test( NAME instrument_mhs_metop-b_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/mhs_metop-b_gfs_HofX.yaml" + MPI 1 + LABELS mhs metop-b HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_mhs_metop-c_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/mhs_metop-c_gfs_HofX.yaml" + MPI 1 + LABELS mhs metop-c HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_mhs_n19_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/mhs_n19_gfs_HofX.yaml" + MPI 1 + LABELS mhs n19 HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with Bias Correction (bc) + ufo_add_test( NAME instrument_mhs_metop-b_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/mhs_metop-b_gfs_HofX_bc.yaml" + MPI 1 + LABELS mhs metop-b bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_mhs_metop-c_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/mhs_metop-c_gfs_HofX_bc.yaml" + MPI 1 + LABELS mhs metop-c bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + ufo_add_test( NAME instrument_mhs_n19_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/mhs_n19_gfs_HofX_bc.yaml" + MPI 1 + LABELS mhs n19 bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + +# # H(x) with QC +# ufo_add_test( NAME instrument_mhs_metop-b_gfs_HofX_qc +# TIER 2 +# ECBUILD +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/mhs_metop-b_gfs_HofX_qc.yaml" +# MPI 1 +# LABELS mhs metop-b qc instrument +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + +# ufo_add_test( NAME instrument_mhs_metop-c_gfs_HofX_qc +# TIER 2 +# ECBUILD +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/mhs_metop-c_gfs_HofX_qc.yaml" +# MPI 1 +# LABELS mhs metop-c qc instrument +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + +# ufo_add_test( NAME instrument_mhs_n19_gfs_HofX_qc +# TIER 2 +# ECBUILD +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/mhs_n19_gfs_HofX_qc.yaml" +# MPI 1 +# LABELS mhs n19 qc instrument +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) +endif( crtm_FOUND ) diff --git a/test/testinput/instrumentTests/mhs/mhs_metop-b_gfs_HofX.yaml b/test/testinput/instrumentTests/mhs/mhs_metop-b_gfs_HofX.yaml new file mode 100644 index 000000000..16d11a30e --- /dev/null +++ b/test/testinput/instrumentTests/mhs/mhs_metop-b_gfs_HofX.yaml @@ -0,0 +1,21 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: mhs_metop-b + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: mhs_metop-b + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/mhs_metop-b_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/mhs_metop-b_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.5e-5 diff --git a/test/testinput/instrumentTests/mhs/mhs_metop-b_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/mhs/mhs_metop-b_gfs_HofX_bc.yaml new file mode 100644 index 000000000..3efbb4225 --- /dev/null +++ b/test/testinput/instrumentTests/mhs/mhs_metop-b_gfs_HofX_bc.yaml @@ -0,0 +1,48 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID mhs_metop-b + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: mhs_metop-b + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/mhs_metop-b_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/mhs_metop-b_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_mhs_metop-b.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &mhs_metop-b_tlap Data/ufo/testinput_tier_1/instruments/radiance/mhs_metop-b_tlapmean.txt + - name: lapse_rate + options: + tlapse: *mhs_metop-b_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/mhs/mhs_metop-c_gfs_HofX.yaml b/test/testinput/instrumentTests/mhs/mhs_metop-c_gfs_HofX.yaml new file mode 100644 index 000000000..41a486216 --- /dev/null +++ b/test/testinput/instrumentTests/mhs/mhs_metop-c_gfs_HofX.yaml @@ -0,0 +1,21 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: mhs_metop-c + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: mhs_metop-c + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/mhs_metop-c_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/mhs_metop-c_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.5e-5 diff --git a/test/testinput/instrumentTests/mhs/mhs_metop-c_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/mhs/mhs_metop-c_gfs_HofX_bc.yaml new file mode 100644 index 000000000..a40bcaa97 --- /dev/null +++ b/test/testinput/instrumentTests/mhs/mhs_metop-c_gfs_HofX_bc.yaml @@ -0,0 +1,48 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID mhs_metop-c + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: mhs_metop-c + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/mhs_metop-c_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/mhs_metop-c_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_mhs_metop-c.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &mhs_metop-c_tlap Data/ufo/testinput_tier_1/instruments/radiance/mhs_metop-c_tlapmean.txt + - name: lapse_rate + options: + tlapse: *mhs_metop-c_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/mhs/mhs_n19_gfs_HofX.yaml b/test/testinput/instrumentTests/mhs/mhs_n19_gfs_HofX.yaml new file mode 100644 index 000000000..4bd040190 --- /dev/null +++ b/test/testinput/instrumentTests/mhs/mhs_n19_gfs_HofX.yaml @@ -0,0 +1,21 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: mhs_n19 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: mhs_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/mhs_n19_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/mhs_n19_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.5e-5 diff --git a/test/testinput/instrumentTests/mhs/mhs_n19_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/mhs/mhs_n19_gfs_HofX_bc.yaml new file mode 100644 index 000000000..82dfe389c --- /dev/null +++ b/test/testinput/instrumentTests/mhs/mhs_n19_gfs_HofX_bc.yaml @@ -0,0 +1,48 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID mhs_n19 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: mhs_n19 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/mhs_n19_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-5 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/mhs_n19_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_mhs_n19.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: cosine_of_latitude_times_orbit_node + options: + preconditioner: 0.01 + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &mhs_n19_tlap Data/ufo/testinput_tier_1/instruments/radiance/mhs_n19_tlapmean.txt + - name: lapse_rate + options: + tlapse: *mhs_n19_tlap + - name: emissivity + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-7 diff --git a/test/testinput/instrumentTests/radarVAD/CMakeLists.txt b/test/testinput/instrumentTests/radarVAD/CMakeLists.txt new file mode 100644 index 000000000..f02f047d2 --- /dev/null +++ b/test/testinput/instrumentTests/radarVAD/CMakeLists.txt @@ -0,0 +1,27 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for radar VAD wind + +# H(x) test +ecbuild_add_test( TARGET test_ufo_radarVAD_gfs_HofX + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/radarVAD_gfs_HofX.yaml" + LABELS radarVAD HofX instrument + ENVIRONMENT OOPS_TRAPFPE=1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ) + +# H(x) with QC +#ecbuild_add_test( TARGET test_ufo_vadwinds_gfs_qc +# COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x +# ARGS "${CMAKE_CURRENT_SOURCE_DIR}/vadwinds_gfs_qc.yaml" +# LABELS radarVAD QC instrument +# ENVIRONMENT OOPS_TRAPFPE=1 +# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ +# LIBS ufo +# TEST_DEPENDS test_ufo_radarVAD_gfs_HofX ) + diff --git a/test/testinput/instrumentTests/radarVAD/radarVAD_gfs_HofX.yaml b/test/testinput/instrumentTests/radarVAD/radarVAD_gfs_HofX.yaml new file mode 100644 index 000000000..4fcaf43ba --- /dev/null +++ b/test/testinput/instrumentTests/radarVAD/radarVAD_gfs_HofX.yaml @@ -0,0 +1,15 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: VertInterp + obs space: + name: RadarVAD + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/vadwind_obs_2020110112_m.nc4 + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/vadwind_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.0e-04 diff --git a/test/testinput/instrumentTests/radarVAD/vadwinds_gfs_qc.yaml b/test/testinput/instrumentTests/radarVAD/vadwinds_gfs_qc.yaml new file mode 100644 index 000000000..ea0f9fa00 --- /dev/null +++ b/test/testinput/instrumentTests/radarVAD/vadwinds_gfs_qc.yaml @@ -0,0 +1,163 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs space: + name: vadwinds_QC + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/conventional/vadwind_obs_2020110112_m.nc4 + obsgrouping: + group variables: ["station_id", "datetime"] + sort variable: "air_pressure" + sort order: "descending" + obsdataout: + obsfile: Data/vadwind_obs-output_2020120112.nc4 + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/instruments/conventional/vadwind_geoval_2020110112_m.nc4 +#-------------------------------------------------------------------------------------------------------------------- + obs filters: +# Begin by assigning all ObsError to a constant value. These might get overwritten later. + - filter: BlackList + filter variables: + - name: eastward_wind + - name: northward_wind + action: + name: assign error + error parameter: 2.0 # 2.0 m/s +# +# Assign the initial ObsError, based on height/pressure + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: air_pressure@MetaData + xvals: [100000, 95000, 85000, 80000, 70000, 65000, 60000, 55000, 50000, 45000, 40000, 35000, 30000, 25000, 20000, 15000, 10000] + errors: [1.4, 1.5, 1.5, 1.6, 1.6, 1.8, 1.9, 2.0, 2.1, 2.3, 2.6, 2.8, 3.0, 3.2, 2.7, 2.4, 2.1] +# +# Reject all obs with PreQC mark already set above 3 + - filter: PreQC + maxvalue: 3 + action: + name: reject +# +# Reject when pressure is less than 226 mb. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: air_pressure@MetaData + minvalue: 22600 + action: + name: reject +# +# Observation Range Sanity Check: either wind component or velocity exceeds 135 m/s + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + minvalue: -135 + maxvalue: 135 + action: + name: reject + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: Velocity@ObsFunction + maxvalue: 135.0 + action: + name: reject +# +# Reject when difference of wind direction is more than 50 degrees. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: WindDirAngleDiff@ObsFunction + options: + test_hofx: GsiHofX + maxvalue: 50.0 + action: + name: reject + defer to post: true +# +# Inflate obserror when multiple obs exist inside vertical model layers. + - filter: BlackList + filter variables: + - name: eastward_wind + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [eastward_wind] + defer to post: true +# + - filter: BlackList + filter variables: + - name: northward_wind + action: + name: inflate error + inflation variable: + name: ObsErrorFactorConventional@ObsFunction + options: + test QCflag: PreQC + inflate variables: [northward_wind] + defer to post: true +# + - filter: Background Check + filter variables: + - name: eastward_wind + - name: northward_wind + absolute threshold: 7.5 + test_hofx: GsiHofX # Remove this line when running UFO H(x) + action: + name: inflate error + inflation factor: 2.5 + defer to post: true +# +# If the total inflation factor is too big, reject. + - filter: Bounds Check + filter variables: + - name: eastward_wind + action: + name: reject + maxvalue: 6.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: eastward_wind@ObsErrorData # After inflation step + denominator: + name: eastward_wind@ObsError + defer to post: true +# + - filter: Bounds Check + filter variables: + - name: northward_wind + action: + name: reject + maxvalue: 6.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: + name: northward_wind@ObsErrorData # After inflation step + denominator: + name: northward_wind@ObsError + defer to post: true + + passedBenchmark: 806 diff --git a/test/testinput/instrumentTests/seviri/CMakeLists.txt b/test/testinput/instrumentTests/seviri/CMakeLists.txt new file mode 100644 index 000000000..ebc622fcb --- /dev/null +++ b/test/testinput/instrumentTests/seviri/CMakeLists.txt @@ -0,0 +1,45 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for SEVIRI + + +if( crtm_FOUND ) + # H(x) test + ufo_add_test( NAME instrument_seviri_m11_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/seviri_m11_gfs_HofX.yaml" + MPI 1 + LABELS seviri m11 HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data get_crtm_coeffs ) + + # H(x) with Bias Correction (bc) + ufo_add_test( NAME instrument_seviri_m11_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/seviri_m11_gfs_HofX_bc.yaml" + MPI 1 + LABELS seviri m11 bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with QC + ufo_add_test( NAME instrument_seviri_m11_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/seviri_m11_gfs_HofX_qc.yaml" + MPI 1 + LABELS seviri m11 qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) +endif( crtm_FOUND ) diff --git a/test/testinput/instrumentTests/seviri/seviri_m11_gfs_HofX.yaml b/test/testinput/instrumentTests/seviri/seviri_m11_gfs_HofX.yaml new file mode 100644 index 000000000..54c2ec7e2 --- /dev/null +++ b/test/testinput/instrumentTests/seviri/seviri_m11_gfs_HofX.yaml @@ -0,0 +1,21 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: seviri_m11 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: seviri_m11 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/seviri_m11_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 4-11 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/seviri_m11_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-5 diff --git a/test/testinput/instrumentTests/seviri/seviri_m11_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/seviri/seviri_m11_gfs_HofX_bc.yaml new file mode 100644 index 000000000..589947de3 --- /dev/null +++ b/test/testinput/instrumentTests/seviri/seviri_m11_gfs_HofX_bc.yaml @@ -0,0 +1,45 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID seviri_m11 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: seviri_m11 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/seviri_m11_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &channels 4-11 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/seviri_m11_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_seviri_m11.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &seviri_m11_tlap Data/ufo/testinput_tier_1/instruments/radiance/seviri_m11_tlapmean.txt + - name: lapse_rate + options: + tlapse: *seviri_m11_tlap + - name: emissivity +# The scan angle here actually is used as for scan positions in the input ioda file to replicate gsi + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + vector ref: GsiHofXBc + tolerance: 1.e-6 diff --git a/test/testinput/instrumentTests/seviri/seviri_m11_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/seviri/seviri_m11_gfs_HofX_qc.yaml new file mode 100644 index 000000000..69fc9eccc --- /dev/null +++ b/test/testinput/instrumentTests/seviri/seviri_m11_gfs_HofX_qc.yaml @@ -0,0 +1,145 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID seviri_m11 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: seviri_m11 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/seviri_m11_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: &all_channels 4-11 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/seviri_m11_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_seviri_m11.2020110106.nc4 + variational bc: + predictors: + - name: constant + - name: lapse_rate + options: + order: 2 + tlapse: &seviri_m11_tlap Data/ufo/testinput_tier_1/instruments/radiance/seviri_m11_tlapmean.txt + - name: lapse_rate + options: + tlapse: *seviri_m11_tlap + - name: emissivity +# The scan angle here actually is used as for scan positions in the input ioda file to replicate gsi + - name: scan_angle + options: + order: 4 + - name: scan_angle + options: + order: 3 + - name: scan_angle + options: + order: 2 + - name: scan_angle + obs filters: +# Observation Range Sanity Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + minvalue: 50.00001 + maxvalue: 449.99999 + action: + name: reject +# Surface Check:use chn 2 and 3 over both sea and land while other IR chns only over sea +# ch2 and ch3 in GSI should be the original seviri ch5 and ch6 + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 4,7-11 + where: + - variable: + name: water_area_fraction@GeoVaLs + maxvalue: 0.99 +# Do not use ch5,6 over snow + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + where: + - variable: + name: surface_snow_area_fraction@GeoVaLs + minvalue: 0.01 +# Do not use ch5,6 over ice + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + where: + - variable: + name: ice_area_fraction@GeoVaLs + minvalue: 0.01 +# Do not use over mixed surface + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + where: + - variable: + name: land_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: water_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: ice_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: surface_snow_area_fraction@GeoVaLs + maxvalue: 0.99 +# QC_terrain: If seviri and terrain height > 1km. do not use + - filter: Domain Check + filter variables: + - name: brightness_temperature + channels: *all_channels + where: + - variable: + name: surface_geopotential_height@GeoVaLs + maxvalue: 1000.0 +# Gross check + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *all_channels + absolute threshold: 2.0 + action: + name: reject +# Surface Jacobians Check + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + obserr_demisf: [0.01, 0.02, 0.02, 0.02, 0.02] + obserr_dtempf: [0.50, 2.00, 3.00, 3.00, 5.00] +# Useflag Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *all_channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *all_channels + options: + channels: *all_channels + use_flag: [ -1, 1, 1, -1, -1, -1, -1, -1 ] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 13 diff --git a/test/testinput/instrumentTests/ssmis/CMakeLists.txt b/test/testinput/instrumentTests/ssmis/CMakeLists.txt new file mode 100644 index 000000000..1fae5e991 --- /dev/null +++ b/test/testinput/instrumentTests/ssmis/CMakeLists.txt @@ -0,0 +1,45 @@ +# (C) Copyright 2020-2021 UCAR. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + +# Instrument tests for SSMIS + + +if( crtm_FOUND ) + # H(x) test + ufo_add_test( NAME instrument_ssmis_f17_gfs_HofX + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/ssmis_f17_gfs_HofX.yaml" + MPI 1 + LABELS ssmis f17 HofX instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with Bias Correction (bc) + ufo_add_test( NAME instrument_ssmis_f17_gfs_HofX_bc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/ssmis_f17_gfs_HofX_bc.yaml" + MPI 1 + LABELS ssmis f17 bc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) + + # H(x) with QC + ufo_add_test( NAME instrument_ssmis_f17_gfs_HofX_qc + TIER 2 + ECBUILD + COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsFilters.x + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/ssmis_f17_gfs_HofX_qc.yaml" + MPI 1 + LABELS ssmis f17 qc instrument + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../ + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data ufo_get_crtm_test_data ) +endif( crtm_FOUND ) diff --git a/test/testinput/instrumentTests/ssmis/ssmis_f17_gfs_HofX.yaml b/test/testinput/instrumentTests/ssmis/ssmis_f17_gfs_HofX.yaml new file mode 100644 index 000000000..8375d1db7 --- /dev/null +++ b/test/testinput/instrumentTests/ssmis/ssmis_f17_gfs_HofX.yaml @@ -0,0 +1,21 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: ssmis_f17 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: ssmis_f17 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-24 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_geoval_2020110112_m.nc4 + vector ref: GsiHofX + tolerance: 1.e-5 diff --git a/test/testinput/instrumentTests/ssmis/ssmis_f17_gfs_HofX_bc.yaml b/test/testinput/instrumentTests/ssmis/ssmis_f17_gfs_HofX_bc.yaml new file mode 100644 index 000000000..8c24f9bc6 --- /dev/null +++ b/test/testinput/instrumentTests/ssmis/ssmis_f17_gfs_HofX_bc.yaml @@ -0,0 +1,63 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID ssmis_f17 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: ssmis_f17 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/ssmis_f17_hofx_2020110112.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-24 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_ssmis_f17.nc4 + variational bc: + predictors: + - name: constant + - name: cloud_liquid_water + options: + satellite: SSMIS + ch19h: 12 + ch19v: 13 + ch22v: 14 + ch37h: 15 + ch37v: 16 + ch91v: 17 + ch91h: 18 + - name: cosine_of_latitude_times_orbit_node + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &ssmis_f17_tlap Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_tlapmean.txt + - name: lapse_rate + options: + tlapse: *ssmis_f17_tlap + - name: emissivity + - name: scan_angle + options: + var_name: scan_position + order: 4 + - name: scan_angle + options: + var_name: scan_position + order: 3 + - name: scan_angle + options: + var_name: scan_position + order: 2 + - name: scan_angle + options: + var_name: scan_position + vector ref: GsiHofXBc + tolerance: 1.e-5 diff --git a/test/testinput/instrumentTests/ssmis/ssmis_f17_gfs_HofX_qc.yaml b/test/testinput/instrumentTests/ssmis/ssmis_f17_gfs_HofX_qc.yaml new file mode 100644 index 000000000..0b71ce802 --- /dev/null +++ b/test/testinput/instrumentTests/ssmis/ssmis_f17_gfs_HofX_qc.yaml @@ -0,0 +1,277 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID ssmis_f17 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: ssmis_f17 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/ssmis_f17_hofx_2020110112.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-24 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_ssmis_f17.nc4 + variational bc: + predictors: + - name: constant + - name: cloud_liquid_water + options: + satellite: SSMIS + ch19h: 12 + ch19v: 13 + ch22v: 14 + ch37h: 15 + ch37v: 16 + ch91v: 17 + ch91h: 18 + - name: cosine_of_latitude_times_orbit_node + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &ssmis_f17_tlap Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_tlapmean.txt + - name: lapse_rate + options: + tlapse: *ssmis_f17_tlap + - name: emissivity + - name: scan_angle + options: + var_name: scan_position + order: 4 + - name: scan_angle + options: + var_name: scan_position + order: 3 + - name: scan_angle + options: + var_name: scan_position + order: 2 + - name: scan_angle + options: + var_name: scan_position + obs filters: +#step1: Gross check (setuprad) + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *channels + threshold: 1.5 + action: + name: reject +#step1: Gross check(qcmod) + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *channels + absolute threshold: 3.5 + bias correction parameter: 1.0 + action: + name: reject +# #step2: clw check +# Keep the CLW check in yaml for further improvement. +# The test case using 2020110112 global SSMIS data shows that CLW check is not activated in GSI. + # - filter: Bounds Check + # filter variables: + # - name: brightness_temperature + # channels: 1 + # test variables: + # - name: CLWRetMW_SSMIS@ObsFunction + # options: + # satellite: SSMIS + # ch19h: 12 + # ch19v: 13 + # ch22v: 14 + # ch37h: 15 + # ch37v: 16 + # ch91v: 17 + # ch91h: 18 + # varGroup: ObsValue +# # minvalue: 0.0 + # maxvalue: 100 + # where: + # - variable: + # name: water_area_fraction@GeoVaLs + # minvalue: 0.99 + # action: + # name: reject +# #ch2 + # - filter: Bounds Check + # filter variables: + # - name: brightness_temperature + # channels: 2 + # test variables: + # - name: CLWRetMW_SSMIS@ObsFunction + # options: + # satellite: SSMIS + # ch19h: 12 + # ch19v: 13 + # ch22v: 14 + # ch37h: 15 + # ch37v: 16 + # ch91v: 17 + # ch91h: 18 + # varGroup: ObsValue + # minvalue: 0.0 + # maxvalue: 0.2 + # where: + # - variable: + # name: water_area_fraction@GeoVaLs + # minvalue: 0.99 + # action: + # name: reject +# #ch3 + # - filter: Bounds Check + # filter variables: + # - name: brightness_temperature + # channels: 3 + # test variables: + # - name: CLWRetMW_SSMIS@ObsFunction + # options: + # satellite: SSMIS + # ch19h: 12 + # ch19v: 13 + # ch22v: 14 + # ch37h: 15 + # ch37v: 16 + # ch91v: 17 + # ch91h: 18 + # varGroup: ObsValue + # minvalue: 0.0 + # maxvalue: 0.6 + # where: + # - variable: + # name: water_area_fraction@GeoVaLs + # minvalue: 0.99 + # action: + # name: reject +# #ch4-7 + # - filter: Bounds Check + # filter variables: + # - name: brightness_temperature + # channels: 4-7 + # test variables: + # - name: CLWRetMW_SSMIS@ObsFunction + # options: + # satellite: SSMIS + # ch19h: 12 + # ch19v: 13 + # ch22v: 14 + # ch37h: 15 + # ch37v: 16 + # ch91v: 17 + # ch91h: 18 + # varGroup: ObsValue + # minvalue: 0.0 + # maxvalue: 2.0 + # where: + # - variable: + # name: water_area_fraction@GeoVaLs + # minvalue: 0.99 + # action: + # name: reject +# #ch8-18 +# #ch19-24 + # - filter: Bounds Check + # filter variables: + # - name: brightness_temperature + # channels: 19-24 + # test variables: + # - name: CLWRetMW_SSMIS@ObsFunction + # options: + # satellite: SSMIS + # ch19h: 12 + # ch19v: 13 + # ch22v: 14 + # ch37h: 15 + # ch37v: 16 + # ch91v: 17 + # ch91h: 18 + # varGroup: ObsValue + # minvalue: 0.0 + # maxvalue: 10.0 + # where: + # - variable: + # name: water_area_fraction@GeoVaLs + # minvalue: 0.99 + # action: + # name: reject +#step3: + - filter: Difference Check + filter variables: + - name: brightness_temperature + channels: 1-2,12-16 + reference: brightness_temperature_2@ObsValue + value: brightness_temperature_2@HofX + minvalue: -1.5 + maxvalue: 1.5 + where: + - variable: + name: water_area_fraction@GeoVaLs + maxvalue: 0.99 +#QC_terrain: If seviri and terrain height > 2km. do not use + - filter: Domain Check + filter variables: + - name: brightness_temperature + channels: *channels + where: + - variable: + name: height_above_mean_sea_level@MetaData + maxvalue: 2000.0 +#do not use over mixed surface + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: 1-3,8-18 + where: + - variable: + name: land_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: water_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: ice_area_fraction@GeoVaLs + maxvalue: 0.99 + - variable: + name: surface_snow_area_fraction@GeoVaLs + maxvalue: 0.99 +#step4: Generate q.c. bounds and modified variances + - filter: BlackList + filter variables: + - name: brightness_temperature + channels: *channels + action: + name: inflate error + inflation variable: +# Surface Jacobian check + name: ObsErrorFactorSurfJacobianRad@ObsFunction + channels: *channels + options: + channels: *channels + obserr_demisf: [0.010, 0.010, 0.010, 0.010, 0.010] + obserr_dtempf: [0.500, 0.500, 0.500, 0.500, 0.500] +# Useflag Check + - filter: Bounds Check + filter variables: + - name: brightness_temperature + channels: *channels + test variables: + - name: ChannelUseflagCheckRad@ObsFunction + channels: *channels + options: + channels: *channels + use_flag: [ 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1] + minvalue: 1.0e-12 + action: + name: reject + passedBenchmark: 440 #(m_unittest) diff --git a/test/testinput/interpolate_data_from_file_predictor.yaml b/test/testinput/interpolate_data_from_file_predictor.yaml new file mode 100644 index 000000000..660f58cd7 --- /dev/null +++ b/test/testinput/interpolate_data_from_file_predictor.yaml @@ -0,0 +1,48 @@ +window begin: 2010-01-01T00:00:00Z +window end: 2030-01-01T00:00:00Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s_interpolate_data_from_file_predictors.nc4 + simulated variables: [air_temperature, eastward_wind, northward_wind, specific_humidity] + obs bias: + static bc: + predictors: + - name: interpolate_data_from_file + options: + corrected variables: + - name: air_temperature + file: Data/ufo/testinput_tier_1/bias_interpolation_air_temperature.nc4 + interpolation: + - name: station_id@MetaData + method: exact + - name: specific_humidity + file: Data/ufo/testinput_tier_1/bias_interpolation_specific_humidity.nc4 + interpolation: + - name: station_id@MetaData + method: exact + tolerance: 1.0e-16 +- obs space: + name: Satellite + obsdatain: + obsfile: Data/ufo/testinput_tier_1/mhs_metop-b_obs_2018041500_m_interpolate_date_from_file_predictors.nc4 + simulated variables: [brightness_temperature] + channels: &channels 2-4 + obs bias: + static bc: + predictors: + - name: interpolate_data_from_file + options: + corrected variables: + - name: brightness_temperature + channels: *channels + file: Data/ufo/testinput_tier_1/bias_interpolation_brightness_temperature.nc4 + interpolation: + - name: scan_position@MetaData + method: nearest # In practice, 'exact' would probably be used. 'nearest' is used + # here to reduce the size of the NetCDF file used by this test. + # The predictor's output will depend on the scan position and also (because the + # 'channels' option is set) on the channel number + tolerance: 1.0e-16 diff --git a/test/testinput/legendre_predictor.yaml b/test/testinput/legendre_predictor.yaml new file mode 100644 index 000000000..719cc54df --- /dev/null +++ b/test/testinput/legendre_predictor.yaml @@ -0,0 +1,34 @@ +window begin: 2019-12-29T21:00:00Z +window end: 2019-12-30T03:00:00Z + +observations: +- obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_n20_obs_20191230T0000_rttov_biaspred_redo_scan.nc4 + obsdataout: + obsfile: Data/atms_npp_obs_2019123000_m_rttov_out_bias.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/geovals_atms_20191230T0000Z_benchmark.nc4 + obs bias: + variational bc: + predictors: + - name: Legendre + number of scan positions: 32 + options: + order: 1 + - name: Legendre + number of scan positions: 32 + options: + order: 2 + - name: Legendre + number of scan positions: 32 + options: + order: 3 + - name: Legendre + number of scan positions: 32 + options: + order: 4 + tolerance: 1.0e-6 diff --git a/test/testinput/locations.yaml b/test/testinput/locations.yaml index 8e0be0a64..16ea5caee 100644 --- a/test/testinput/locations.yaml +++ b/test/testinput/locations.yaml @@ -1,7 +1,41 @@ window begin: 2018-04-14T21:00:00Z window end: 2018-04-15T03:00:00Z obs space: - name: Radiosonde - obsdatain: - obsfile: Data/ioda/testinput_tier_1/sondes_obs_2018041500_s.nc4 - simulated variables: [air_temperature] + name: test data + simulated variables: [test] + generate: + list: + lons: [ 0.0, 10.0, 20.0, 50.0, 90.0, 100.0, 200.0, 300.0, 359.0, 0.0 ] + lats: [ 0.0, 10.0, 20.0, 80.0, 90.0, -10.0, -20.0, -80.0, -90.0, 0.0 ] + datetimes: + - 2018-04-14T21:50:00Z + - 2018-04-14T22:04:00Z + - 2018-04-14T21:10:00Z + - 2018-04-14T23:00:00Z + - 2018-04-15T00:00:00Z + - 2018-04-15T01:00:00Z + - 2018-04-15T02:00:00Z + - 2018-04-15T03:00:00Z + - 2018-04-15T02:30:00Z + - 2018-04-14T23:59:00Z + obs errors: [1.0] + +locations test: + reference lons: [ 0.0, 10.0, 20.0, 50.0, 90.0, 100.0, 200.0, 300.0, 359.0, 0.0 ] + reference lats: [ 0.0, 10.0, 20.0, 80.0, 90.0, -10.0, -20.0, -80.0, -90.0, 0.0 ] + time mask tests: + - t1: 2018-04-14T21:00:00Z + t2: 2018-04-15T03:00:00Z + reference mask: [ true, true, true, true, true, true, true, true, true, true ] + - t1: 2018-04-14T00:00:00Z + t2: 2018-04-14T03:00:00Z + reference mask: [ false, false, false, false, false, false, false, false, false, false ] + - t1: 2018-04-15T01:00:00Z + t2: 2018-04-15T01:00:59Z + reference mask: [ false, false, false, false, false, false, false, false, false, false ] + - t1: 2018-04-14T21:00:00Z + t2: 2018-04-15T00:00:00Z + reference mask: [ true, true, true, true, true, false, false, false, false, true ] + - t1: 2018-04-15T00:00:00Z + t2: 2018-04-15T03:00:00Z + reference mask: [ false, false, false, false, false, true, true, true, true, false ] diff --git a/test/testinput/metar_qc_filters.yaml b/test/testinput/metar_qc_filters.yaml index d6f28fffb..f77087e4d 100644 --- a/test/testinput/metar_qc_filters.yaml +++ b/test/testinput/metar_qc_filters.yaml @@ -5,7 +5,7 @@ observations: - obs space: name: surface_METAR obsdatain: - obsfile: Data/ioda/testinput_tier_1/sfc_obs_2018041500_metars_small.nc + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small.nc obsdataout: obsfile: Data/sfc_obs_2018041500_metars_small_out.nc simulated variables: [air_temperature, surface_pressure, specific_humidity, eastward_wind, northward_wind] @@ -30,8 +30,8 @@ observations: - filter: Bounds Check filter variables: - name: specific_humidity - minvalue: 1.E-7 - maxvalue: 0.34999999 + minvalue: 1.0E-7 + maxvalue: 0.034999999 action: name: reject # passedBenchmark: 165 (nothing outside the range) @@ -89,11 +89,54 @@ observations: action: name: assign error error parameter: 1.4 # 1.4 m/s +# The next series of filters assigns obsError as a linear interpolation from steps +# in another variable, such as pressure/altitude. + - filter: Bounds Check + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: surface_pressure@ObsValue + xvals: [10000, 20000, 25000, 30000, 50000, 85000, 97500, 100000] #Pressure (Pa), ascending order + errors: [3.0, 2.0, 1.5, 1.5, 2.0, 2.5, 2.5, 3.0] # - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: surface_pressure@ObsValue + xvals: [100000, 95000, 80000, 65000, 60000, 55000, 50000, 45000, 40000, 35000, 30000, 25000, 20000, 15000, 10000] #Pressure (Pa) + errors: [1.4, 1.5, 1.6, 1.8, 1.9, 2.0, 2.1, 2.3, 2.6, 2.8, 3.0, 3.2, 2.7, 2.4, 2.1] +# + - filter: BlackList filter variables: - name: specific_humidity action: name: assign error - error parameter: 0.0001 # 0.1 g/kg + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + scale_factor_var: specific_humidity@ObsValue + xvar: + name: surface_pressure@ObsValue + xvals: [110000, 105000, 100000, 95000, 90000, 85000, 80000, 75000, 70000, 65000, 60000, 55000, + 50000, 45000, 40000, 35000, 30000, 25000, 20000, 15000, 10000, 7500, 5000, 4000, 3000] + errors: [.19455, .19062, .18488, .17877, .17342, .16976, .16777, .16696, .16605, .16522, .16637, .17086, + .17791, .18492, .18996, .19294, .19447, .19597, .19748, .19866, .19941, .19979, .19994, .19999, .2] + compareVariables: + - reference: + name: specific_humidity_obserror@TestReference + test: + name: specific_humidity@EffectiveError + absTol: 1.0e-6 passedBenchmark: 161 diff --git a/test/testinput/metoffice_radiance_error_matrices.yaml b/test/testinput/metoffice_radiance_error_matrices.yaml new file mode 100644 index 000000000..5835d2f8f --- /dev/null +++ b/test/testinput/metoffice_radiance_error_matrices.yaml @@ -0,0 +1,20 @@ +MetOfficeRadianceErrorMatrices: + BMatrix: ../resources/bmatrix/rttov/atms_bmatrix_70_test.dat + background fields: + - air_temperature + - specific_humidity + - mass_content_of_cloud_liquid_water_in_atmosphere_layer + - mass_content_of_cloud_ice_in_atmosphere_layer + - surface_temperature + - specific_humidity_at_two_meters_above_surface + - skin_temperature + - air_pressure_at_two_meters_above_surface + qtotal: true + RMatrix: ../resources/rmatrix/rttov/atms_noaa_20_rmatrix_test.nc4 + channels: 18,20,21 + latitude: -60.0 + nelements: 144 + BHT_value: 31.3266620636 + HBHT_value: 594.102966309 + HBHT_R_value: 594.232543954 + tol: 1e-4 diff --git a/test/testinput/mhs_crtm.yaml b/test/testinput/mhs_crtm.yaml index 2fa5429d8..96627f6fb 100644 --- a/test/testinput/mhs_crtm.yaml +++ b/test/testinput/mhs_crtm.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv linear obs operator: Absorbers: [H2O,O3,CO2] obs options: @@ -14,7 +15,7 @@ observations: obs space: name: mhs_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/mhs_n19_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/mhs_n19_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 1-5 geovals: diff --git a/test/testinput/obsdiag_background_error_identity.yaml b/test/testinput/obsdiag_background_error_identity.yaml new file mode 100644 index 000000000..f4fc9d4e9 --- /dev/null +++ b/test/testinput/obsdiag_background_error_identity.yaml @@ -0,0 +1,24 @@ +# Verifies that the BackgroundErrorVertInterp obs operator produces correct obs diagnostics +# when using a "linear" vertical coordinate (height). + +window begin: 2018-04-14T20:00:00Z +window end: 2018-04-15T03:00:00Z +obs space: + name: Sondes + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [surface_pressure] +obs operator: + name: Composite + components: + # operator used to evaluate H(x) + - name: Identity + # operator used to evaluate background errors + - name: BackgroundErrorIdentity +geovals: + filename: Data/ufo/testinput_tier_1/sondes_background_error_vert_interp_height_geoval_2018041500_s.nc4 +obs diagnostics: + variables: [surface_pressure_background_error] +reference obs diagnostics: + filename: Data/ufo/testinput_tier_1/sondes_background_error_vert_interp_height_obsdiag_2018041500_s.nc4 +tolerance: 1.e-6 diff --git a/test/testinput/obsdiag_background_error_upper_air_and_surface_variables.yaml b/test/testinput/obsdiag_background_error_upper_air_and_surface_variables.yaml new file mode 100644 index 000000000..9a9b21564 --- /dev/null +++ b/test/testinput/obsdiag_background_error_upper_air_and_surface_variables.yaml @@ -0,0 +1,45 @@ +# Verifies that the BackgroundErrorVertInterp and BackgroundErrorIdentity obs operators can be used +# together, each evaluating background error estimates of different variables. + +window begin: 2018-04-14T20:00:00Z +window end: 2018-04-15T03:00:00Z +obs space: + name: Sondes + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [air_temperature, specific_humidity, surface_pressure, northward_wind, eastward_wind] +obs operator: + name: Composite + components: + # operators used to evaluate H(x) + - name: VertInterp + variables: + - name: air_temperature + - name: specific_humidity + - name: northward_wind + - name: eastward_wind + - name: Identity + variables: + - name: surface_pressure + # operators used to evaluate background errors + - name: BackgroundErrorVertInterp + variables: + - name: northward_wind + - name: eastward_wind + - name: air_temperature + - name: specific_humidity + observation vertical coordinate: air_pressure + vertical coordinate: background_error_air_pressure + - name: BackgroundErrorIdentity + variables: + - name: surface_pressure +geovals: + filename: Data/ufo/testinput_tier_1/sondes_background_error_vert_interp_air_pressure_geoval_2018041500_s.nc4 +obs diagnostics: + variables: + - northward_wind_background_error + - surface_pressure_background_error + - specific_humidity_background_error +reference obs diagnostics: + filename: Data/ufo/testinput_tier_1/sondes_background_error_vert_interp_air_pressure_obsdiag_2018041500_s.nc4 +tolerance: 1.e-6 diff --git a/test/testinput/obsdiag_background_error_vert_interp_air_pressure.yaml b/test/testinput/obsdiag_background_error_vert_interp_air_pressure.yaml new file mode 100644 index 000000000..d997f244e --- /dev/null +++ b/test/testinput/obsdiag_background_error_vert_interp_air_pressure.yaml @@ -0,0 +1,26 @@ +# Verifies that the BackgroundErrorVertInterp obs operator produces correct obs diagnostics +# when using a "logarithmic" vertical coordinate (air pressure). + +window begin: 2018-04-14T20:00:00Z +window end: 2018-04-15T03:00:00Z +obs space: + name: Sondes + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [air_temperature, specific_humidity, northward_wind, eastward_wind] +obs operator: + name: Composite + components: + # operator used to evaluate H(x) + - name: VertInterp + # operator used to evaluate background errors + - name: BackgroundErrorVertInterp + observation vertical coordinate: air_pressure + vertical coordinate: background_error_air_pressure +geovals: + filename: Data/ufo/testinput_tier_1/sondes_background_error_vert_interp_air_pressure_geoval_2018041500_s.nc4 +obs diagnostics: + variables: [northward_wind_background_error, specific_humidity_background_error] +reference obs diagnostics: + filename: Data/ufo/testinput_tier_1/sondes_background_error_vert_interp_air_pressure_obsdiag_2018041500_s.nc4 +tolerance: 1.e-6 diff --git a/test/testinput/obsdiag_background_error_vert_interp_height.yaml b/test/testinput/obsdiag_background_error_vert_interp_height.yaml new file mode 100644 index 000000000..9712ab735 --- /dev/null +++ b/test/testinput/obsdiag_background_error_vert_interp_height.yaml @@ -0,0 +1,26 @@ +# Verifies that the BackgroundErrorVertInterp obs operator produces correct obs diagnostics +# when using a "linear" vertical coordinate (height). + +window begin: 2018-04-14T20:00:00Z +window end: 2018-04-15T03:00:00Z +obs space: + name: Sondes + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [air_temperature, specific_humidity, northward_wind, eastward_wind] +obs operator: + name: Composite + components: + # operator used to evaluate H(x) + - name: VertInterp + # operator used to evaluate background errors + - name: BackgroundErrorVertInterp + observation vertical coordinate: height + vertical coordinate: background_error_height +geovals: + filename: Data/ufo/testinput_tier_1/sondes_background_error_vert_interp_height_geoval_2018041500_s.nc4 +obs diagnostics: + variables: [northward_wind_background_error, specific_humidity_background_error] +reference obs diagnostics: + filename: Data/ufo/testinput_tier_1/sondes_background_error_vert_interp_height_obsdiag_2018041500_s.nc4 +tolerance: 1.e-6 diff --git a/test/testinput/obsdiag_crtm_airs_jacobian.yaml b/test/testinput/obsdiag_crtm_airs_jacobian.yaml index 57e692836..668298031 100644 --- a/test/testinput/obsdiag_crtm_airs_jacobian.yaml +++ b/test/testinput/obsdiag_crtm_airs_jacobian.yaml @@ -3,7 +3,7 @@ window end: 2018-04-15T03:00:00Z obs space: name: airs_aqua obsdatain: - obsfile: Data/ioda/testinput_tier_1/airs_aqua_obs_2018041500_m_unittest.nc4 + obsfile: Data/ufo/testinput_tier_1/airs_aqua_obs_2018041500_m_unittest.nc4 simulated variables: [brightness_temperature] channels: &all_channels 1, 6, 7, 10, 11, 15, 16, 17, 20, 21, 22, 24, @@ -31,6 +31,7 @@ obs space: obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: airs_aqua EndianType: little_endian diff --git a/test/testinput/obsdiag_crtm_airs_optics.yaml b/test/testinput/obsdiag_crtm_airs_optics.yaml index 792a1a7fe..56dda9af7 100644 --- a/test/testinput/obsdiag_crtm_airs_optics.yaml +++ b/test/testinput/obsdiag_crtm_airs_optics.yaml @@ -3,7 +3,7 @@ window end: 2018-04-15T03:00:00Z obs space: name: airs_aqua obsdatain: - obsfile: Data/ioda/testinput_tier_1/airs_aqua_obs_2018041500_m_unittest.nc4 + obsfile: Data/ufo/testinput_tier_1/airs_aqua_obs_2018041500_m_unittest.nc4 simulated variables: [brightness_temperature] channels: &all_channels 1, 6, 7, 10, 11, 15, 16, 17, 20, 21, 22, 24, @@ -31,6 +31,7 @@ obs space: obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: airs_aqua EndianType: little_endian diff --git a/test/testinput/obsdiag_crtm_amsua_jacobian.yaml b/test/testinput/obsdiag_crtm_amsua_jacobian.yaml index 447de85b1..7309cf641 100644 --- a/test/testinput/obsdiag_crtm_amsua_jacobian.yaml +++ b/test/testinput/obsdiag_crtm_amsua_jacobian.yaml @@ -3,7 +3,7 @@ window end: 2018-04-15T03:00:00Z obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 simulated variables: [brightness_temperature] channels: &all_channels 1-15 obs operator: @@ -11,6 +11,7 @@ obs operator: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv obs options: Sensor_ID: amsua_n19 EndianType: little_endian diff --git a/test/testinput/obsdiag_crtm_amsua_optics.yaml b/test/testinput/obsdiag_crtm_amsua_optics.yaml index 7b94c902f..254b8890c 100644 --- a/test/testinput/obsdiag_crtm_amsua_optics.yaml +++ b/test/testinput/obsdiag_crtm_amsua_optics.yaml @@ -3,7 +3,7 @@ window end: 2018-04-15T03:00:00Z obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m_qc.nc4 simulated variables: [brightness_temperature] channels: &all_channels 1-15 obs operator: @@ -11,6 +11,7 @@ obs operator: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv obs options: Sensor_ID: amsua_n19 EndianType: little_endian diff --git a/test/testinput/obsdiag_crtm_atms_jacobian.yaml b/test/testinput/obsdiag_crtm_atms_jacobian.yaml index f636283d3..2b8d8def9 100644 --- a/test/testinput/obsdiag_crtm_atms_jacobian.yaml +++ b/test/testinput/obsdiag_crtm_atms_jacobian.yaml @@ -3,7 +3,7 @@ window end: 2018-04-15T03:00:00Z obs space: name: atms_npp obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 simulated variables: [brightness_temperature] channels: &all_channels 1-22 obs operator: @@ -11,6 +11,7 @@ obs operator: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv obs options: Sensor_ID: atms_npp EndianType: little_endian diff --git a/test/testinput/obsdiag_crtm_atms_optics.yaml b/test/testinput/obsdiag_crtm_atms_optics.yaml index fc0282f48..72c853f68 100644 --- a/test/testinput/obsdiag_crtm_atms_optics.yaml +++ b/test/testinput/obsdiag_crtm_atms_optics.yaml @@ -3,7 +3,7 @@ window end: 2018-04-15T03:00:00Z obs space: name: atms_npp obsdatain: - obsfile: Data/ioda/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 + obsfile: Data/ufo/testinput_tier_1/atms_npp_obs_2018041500_m_qc.nc4 simulated variables: [brightness_temperature] channels: &all_channels 1-22 obs operator: @@ -11,6 +11,7 @@ obs operator: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] Cloud_Fraction: 1.0 + SurfaceWindGeoVars: uv obs options: Sensor_ID: atms_npp EndianType: little_endian diff --git a/test/testinput/obsdiag_crtm_cris_jacobian.yaml b/test/testinput/obsdiag_crtm_cris_jacobian.yaml index bf04bd5c1..9d5b33ede 100644 --- a/test/testinput/obsdiag_crtm_cris_jacobian.yaml +++ b/test/testinput/obsdiag_crtm_cris_jacobian.yaml @@ -3,7 +3,7 @@ window end: 2018-04-15T03:00:00Z obs space: name: cris-fsr_npp obsdatain: - obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 + obsfile: Data/ufo/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 simulated variables: [brightness_temperature] channels: &all_channels 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, @@ -41,6 +41,7 @@ obs space: obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: cris-fsr_npp EndianType: little_endian diff --git a/test/testinput/obsdiag_crtm_cris_optics.yaml b/test/testinput/obsdiag_crtm_cris_optics.yaml index 2e40273bf..fdc6ada07 100644 --- a/test/testinput/obsdiag_crtm_cris_optics.yaml +++ b/test/testinput/obsdiag_crtm_cris_optics.yaml @@ -3,7 +3,7 @@ window end: 2018-04-15T03:00:00Z obs space: name: cris-fsr_npp obsdatain: - obsfile: Data/ioda/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 + obsfile: Data/ufo/testinput_tier_1/cris-fsr_npp_obs_2018041500_m_unittest.nc4 simulated variables: [brightness_temperature] channels: &all_channels 19, 24, 26, 27, 28, 31, 32, 33, 37, 39, 42, 44, 47, 49, 50, @@ -41,6 +41,7 @@ obs space: obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: cris-fsr_npp EndianType: little_endian diff --git a/test/testinput/obsdiag_crtm_iasi_jacobian.yaml b/test/testinput/obsdiag_crtm_iasi_jacobian.yaml index d09243ba8..be01dcbf1 100644 --- a/test/testinput/obsdiag_crtm_iasi_jacobian.yaml +++ b/test/testinput/obsdiag_crtm_iasi_jacobian.yaml @@ -3,7 +3,7 @@ window end: 2018-04-15T03:00:00Z obs space: name: iasi_metop-a obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 simulated variables: [brightness_temperature] channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, @@ -57,6 +57,7 @@ obs space: obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: iasi_metop-a EndianType: little_endian diff --git a/test/testinput/obsdiag_crtm_iasi_optics.yaml b/test/testinput/obsdiag_crtm_iasi_optics.yaml index af6410768..e7d85fd59 100644 --- a/test/testinput/obsdiag_crtm_iasi_optics.yaml +++ b/test/testinput/obsdiag_crtm_iasi_optics.yaml @@ -3,7 +3,7 @@ window end: 2018-04-15T03:00:00Z obs space: name: iasi_metop-a obsdatain: - obsfile: Data/ioda/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 + obsfile: Data/ufo/testinput_tier_1/iasi_metop-a_obs_2018041500_m_unittest.nc4 simulated variables: [brightness_temperature] channels: &all_channels 16, 29, 32, 35, 38, 41, 44, 47, 49, 50, 51, 53, 55, 56, 57, 59, 61, 62, 63, 66, 68, 70, 72, 74, 76, 78, 79, 81, 82, 83, @@ -57,6 +57,7 @@ obs space: obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: iasi_metop-a EndianType: little_endian diff --git a/test/testinput/obserror_assign_unittests.yaml b/test/testinput/obserror_assign_unittests.yaml new file mode 100644 index 000000000..8dd0c7d37 --- /dev/null +++ b/test/testinput/obserror_assign_unittests.yaml @@ -0,0 +1,1096 @@ +Assign constant observation error value: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + air_pressure: [ 100000, 90000, 80000, 90000, 100000] + station_id: [ 1, 1, 1, 1, 1] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error parameter: 1.2 + expected_obserror: [1.2, 1.2, 1.2, 1.2, 1.2] + +# Tests using the NetCDF backend + +Assign observation error from NetCDF variance; 1 exact; 1 linear (along the first axis): + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + air_pressure: [ 2, 4, 6, 8, 10] + observation_type: [0, 1, 2, 3, 4] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multi_variant_extract_variance.nc + interpolation: + - name: observation_type@MetaData + method: exact + - name: air_pressure@MetaData + method: linear + expected_obserror_variance: [375.5, 601, 242.5, 175, 337] +Assign observation error from NetCDF variance; 1 nearest; 1 linear (along the second axis): + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -6, -6, -6, -4 ] + lons: [ 30, 35, 40, 45 ] + datetimes: [ '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z' ] + obs errors: [1.0] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_latitude_longitude.nc + interpolation: + - name: latitude@MetaData + method: nearest + - name: longitude@MetaData + method: linear + expected_obserror_variance: [1, 1.5, 2, 5.5] +Assign observation error from NetCDF variance; 1 exact; 1 nearest: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + air_pressure: [ 1, 2, 4, 13, 35] + observation_type: [0, 1, 2, 3, 4] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multi_variant_extract_variance.nc + interpolation: + - name: observation_type@MetaData + method: exact + - name: air_pressure@MetaData + method: nearest + expected_obserror_variance: [278, 755, 397, 846, 676] +Assign observation error from NetCDF full matrix; 3 exact; 1 nearest: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + satellite_id: [ 0, 0, 1, 1, 1 ] + processing_center: [ 0, 1, 0, 1, 0 ] + latitude_band: [ 6, 11, 4, 30, 7 ] + channel_number: [ 0, 1, 2, 3, 4 ] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multi_variant_with_extract_fullr.nc + interpolation: + - name: channel_number@MetaData + method: exact + - name: satellite_id@MetaData + method: exact + - name: processing_center@MetaData + method: exact + - name: latitude_band@MetaData + method: nearest + expected_obserror_variance: [685, 431, 771, 25, 805] +Assign observation error from NetCDF full matrix; 3 exact; 1 nearest; demonstrating yaml order respected with sorting: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + satellite_id: [ 0, 0, 1, 1, 1 ] + processing_center: [ 0, 1, 0, 1, 0 ] + latitude_band: [ 6, 11, 4, 30, 7 ] + channel_number: [ 0, 1, 2, 3, 4 ] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multi_variant_with_extract_fullr.nc + interpolation: + - name: processing_center@MetaData + method: exact + - name: satellite_id@MetaData + method: exact + - name: latitude_band@MetaData + method: nearest + - name: channel_number@MetaData + method: exact + expected_obserror_variance: [685, 431, 771, 25, 805] +Assign observation error from NetCDF full matrix with ioda-v2-style variable names; 3 exact; 1 nearest; demonstrating yaml order respected with sorting: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + satellite_id: [ 0, 0, 1, 1, 1 ] + processing_center: [ 0, 1, 0, 1, 0 ] + latitude_band: [ 6, 11, 4, 30, 7 ] + channel_number: [ 0, 1, 2, 3, 4 ] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multi_variant_with_extract_fullr_v2.nc + interpolation: + - name: processing_center@MetaData + method: exact + - name: satellite_id@MetaData + method: exact + - name: latitude_band@MetaData + method: nearest + - name: channel_number@MetaData + method: exact + expected_obserror_variance: [685, 431, 771, 25, 805] +Assign observation error from NetCDF full matrix 2D; 1 exact: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + channel_number: [ 2, 0, 4, 7, 5 ] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_2d_fullr.nc + interpolation: + - name: channel_number@MetaData + method: exact + expected_obserror_variance: [88, 685, 798, 618, 544] +Assign observation error from NetCDF variance; 3 exact (1 string, 1 datetime): + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z' ] + obs errors: [1.0] + air_pressure: [2, 4, 6, 8, 9] + StringVar: ['aaa', 'aaa', 'bbb', 'ccc', 'ccc'] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multi_variant_extract_variance_alt.nc + interpolation: + - name: StringVar@MetaData + method: exact + - name: air_pressure@MetaData + method: exact + - name: datetime@MetaData + method: exact + expected_obserror_variance: [278, 487, 917, 178, 289] + Assign observation error from NetCDF file; data types float, int and string; missing values: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -10, -10, -10, -10, 10, 10, 10, 10 ] + lons: [ 30, 60, 30, 60, 30, 60, 30, 60 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z' ] + obs errors: [1.0] + air_pressure: [1000, 1000, 100000, 100000, 1000, 1000, 100000, 100000] + StringVar: [south, south, south, south, north, north, north, north] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_stringvar_pressure_longitude_missing_values.nc + interpolation: + - name: StringVar@MetaData + method: exact + - name: air_pressure@MetaData + method: exact + - name: longitude@MetaData + method: exact + expected_obserror_variance: [1, 2, 3, 4, 5, 6, 7, 8] +Assign observation error from NetCDF variance; attempt at using 'nearest' for strings: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z' ] + obs errors: [1.0] + air_pressure: [2, 4, 6, 8, 9] + StringVar: ['aaa', 'aaa', 'bbb', 'ccc', 'ccc'] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multi_variant_extract_variance_alt.nc + interpolation: + - name: StringVar@MetaData + method: nearest + expectExceptionWithMessage: Nearest match not compatible with string type +Assign observation error from NetCDF variance; attempt at using 'linear' for strings: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z' ] + obs errors: [1.0] + air_pressure: [2, 4, 6, 8, 9] + StringVar: ['aaa', 'aaa', 'bbb', 'ccc', 'ccc'] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multi_variant_extract_variance_alt.nc + interpolation: + - name: StringVar@MetaData + method: linear + expectExceptionWithMessage: linear interpolation not compatible with string type +Assign observation error from NetCDF variance; 1 least upper bound; 1 exact: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2, 3 ] + lons: [ 178, 179, 180, 181, 182, 183 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + air_pressure: [ 300, 300, 300, 301, 301, 301] + station_id: [0, 1, 2, 0, 1, 2] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_lower_upper_bounds.nc + interpolation: + - name: air_pressure@MetaData + method: least upper bound + - name: station_id@MetaData + method: exact + expected_obserror_variance: [1, 2, 3, 4, 5, 6] +Assign observation error from NetCDF variance; 1 greatest lower bound; 1 exact: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2, 3 ] + lons: [ 178, 179, 180, 181, 182, 183 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + air_pressure: [ 599, 599, 599, 600, 600, 600] + station_id: [0, 1, 2, 0, 1, 2] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_lower_upper_bounds.nc + interpolation: + - name: air_pressure@MetaData + method: greatest lower bound + - name: station_id@MetaData + method: exact + expected_obserror_variance: [1, 2, 3, 4, 5, 6] +Assign observation error from NetCDF variance; no match found for an 'exact' variable: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + air_pressure: [ 2, 4, 6, 8, 10] + observation_type: [1000, 1, 2, 3, 4] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multi_variant_extract_variance.nc + interpolation: + - name: observation_type@MetaData + method: exact + - name: air_pressure@MetaData + method: linear + expectExceptionWithMessage: No match found for exact match extraction of value '1000' of the variable 'observation_type@MetaData' +Assign observation error from NetCDF variance; no match found for a 'least upper bound' variable: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2, 3 ] + lons: [ 178, 179, 180, 181, 182, 183 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + air_pressure: [ 300, 300, 10000, 301, 301, 301] + station_id: [0, 1, 2, 0, 1, 2] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_lower_upper_bounds.nc + interpolation: + - name: air_pressure@MetaData + method: least upper bound + - name: station_id@MetaData + method: exact + expectExceptionWithMessage: No match found for 'least upper bound' extraction of value '10000' of the variable 'air_pressure@MetaData' +Assign observation error from NetCDF variance; no match found for a 'greatest lower bound' variable: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2, 3 ] + lons: [ 178, 179, 180, 181, 182, 183 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0] + air_pressure: [ 599, 599, 100, 600, 600, 600] + station_id: [0, 1, 2, 0, 1, 2] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_lower_upper_bounds.nc + interpolation: + - name: air_pressure@MetaData + method: greatest lower bound + - name: station_id@MetaData + method: exact + expectExceptionWithMessage: No match found for 'greatest lower bound' extraction of value '100' of the variable 'air_pressure@MetaData' +Assign observation error from NetCDF variance; attempt to use the 'least upper bound' mode for a string variable: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z' ] + obs errors: [1.0] + air_pressure: [2, 4, 6, 8, 9] + StringVar: ['aaa', 'aaa', 'bbb', 'ccc', 'ccc'] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multi_variant_extract_variance_alt.nc + interpolation: + - name: StringVar@MetaData + method: least upper bound + expectExceptionWithMessage: The 'least upper bound' method cannot be used for string variables +Assign observation error from NetCDF variance; attempt to use the 'greatest lower bound' mode for a string variable: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z' ] + obs errors: [1.0] + air_pressure: [2, 4, 6, 8, 9] + StringVar: ['aaa', 'aaa', 'bbb', 'ccc', 'ccc'] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multi_variant_extract_variance_alt.nc + interpolation: + - name: StringVar@MetaData + method: greatest lower bound + expectExceptionWithMessage: The 'greatest lower bound' method cannot be used for string variables +Assign observation error from NetCDF variance; multi-channel variable support: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [brightness_temperature] + channels: &all_channels 1-2 + generate: + list: + lats: [ -2, -1, 0, 1, 2 ] + lons: [ 178, 179, 180, 181, 182 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z' ] + obs errors: [1.0, 1.0] + satellite_id: [ 0, 0, 1, 1, 1 ] + processing_center: [ 0, 1, 0, 1, 0 ] + latitude_band: [ 6, 11, 4, 30, 7 ] + ObsError assign: + filter variables: + - name: brightness_temperature + channels: *all_channels + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + channels: *all_channels + options: + channels: *all_channels + file: Data/ufo/testinput_tier_1/obserror_multi_variant_with_extract_fullr.nc + interpolation: + - name: satellite_id@MetaData + method: exact + - name: processing_center@MetaData + method: exact + - name: latitude_band@MetaData + method: nearest +# python-iris debug command: et = cube[2, 2, 5]; meta=[f"{coord.name()}: {coord.points[0]}" for coord in et.coords()]; print(f"data={et.data} {meta}") +# data[1,1,0]=871 ['channel_number: 1', 'channel_number@MetaData: 1', 'index: 0', 'latitude_band@MetaData: 5', 'processing_center@MetaData: 0', 'satellite_id@MetaData: 0'] +# data[1,1,3]=431 ['channel_number: 1', 'channel_number@MetaData: 1', 'index: 3', 'latitude_band@MetaData: 15', 'processing_center@MetaData: 1', 'satellite_id@MetaData: 0'] +# data[1,1,4]=83 ['channel_number: 1', 'channel_number@MetaData: 1', 'index: 4', 'latitude_band@MetaData: 3', 'processing_center@MetaData: 0', 'satellite_id@MetaData: 1'] +# data[1,1,7]=399 ['channel_number: 1', 'channel_number@MetaData: 1', 'index: 7', 'latitude_band@MetaData: 46', 'processing_center@MetaData: 1', 'satellite_id@MetaData: 1'] +# data[1,1,5]=92 ['channel_number: 1', 'channel_number@MetaData: 1', 'index: 5', 'latitude_band@MetaData: 8', 'processing_center@MetaData: 0', 'satellite_id@MetaData: 1'] +# data[2,2,0]=87.0 ['channel_number: 2', 'channel_number@MetaData: 2', 'index: 0', 'latitude_band@MetaData: 5', 'processing_center@MetaData: 0', 'satellite_id@MetaData: 0'] +# data[2,2,3]=12.0 ['channel_number: 2', 'channel_number@MetaData: 2', 'index: 3', 'latitude_band@MetaData: 15', 'processing_center@MetaData: 1', 'satellite_id@MetaData: 0'] +# data[2,2,4]=771.0 ['channel_number: 2', 'channel_number@MetaData: 2', 'index: 4', 'latitude_band@MetaData: 3', 'processing_center@MetaData: 0', 'satellite_id@MetaData: 1'] +# data[2,2,7]=33.0 ['channel_number: 2', 'channel_number@MetaData: 2', 'index: 7', 'latitude_band@MetaData: 46', 'processing_center@MetaData: 1', 'satellite_id@MetaData: 1'] +# data[2,2,5]=308.0 ['channel_number: 2', 'channel_number@MetaData: 2', 'index: 5', 'latitude_band@MetaData: 8', 'processing_center@MetaData: 0', 'satellite_id@MetaData: 1'] + expected_obserror_variance: [871, 431, 83, 399, 92, 87, 12, 771, 33, 308] + +# Tests using the CSV backend + +Assign observation error from CSV file; data types float, int and string; payload of type float: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -10, -10, -10, -10, 10, 10, 10, 10 ] + lons: [ 31, 59, 31, 59, 31, 59, 31, 59 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z' ] + obs errors: [1.0] + air_pressure: [10, 10, 90000, 90000, 10, 10, 90000, 90000] + StringVar: [south, south, south, south, north, north, north, north] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_stringvar_pressure_longitude.csv + interpolation: + - name: StringVar@MetaData + method: exact + - name: air_pressure@MetaData + method: nearest + - name: longitude@MetaData + method: nearest + expected_obserror_variance: [1, 2, 3, 4, 5, 6, 7, 8] +Assign observation error from CSV file with ioda-v2-style variable names; data types float, int and string; payload of type float: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -10, -10, -10, -10, 10, 10, 10, 10 ] + lons: [ 31, 59, 31, 59, 31, 59, 31, 59 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z' ] + obs errors: [1.0] + air_pressure: [10, 10, 90000, 90000, 10, 10, 90000, 90000] + StringVar: [south, south, south, south, north, north, north, north] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_stringvar_pressure_longitude_v2.csv + interpolation: + - name: StringVar@MetaData + method: exact + - name: air_pressure@MetaData + method: nearest + - name: longitude@MetaData + method: nearest + expected_obserror_variance: [1, 2, 3, 4, 5, 6, 7, 8] +Assign observation error from CSV file; data types float, int and string; payload of type float; strings containing spaces: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -10, -10, -10, -10, 10, 10, 10, 10 ] + lons: [ 31, 59, 31, 59, 31, 59, 31, 59 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z' ] + obs errors: [1.0] + air_pressure: [10, 10, 90000, 90000, 10, 10, 90000, 90000] + StringVar: ["South America", "South America", "South America", "South America", + "North America", "North America", "North America", "North America"] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_spaces_in_stringvar.csv + interpolation: + - name: StringVar@MetaData + method: exact + - name: air_pressure@MetaData + method: nearest + - name: longitude@MetaData + method: nearest + expected_obserror_variance: [1, 2, 3, 4, 5, 6, 7, 8] +Assign observation error from CSV file; data type datetime; payload of type int: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 0, 0, 0 ] + lons: [ 0, 0, 0, 0 ] + datetimes: [ '2010-12-01T00:00:00Z', '2010-12-02T00:00:00Z', + '2010-01-03T12:00:00Z', '2010-01-04T00:00:00Z' ] + obs errors: [1.0] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_datetime.csv + interpolation: + - name: datetime@MetaData + method: exact + expected_obserror_variance: [3, 4, 1, 2] +Assign observation error from CSV file; data types float, int and string; missing values: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -10, -10, -10, -10, 10, 10, 10, 10 ] + lons: [ 30, 60, 30, 60, 30, 60, 30, 60 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z' ] + obs errors: [1.0] + air_pressure: [1000, 1000, 100000, 100000, 1000, 1000, 100000, 100000] + StringVar: [south, south, south, south, north, north, north, north] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_stringvar_pressure_longitude_missing_values.csv + interpolation: + - name: StringVar@MetaData + method: exact + - name: air_pressure@MetaData + method: exact + - name: longitude@MetaData + method: exact + expected_obserror_variance: [1, 2, 3, 4, 5, 6, 7, 8] +Assign observation error from CSV file; no column with @ErrorVariance suffix: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 1 ] + lons: [ 1 ] + datetimes: [ '2010-01-01T00:00:00Z' ] + obs errors: [1.0] + air_pressure: [1] + StringVar: [abc] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_misspelled_variance.csv + interpolation: + - name: StringVar@MetaData + method: exact + expectExceptionWithMessage: "No payload column found: no column name begins with 'ErrorVariance/' or ends with '@ErrorVariance'" +Assign observation error from CSV file; multiple columns with @ErrorVariance suffix: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 1 ] + lons: [ 1 ] + datetimes: [ '2010-01-01T00:00:00Z' ] + obs errors: [1.0] + air_pressure: [1] + StringVar: [abc] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_multiple_variances.csv + interpolation: + - name: StringVar@MetaData + method: exact + expectExceptionWithMessage: "Multiple payload candidates found: more than one column name begins with 'ErrorVariance/' or ends with '@ErrorVariance'" +Assign observation error from CSV file; string data in column with @ErrorVariance suffix: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 1 ] + lons: [ 1 ] + datetimes: [ '2010-01-01T00:00:00Z' ] + obs errors: [1.0] + air_pressure: [1] + StringVar: [abc] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_string_variance.csv + interpolation: + - name: StringVar@MetaData + method: exact + expectExceptionWithMessage: The payload column must contain numeric data +Assign observation error from CSV file; datetime data in column with @ErrorVariance suffix: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 1 ] + lons: [ 1 ] + datetimes: [ '2010-01-01T00:00:00Z' ] + obs errors: [1.0] + air_pressure: [1] + StringVar: [abc] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_datetime_variance.csv + interpolation: + - name: StringVar@MetaData + method: exact + expectExceptionWithMessage: The payload column must contain numeric data +Assign observation error from CSV file; empty file: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 1 ] + lons: [ 1 ] + datetimes: [ '2010-01-01T00:00:00Z' ] + obs errors: [1.0] + air_pressure: [1] + StringVar: [abc] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_empty.csv + interpolation: + - name: StringVar@MetaData + method: exact + expectExceptionWithMessage: No data could be loaded from the file +Assign observation error from CSV file; only one header line: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 1 ] + lons: [ 1 ] + datetimes: [ '2010-01-01T00:00:00Z' ] + obs errors: [1.0] + air_pressure: [1] + StringVar: [abc] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_no_data_types.csv + interpolation: + - name: StringVar@MetaData + method: exact + expectExceptionWithMessage: No data could be loaded from the file +Assign observation error from CSV file; no data lines: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 1 ] + lons: [ 1 ] + datetimes: [ '2010-01-01T00:00:00Z' ] + obs errors: [1.0] + air_pressure: [1] + StringVar: [abc] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_no_data_lines.csv + interpolation: + - name: StringVar@MetaData + method: exact + expectExceptionWithMessage: No data could be loaded from the file +Assign observation error from CSV file; incorrect number of data types: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 1 ] + lons: [ 1 ] + datetimes: [ '2010-01-01T00:00:00Z' ] + obs errors: [1.0] + air_pressure: [1] + StringVar: [abc] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_incorrect_num_entries_in_line_2.csv + interpolation: + - name: StringVar@MetaData + method: exact + expectExceptionWithMessage: The number of columns in line 2 differs from that in line 1 +Assign observation error from CSV file; incorrect number of columns in the first data line: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 1 ] + lons: [ 1 ] + datetimes: [ '2010-01-01T00:00:00Z' ] + obs errors: [1.0] + air_pressure: [1] + StringVar: [abc] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_incorrect_num_entries_in_line_3.csv + interpolation: + - name: StringVar@MetaData + method: exact + expectExceptionWithMessage: The number of columns in line 3 differs from that in line 1 +Assign observation error from CSV file; unsupported data type: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 1 ] + lons: [ 1 ] + datetimes: [ '2010-01-01T00:00:00Z' ] + obs errors: [1.0] + air_pressure: [1] + StringVar: [abc] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_unsupported_data_type.csv + interpolation: + - name: StringVar@MetaData + method: exact + expectExceptionWithMessage: Unsupported data type 'color' +Assign observation error from CSV file; data types float, int and string; missing values: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ -10, -10, -10, -10, 10, 10, 10, 10 ] + lons: [ 30, 60, 30, 60, 30, 60, 30, 60 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', + '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z' ] + obs errors: [1.0] + air_pressure: [1000, 1000, 100000, 100000, 1000, 1000, 100000, 100000] + StringVar: [south, south, south, south, north, north, north, north] + ObsError assign: + filter variables: + - name: air_temperature + action: + name: assign error + error function: + name: DrawObsErrorFromFile@ObsFunction + options: + file: Data/ufo/testinput_tier_1/obserror_stringvar_pressure_longitude_missing_values.csv + interpolation: + - name: StringVar@MetaData + method: exact + - name: air_pressure@MetaData + method: exact + - name: longitude@MetaData + method: exact + expected_obserror_variance: [1, 2, 3, 4, 5, 6, 7, 8] diff --git a/test/testinput/obsfilterdata.yaml b/test/testinput/obsfilterdata.yaml index 4a7782129..6315cd005 100644 --- a/test/testinput/obsfilterdata.yaml +++ b/test/testinput/obsfilterdata.yaml @@ -4,8 +4,9 @@ obs filter data: - obs space: name: Satwind obsdatain: - obsfile: Data/ioda/testinput_tier_1/satwind_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/satwind_obs_2018041500_m.nc4 simulated variables: [eastward_wind, northward_wind] + test data: datetime variables: - name: datetime@MetaData float variables: @@ -35,9 +36,10 @@ obs filter data: - obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 1-10,15 + test data: datetime variables: - name: datetime@MetaData float variables: diff --git a/test/testinput/omi_aura.yaml b/test/testinput/omi_aura.yaml index 25dc2e286..6fa583f99 100644 --- a/test/testinput/omi_aura.yaml +++ b/test/testinput/omi_aura.yaml @@ -10,7 +10,7 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/omi_aura_obs_2019101700_m.nc4 + obsfile: Data/ufo/testinput_tier_1/omi_aura_obs_2019101700_m.nc4 obsdataout: obsfile: Data/omi_aura_obs_2019101700_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/omi_aura_flipz.yaml b/test/testinput/omi_aura_flipz.yaml index b3c231e39..27f9e3fc5 100644 --- a/test/testinput/omi_aura_flipz.yaml +++ b/test/testinput/omi_aura_flipz.yaml @@ -10,7 +10,7 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/omi_aura_obs_2019101700_m.nc4 + obsfile: Data/ufo/testinput_tier_1/omi_aura_obs_2019101700_m.nc4 obsdataout: obsfile: Data/omi_aura_obs_flipz_2019101700_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/ompslp_npp.yaml b/test/testinput/ompslp_npp.yaml new file mode 100644 index 000000000..b40cd4da6 --- /dev/null +++ b/test/testinput/ompslp_npp.yaml @@ -0,0 +1,22 @@ +window begin: 2019-10-16T20:30:00Z +window end: 2019-10-17T03:30:00Z + +observations: +- obs operator: + name: VertInterp + vertical coordinate: ["air_pressure"] + obs space: + name: OzoneLevel + obsdatain: + obsfile: Data/ufo/testinput_tier_1/ompslp_npp_obs_2019101700_m.nc4 + obsdataout: + obsfile: Data/ompslp_npp_obs_2019101700_m_out.nc4 + simulated variables: [mole_fraction_of_ozone_in_air] + geovals: + filename: Data/ufo/testinput_tier_1/ompslp_npp_geoval_2019101700_m.nc4 + vector ref: GsiHofX + tolerance: 1.0e-04 # in % so that corresponds to 10^-3 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-9 + tolerance AD: 1.0e-11 diff --git a/test/testinput/ompsnp_npp.yaml b/test/testinput/ompsnp_npp.yaml index 7bf7e0db9..b6c3b6d54 100644 --- a/test/testinput/ompsnp_npp.yaml +++ b/test/testinput/ompsnp_npp.yaml @@ -10,7 +10,11 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/ompsnp_npp_obs_2019101700_m.nc4 + obsfile: Data/ufo/testinput_tier_1/ompsnp_npp_obs_2019101700_m.nc4 + obsgrouping: + group variables: ["latitude"] + sort variable: "air_pressure" + sort order: "ascending" obsdataout: obsfile: Data/ompsnp_npp_obs_2019101700_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/ompsnp_npp_L127.yaml b/test/testinput/ompsnp_npp_L127.yaml index 9fa4fed86..674d8d6a9 100644 --- a/test/testinput/ompsnp_npp_L127.yaml +++ b/test/testinput/ompsnp_npp_L127.yaml @@ -10,7 +10,11 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/ompsnp_npp_obs_2020051712_m.nc4 + obsfile: Data/ufo/testinput_tier_1/ompsnp_npp_obs_2020051712_m.nc4 + obsgrouping: + group variables: ["latitude"] + sort variable: "air_pressure" + sort order: "ascending" obsdataout: obsfile: Data/ompsnp_npp_obs_2020051712_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/ompsnp_npp_flipz.yaml b/test/testinput/ompsnp_npp_flipz.yaml index c653f14ff..fbf8fd954 100644 --- a/test/testinput/ompsnp_npp_flipz.yaml +++ b/test/testinput/ompsnp_npp_flipz.yaml @@ -10,7 +10,11 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/ompsnp_npp_obs_2019101700_m.nc4 + obsfile: Data/ufo/testinput_tier_1/ompsnp_npp_obs_2019101700_m.nc4 + obsgrouping: + group variables: ["latitude"] + sort variable: "air_pressure" + sort order: "ascending" obsdataout: obsfile: Data/ompsnp_npp_obs_flipz_2019101700_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/ompsnp_npp_qc_L127.yaml b/test/testinput/ompsnp_npp_qc_L127.yaml new file mode 100644 index 000000000..d6f7938a1 --- /dev/null +++ b/test/testinput/ompsnp_npp_qc_L127.yaml @@ -0,0 +1,73 @@ +window begin: 2020-05-17T08:30:00Z +window end: 2020-05-17T15:30:00Z + +observations: +- obs operator: + name: AtmVertInterpLay + geovals: [mole_fraction_of_ozone_in_air] + coefficients: [0.007886131] # convert from ppmv to DU + nlevels: [22] + obs space: + name: OzoneLayer + obsdatain: + obsfile: Data/ufo/testinput_tier_1/ompsnp_npp_obs_2020051712_qc.nc4 + obsgrouping: + group variables: ["latitude"] + sort variable: "air_pressure" + sort order: "ascending" + obsdataout: + obsfile: Data/ompsnp_npp_obs_2020051712_m_qc_out.nc4 + simulated variables: [integrated_layer_ozone_in_air] + geovals: + filename: Data/ufo/testinput_tier_1/ompsnp_npp_geoval_2020051712_qc.nc4 + obs filters: + - filter: BlackList + filter variables: + - name: integrated_layer_ozone_in_air + where: + - variable: + name: total_ozone_error_flag@MetaData + minvalue: 0.1 + maxvalue: 1.1 + action: + name: reject + - filter: BlackList + filter variables: + - name: integrated_layer_ozone_in_air + where: + - variable: + name: total_ozone_error_flag@MetaData + minvalue: 2.1 # toss toq>2, there are some 4 and 6 + action: + name: reject + - filter: BlackList + filter variables: + - name: integrated_layer_ozone_in_air + where: + - variable: + name: profile_ozone_error_flag@MetaData + minvalue: 1.1 + action: + name: reject + - filter: Domain Check + filter variables: + - name: integrated_layer_ozone_in_air + where: + - variable: + name: latitude@MetaData + minvalue: -90.0 + maxvalue: 90.0 + - variable: + name: longitude@MetaData + minvalue: -180.0 + maxvalue: 360.0 + - filter: Bounds Check + filter variables: + - name: integrated_layer_ozone_in_air + minvalue: 0.000001 + maxvalue: 1000.0 + - filter: Background Check + filter variables: + - name: integrated_layer_ozone_in_air + absolute threshold: 10.0 + passedBenchmark: 4925 # out of 6490 diff --git a/test/testinput/ompstc_npp.yaml b/test/testinput/ompstc_npp.yaml index 57e9c38c3..8d6bb6bde 100644 --- a/test/testinput/ompstc_npp.yaml +++ b/test/testinput/ompstc_npp.yaml @@ -10,7 +10,7 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/ompstc8_npp_obs_2019101700_m.nc4 + obsfile: Data/ufo/testinput_tier_1/ompstc8_npp_obs_2019101700_m.nc4 obsdataout: obsfile: Data/ompstc8_npp_obs_2019101700_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/ompstc_npp_flipz.yaml b/test/testinput/ompstc_npp_flipz.yaml index 3c5114312..0adf1c580 100644 --- a/test/testinput/ompstc_npp_flipz.yaml +++ b/test/testinput/ompstc_npp_flipz.yaml @@ -11,7 +11,7 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/ompstc8_npp_obs_2019101700_m.nc4 + obsfile: Data/ufo/testinput_tier_1/ompstc8_npp_obs_2019101700_m.nc4 obsdataout: obsfile: Data/ompstc8_npp_obs_flipz_2019101700_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/operatorutils.yaml b/test/testinput/operatorutils.yaml new file mode 100644 index 000000000..c7f606f0d --- /dev/null +++ b/test/testinput/operatorutils.yaml @@ -0,0 +1,25 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_s.nc4 + simulated variables: [eastward_wind, surface_pressure, northward_wind, air_temperature] + +cases: + without variables: + expected operator variables: + - name: eastward_wind + - name: surface_pressure + - name: northward_wind + - name: air_temperature + expected indices: [0, 1, 2, 3] + with variables: + variables: + - name: air_temperature + - name: surface_pressure + expected operator variables: + - name: air_temperature + - name: surface_pressure + expected indices: [3, 1] diff --git a/test/testinput/orbital_angle_predictor.yaml b/test/testinput/orbital_angle_predictor.yaml new file mode 100644 index 000000000..11a4c0c42 --- /dev/null +++ b/test/testinput/orbital_angle_predictor.yaml @@ -0,0 +1,65 @@ + +window begin: 2019-12-29T21:00:00Z +window end: 2019-12-30T03:00:00Z + +observations: +- obs space: + name: f17_ssmis + obsdatain: + obsfile: Data/ufo/testinput_tier_1/ssmis_obs_20191230T0000Z_orbital_angle.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-24 + geovals: + filename: Data/ufo/testinput_tier_1/ssmis_geovals_20191230T0000Z_100subset.nc4 + obs bias: + variational bc: + predictors: + - name: orbital_angle + options: + order: 1 + component: "cos" + - name: orbital_angle + options: + order: 1 + component: "sin" + - name: orbital_angle + options: + order: 2 + component: "cos" + - name: orbital_angle + options: + order: 2 + component: "sin" + - name: orbital_angle + options: + order: 3 + component: "cos" + - name: orbital_angle + options: + order: 3 + component: "sin" + - name: orbital_angle + options: + order: 4 + component: "cos" + - name: orbital_angle + options: + order: 4 + component: "sin" + - name: orbital_angle + options: + order: 5 + component: "cos" + - name: orbital_angle + options: + order: 5 + component: "sin" + - name: orbital_angle + options: + order: 6 + component: "cos" + - name: orbital_angle + options: + order: 6 + component: "sin" + tolerance: 1.e-5 diff --git a/test/testinput/parallel_obs_distribution.yaml b/test/testinput/parallel_obs_distribution.yaml deleted file mode 100644 index 372769878..000000000 --- a/test/testinput/parallel_obs_distribution.yaml +++ /dev/null @@ -1,69 +0,0 @@ -window begin: 2018-04-14T21:00:00Z -window end: 2018-04-15T03:00:00Z -obs space: - name: Hirs4 - obsdatain: - obsfile: Data/ioda/testinput_tier_1/hirs4_metop-b_obs_2018041500_m.nc4 - simulated variables: [air_temperature] -intTest: - variable: - name: brightness_temperature_3@PreQC - # Vector obtained by distributing values in the round-robin fashion into four vectors - # and concatenating them. - expectedValues: [0, 0, 0, 0, 0, 0, 0, -7, -7, 0, 0, -7, 0, 0, 0, 0, 0, 0, 0, -7, 0, 0, 0, 0, - -7, -7, 0, 0, 0, 0, 0, 0, -7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -7, -7, 0, 0, - 0, 0, 0, -7, 0, 0, 0, 0, 0, 0, -7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -7, 0, 0, 0, - -7, 0, 0, -7, -7, 0, 0, 0, -7, 0, -7, 0, 0, 0, -7, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0] -floatOrDoubleTest: - variable: - name: brightness_temperature_3@ObsValue - expectedValues: [212.59, 220.2, 214.31, 219.22, 215.44, 220.46, 218.58, 215.33, 216.06, - 216.96, 222.66, 217.14, 216.3, 217.76, 223.43, 220.58, 224.56, 215.63, - 217.03, 216.08, 219.57, 210.91, 221.21, 217.56, 219.05, 212.17, 211.33, - 224.28, 216.22, 219.8, 218.71, 217.02, 215.34, 215, 217.06, 222.2, 217.13, - 216.36, 217.94, 220.1, 221.79, 211.36, 216.03, 216.97, 214.15, 219.22, - 215.39, 218.73, 215.83, 223.94, 214.46, 215.47, 221.31, 216.67, 221.64, - 220.52, 214.88, 215.7, 215.4, 217.16, 222.75, 217.37, 215.73, 223.61, - 219.56, 223.94, 217.79, 217.1, 216.33, 216.15, 219.15, 221.29, 217.87, - 215.87, 223.82, 219.2, 217.04, 217.94, 215.81, 214.93, 220.87, 215.81, - 216.12, 215.51, 218.52, 223.04, 216.98, 216.48, 218.09, 219.94, 224.56, - 217.71, 217.17, 215.79, 217.55, 224.91, 218.03, 218.11, 218.84, 220.31] - -dateTimeTest: - variable: - name: datetime@MetaData - expectedValues: ['2018-04-14T23:41:28Z', '2018-04-14T22:07:30Z', '2018-04-14T23:36:33Z', - '2018-04-15T02:46:51Z', '2018-04-15T02:30:56Z', '2018-04-14T23:31:51Z', - '2018-04-14T23:21:28Z', '2018-04-15T00:56:32Z', '2018-04-14T23:11:11Z', - '2018-04-15T00:46:55Z', '2018-04-15T02:23:08Z', '2018-04-14T21:41:35Z', - '2018-04-14T21:39:01Z', '2018-04-14T23:05:43Z', '2018-04-14T22:58:42Z', - '2018-04-14T21:11:38Z', '2018-04-15T02:06:27Z', '2018-04-15T01:34:18Z', - '2018-04-15T01:38:05Z', '2018-04-15T01:44:42Z', '2018-04-15T00:13:32Z', - '2018-04-15T01:23:12Z', '2018-04-14T23:50:47Z', '2018-04-14T22:14:38Z', - '2018-04-14T22:32:16Z', '2018-04-14T23:41:35Z', '2018-04-15T01:19:02Z', - '2018-04-15T01:12:38Z', '2018-04-15T02:44:12Z', '2018-04-15T02:25:07Z', - '2018-04-14T21:47:58Z', '2018-04-14T23:21:21Z', '2018-04-15T00:56:20Z', - '2018-04-15T00:51:57Z', '2018-04-14T23:06:04Z', '2018-04-15T02:22:55Z', - '2018-04-14T21:41:42Z', '2018-04-14T21:36:33Z', '2018-04-14T21:21:18Z', - '2018-04-14T21:16:00Z', '2018-04-15T02:00:19Z', '2018-04-15T01:23:50Z', - '2018-04-15T01:34:20Z', '2018-04-15T01:39:24Z', '2018-04-15T01:46:22Z', - '2018-04-15T01:56:49Z', '2018-04-15T01:27:19Z', '2018-04-14T22:11:25Z', - '2018-04-14T22:24:40Z', '2018-04-14T22:38:05Z', '2018-04-14T22:01:58Z', - '2018-04-15T02:57:13Z', '2018-04-15T02:50:08Z', '2018-04-15T02:43:48Z', - '2018-04-15T02:20:58Z', '2018-04-14T23:27:13Z', '2018-04-15T00:59:27Z', - '2018-04-14T23:15:06Z', '2018-04-14T23:10:59Z', '2018-04-14T23:05:26Z', - '2018-04-14T22:59:10Z', '2018-04-14T21:39:56Z', '2018-04-14T21:36:07Z', - '2018-04-14T22:59:49Z', '2018-04-14T21:14:24Z', '2018-04-15T02:06:05Z', - '2018-04-15T01:32:53Z', '2018-04-15T01:36:01Z', '2018-04-15T01:41:39Z', - '2018-04-15T01:48:23Z', '2018-04-15T01:57:10Z', '2018-04-14T23:49:59Z', - '2018-04-14T22:12:09Z', '2018-04-15T00:06:07Z', '2018-04-14T22:40:46Z', - '2018-04-14T22:04:38Z', '2018-04-15T02:56:28Z', '2018-04-15T01:07:28Z', - '2018-04-15T02:41:11Z', '2018-04-14T21:54:52Z', '2018-04-14T23:24:16Z', - '2018-04-15T00:58:15Z', '2018-04-15T00:53:55Z', '2018-04-14T23:10:00Z', - '2018-04-15T00:44:26Z', '2018-04-15T02:20:22Z', '2018-04-14T21:40:00Z', - '2018-04-14T21:31:31Z', '2018-04-14T21:17:58Z', '2018-04-14T22:53:28Z', - '2018-04-15T02:06:38Z', '2018-04-15T01:31:47Z', '2018-04-15T01:36:41Z', - '2018-04-15T01:42:53Z', '2018-04-15T01:53:00Z', '2018-04-14T22:40:32Z', - '2018-04-14T22:08:52Z', '2018-04-14T23:55:47Z', '2018-04-14T22:29:48Z', - '2018-04-14T21:02:09Z'] diff --git a/test/testinput/parameters.yaml b/test/testinput/parameters.yaml index e49357f42..b526ab084 100644 --- a/test/testinput/parameters.yaml +++ b/test/testinput/parameters.yaml @@ -1,11 +1,17 @@ empty: # Nothing to see here full: + # These parameters must be specified in the order the corresponding + # Parameter objects are declared in the C++ file; otherwise the + # serialization test, which isn't very sophisticated and simply relies + # on a string comparison, will fail. opt_variable_parameter: - channels: 1,5 name: latitude@MetaData + channels: 1,5 options: some_option: true +simple_string: + opt_variable_parameter: latitude@MetaData no_channels: opt_variable_parameter: name: latitude@MetaData diff --git a/test/testinput/parameters_older_eckit.yaml b/test/testinput/parameters_older_eckit.yaml new file mode 100644 index 000000000..fac1e7f0b --- /dev/null +++ b/test/testinput/parameters_older_eckit.yaml @@ -0,0 +1,25 @@ +empty: + # Nothing to see here +full: + opt_variable_parameter: + channels: 1,5 + name: latitude@MetaData + options: + some_option: true +simple_string: + opt_variable_parameter: latitude@MetaData +no_channels: + opt_variable_parameter: + name: latitude@MetaData + options: + some_option: true +complex_channels: + opt_variable_parameter: + channels: 1,5,10-15 + name: latitude@MetaData +missing_name: + opt_variable_parameter: +misspelled_property: + opt_variable_parameter: + name: latitude@MetaData + canali: 1,5 diff --git a/test/testinput/primitive_variables.yaml b/test/testinput/primitive_variables.yaml new file mode 100644 index 000000000..6dfb52d43 --- /dev/null +++ b/test/testinput/primitive_variables.yaml @@ -0,0 +1,75 @@ +window begin: 2000-01-01T00:00:00Z +window end: 2030-01-01T00:00:00Z + +obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/ahi_himawari8_obs_2019042306_s.nc4 + simulated variables: [brightness_temperature] +cases: + Empty list: + variables: [] + expected values: [] + expected names: [] + expected groups: [] + Mix of single-channel and multi-channel variables: + variables: + - name: latitude@MetaData + - name: brightness_temperature@ObsValue + channels: 10-12 + - name: longitude@MetaData + expected values: + - [40.90339] + - [252.4] + - [285.5] + - [255.1] + - [126.4771] + expected names: + - latitude + - brightness_temperature_10 + - brightness_temperature_11 + - brightness_temperature_12 + - longitude + expected groups: + - MetaData + - ObsValue + - ObsValue + - ObsValue + - MetaData + Mix of ObsSpace variables and ObsFunctions: + variables: + - name: brightness_temperature@ObsValue + channels: 8-9 + - name: latitude@MetaData + # outputs 100 in the first output channel, 200 in the second and 300 in the third + - name: ObsErrorModelRamp@ObsFunction + channels: 10-12 + options: + channels: 10-12 + xvar: + name: latitude@MetaData + x0: [0, 0, 0] + x1: [1000, 1000, 1000] + err0: [100, 200, 300] + err1: [100, 200, 300] + expected values: + - [234.1] + - [244.5] + - [40.90339] + - [100] + - [200] + - [300] + expected names: + - brightness_temperature_8 + - brightness_temperature_9 + - latitude + - ObsErrorModelRamp_10 + - ObsErrorModelRamp_11 + - ObsErrorModelRamp_12 + expected groups: + - ObsValue + - ObsValue + - MetaData + - ObsFunction + - ObsFunction + - ObsFunction diff --git a/test/testinput/processwhere.yaml b/test/testinput/processwhere.yaml index 38e9760f1..b433637c9 100644 --- a/test/testinput/processwhere.yaml +++ b/test/testinput/processwhere.yaml @@ -6,7 +6,7 @@ successful: obsdatain: obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 simulated variables: [var1] - nlocs: 10 + nlocs: 10 ProcessWhere: - where: # test minvalue when all are >= min - variable: @@ -89,41 +89,41 @@ successful: - where: # test min & max for datetime hours - variable: name: datetime@MetaData - minvalue: 0000-00-00T14:00:00Z - maxvalue: 0000-00-00T18:00:00Z + minvalue: "****-**-**T14:00:00Z" + maxvalue: "****-**-**T18:00:00Z" # datetime1@MetaData = [2018-04-15T06:00:00Z, 2018-04-16T15:00:00Z, 2018-04-17T06:00:00Z, 2018-04-18T15:00:00Z, 2018-04-19T06:00:00Z, 2018-04-20T15:00:00Z, 2018-04-21T06:00:00Z, 2018-04-22T15:00:00Z, 2018-04-23T06:00:00Z, 2018-04-24T15:00:00Z] size where true: 5 - where: # test minvalue for datetime hours - variable: name: datetime@MetaData - minvalue: 0000-00-00T14:00:00Z + minvalue: "****-**-**T14:00:00Z" # datetime1@MetaData = [2018-04-15T06:00:00Z, 2018-04-16T15:00:00Z, 2018-04-17T06:00:00Z, 2018-04-18T15:00:00Z, 2018-04-19T06:00:00Z, 2018-04-20T15:00:00Z, 2018-04-21T06:00:00Z, 2018-04-22T15:00:00Z, 2018-04-23T06:00:00Z, 2018-04-24T15:00:00Z] size where true: 5 - where: # test minvalue for datetime hours unconstrained maxvalue - variable: name: datetime@MetaData - minvalue: 0000-00-00T14:00:00Z - maxvalue: 0000-00-00T00:00:00Z + minvalue: "****-**-**T14:00:00Z" + maxvalue: "****-**-**T**:**:**Z" # datetime1@MetaData = [2018-04-15T06:00:00Z, 2018-04-16T15:00:00Z, 2018-04-17T06:00:00Z, 2018-04-18T15:00:00Z, 2018-04-19T06:00:00Z, 2018-04-20T15:00:00Z, 2018-04-21T06:00:00Z, 2018-04-22T15:00:00Z, 2018-04-23T06:00:00Z, 2018-04-24T15:00:00Z] size where true: 5 - where: # test maxvalue for datetime hours - variable: name: datetime@MetaData - maxvalue: 0000-00-00T14:00:00Z + maxvalue: "****-**-**T14:00:00Z" # datetime1@MetaData = [2018-04-15T06:00:00Z, 2018-04-16T15:00:00Z, 2018-04-17T06:00:00Z, 2018-04-18T15:00:00Z, 2018-04-19T06:00:00Z, 2018-04-20T15:00:00Z, 2018-04-21T06:00:00Z, 2018-04-22T15:00:00Z, 2018-04-23T06:00:00Z, 2018-04-24T15:00:00Z] size where true: 5 - where: # test maxvalue for datetime hours with unconstrained minvalue - variable: name: datetime@MetaData - minvalue: 0000-00-00T00:00:00Z - maxvalue: 0000-00-00T14:00:00Z + minvalue: "****-**-**T**:**:**Z" + maxvalue: "****-**-**T14:00:00Z" # datetime1@MetaData = [2018-04-15T06:00:00Z, 2018-04-16T15:00:00Z, 2018-04-17T06:00:00Z, 2018-04-18T15:00:00Z, 2018-04-19T06:00:00Z, 2018-04-20T15:00:00Z, 2018-04-21T06:00:00Z, 2018-04-22T15:00:00Z, 2018-04-23T06:00:00Z, 2018-04-24T15:00:00Z] size where true: 5 - where: # test unconstrained minvalue and maxvalue for datetime hours - variable: name: datetime@MetaData - minvalue: 0000-00-00T00:00:00Z - maxvalue: 0000-00-00T00:00:00Z + minvalue: "****-**-**T**:**:**Z" + maxvalue: "****-**-**T**:**:**Z" # datetime1@MetaData = [2018-04-15T06:00:00Z, 2018-04-16T15:00:00Z, 2018-04-17T06:00:00Z, 2018-04-18T15:00:00Z, 2018-04-19T06:00:00Z, 2018-04-20T15:00:00Z, 2018-04-21T06:00:00Z, 2018-04-22T15:00:00Z, 2018-04-23T06:00:00Z, 2018-04-24T15:00:00Z] size where true: 10 - where: # test that AND for 2 conditions works as expected @@ -182,6 +182,66 @@ successful: is_in: 0-3 # intvar4@MetaData = 0, 0, 0, 0, 0, 1, 2, 3, 4, 5 size where true: 8 + - where: # test maxvalue when all are <= max + - variable: + name: intvar1@MetaData + maxvalue: 10 + # intvar1@MetaData = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + size where true: 10 + - where: # test maxvalue when all but one are > max, one == max + - variable: + name: intvar1@MetaData + maxvalue: 1 + # intvar1@MetaData = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + size where true: 1 + - where: # test min & max at the same time when all should pass + - variable: + name: intvar2@MetaData + minvalue: 1 + maxvalue: 10 + # intvar2@MetaData = 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + size where true: 10 + - where: # test min & max at the same time when some should pass + - variable: + name: intvar2@MetaData + minvalue: 2 + maxvalue: 5 + # intvar2@MetaData = 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + size where true: 4 + - where: # test min & max at the same time when max < min + - variable: + name: intvar2@MetaData + minvalue: 6 + maxvalue: 4 + # intvar2@MetaData = 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + size where true: 0 + - where: # test min & max at the same time when max == min + - variable: + name: intvar2@MetaData + minvalue: 7 + maxvalue: 7 + # intvar2@MetaData = 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + size where true: 1 + - where: # test min & max for variable with missing values + - variable: + name: intvar5@MetaData + minvalue: 2 + maxvalue: 5 + # intvar5@MetaData = 1, _, 2, _, 3, _, 4, _, 5, 0 + size where true: 4 + - where: # test min & max for variable with missing values + - variable: + name: intvar5@MetaData + minvalue: 0 + maxvalue: 100 + # intvar5@MetaData = 1, _, 2, _, 3, _, 4, _, 5, 0 + size where true: 6 + - where: # test min & max for variable with missing values + - variable: + name: intvar5@MetaData + minvalue: 100 + # intvar5@MetaData = 1, _, 2, _, 3, _, 4, _, 5, 0 + size where true: 0 - where: # test is_in for string-based inputs - variable: name: station_id@MetaData @@ -218,6 +278,34 @@ successful: is_in: 0-3 # intvar5@MetaData = 1, _, 2, _, 3, _, 4, _, 5, 0 size where true: 4 + - where: + - variable: + name: var4@MetaData + absolute_tolerance: 1.0e-5 + is_close_to_any_of: [0.0] + # var4@MetaData = 0.0*5, 1.0, 2.0, 3.0, 4.0, 5.0 + size where true: 5 + - where: + - variable: + name: var4@MetaData + absolute_tolerance: 1.0e-4 + is_close_to_any_of: [1.0, 2.0] + # var4@MetaData = 0.0*5, 1.0, 2.0, 3.0, 4.0, 5.0 + size where true: 2 + - where: + - variable: + name: var4@MetaData + absolute_tolerance: 1.5 + is_close_to_any_of: [2.0] + # var4@MetaData = 0.0*5, 1.0, 2.0, 3.0, 4.0, 5.0 + size where true: 3 + - where: + - variable: + name: var4@MetaData + relative_tolerance: 0.5 + is_close_to_any_of: [2.0] + # var4@MetaData = 0.0*5, 1.0, 2.0, 3.0, 4.0, 5.0 + size where true: 3 - where: # test is_not_in - variable: name: intvar3@MetaData @@ -266,6 +354,108 @@ successful: is_not_in: [WARUBAJH, MTLGWTHE, XKEHXQSD, XKEHXQSD] # station_id@MetaData = WARUBAJH, HUAVFIBU, DKPAIDZX, MTLGWTHE, LESDCYOD, UGCMFXFK, HSMHFHQI, JALVYAAR, MOYSUYWB, XKEHXQSD size where true: 7 + - where: + - variable: + name: var4@MetaData + absolute_tolerance: 1.0e-5 + is_not_close_to_any_of: [0.0] + # var4@MetaData = 0.0*5, 1.0, 2.0, 3.0, 4.0, 5.0 + size where true: 5 + - where: + - variable: + name: var4@MetaData + absolute_tolerance: 1.0e-5 + is_not_close_to_any_of: [1.0, 2.0] + # var4@MetaData = 0.0*5, 1.0, 2.0, 3.0, 4.0, 5.0 + size where true: 8 + - where: + - variable: + name: var4@MetaData + absolute_tolerance: 1.5 + is_not_close_to_any_of: [2.0] + # var4@MetaData = 0.0*5, 1.0, 2.0, 3.0, 4.0, 5.0 + size where true: 7 + - where: + - variable: + name: var4@MetaData + relative_tolerance: 0.2 + is_not_close_to_any_of: [2.0] + # var4@MetaData = 0.0*5, 1.0, 2.0, 3.0, 4.0, 5.0 + size where true: 9 + - where: # test any_bit_set_of with a single bit specified + - variable: + name: intvar1@MetaData + any_bit_set_of: 3 + # intvar1@MetaData = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + # i.e. binary 0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000, 1001, 1010 + size where true: 3 # "where" should select the following values of intvar1: 8, 9, 10 + - where: # test any_bit_set_of with multiple bits specified + - variable: + name: intvar1@MetaData + any_bit_set_of: 0, 1 + # intvar1@MetaData = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + # i.e. binary 0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000, 1001, 1010 + size where true: 8 # "where" should select all values of intvar1 except for 4 and 8 + - where: # test any_bit_unset_of with a single bit specified + - variable: + name: intvar1@MetaData + any_bit_unset_of: 3 + # intvar1@MetaData = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + # i.e. binary 0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000, 1001, 1010 + size where true: 7 # "where" should select all values of intvar1 except for 8, 9, 10 + - where: # test any_bit_unset_of with multiple bits specified + - variable: + name: intvar1@MetaData + any_bit_unset_of: 0, 1 + # intvar1@MetaData = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + # i.e. binary 0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000, 1001, 1010 + size where true: 8 # "where" should select the following values of intvar1: 3 and 7 + - where: # test matches_regex on string variables + - variable: + name: station_id@MetaData + matches_regex: ".*U.*A.*" + # station_id@MetaData = WARUBAJH, HUAVFIBU, DKPAIDZX, MTLGWTHE, LESDCYOD, UGCMFXFK, HSMHFHQI, JALVYAAR, MOYSUYWB, XKEHXQSD + size where true: 2 + - where: # test matches_regex on int variables + - variable: + name: intvar1@MetaData + matches_regex: "10?" + # intvar1@MetaData = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + size where true: 2 + - where: # test matches_wildcard on string variables + - variable: + name: station_id@MetaData + matches_wildcard: "*U*A*" + # station_id@MetaData = WARUBAJH, HUAVFIBU, DKPAIDZX, MTLGWTHE, LESDCYOD, UGCMFXFK, HSMHFHQI, JALVYAAR, MOYSUYWB, XKEHXQSD + size where true: 2 + - where: # test matches_wildcard on int variables + - variable: + name: intvar1@MetaData + matches_wildcard: "1*" + # intvar1@MetaData = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + size where true: 2 + - where: # test matches_any_wildcard on string variables + - variable: + name: station_id@MetaData + matches_any_wildcard: ["*U*A*", "H*"] + # station_id@MetaData = WARUBAJH, HUAVFIBU, DKPAIDZX, MTLGWTHE, LESDCYOD, UGCMFXFK, HSMHFHQI, JALVYAAR, MOYSUYWB, XKEHXQSD + size where true: 3 + - where: # test matches_any_wildcard on int variables + - variable: + name: intvar1@MetaData + matches_any_wildcard: ["1*", "6*"] + # intvar1@MetaData = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + size where true: 3 + - where: # test matches_any_wildcard with an empty list of wildcards on string variables + - variable: + name: station_id@MetaData + matches_any_wildcard: [] + size where true: 0 + - where: # test matches_any_wildcard with an empty list of wildcards on int variables + - variable: + name: intvar1@MetaData + matches_any_wildcard: [] + size where true: 0 user_error_type_handling_is_in: window begin: 2018-01-01T00:00:00Z @@ -275,7 +465,7 @@ user_error_type_handling_is_in: obsdatain: obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 simulated variables: [var1] - nlocs: 10 + nlocs: 10 ProcessWhere: - where: # test is_not_in for unsupported type (float) - variable: @@ -287,3 +477,64 @@ user_error_type_handling_is_in: name: var1@MetaData is_in: 0.5 size where true: 0 + - where: # test any_bit_set_of for unsupported type (float) + - variable: + name: var1@MetaData + any_bit_set_of: 1 + size where true: 0 + - where: # test any_bit_set_of for unsupported type (string) + - variable: + name: station_id@MetaData + any_bit_set_of: 1 + size where true: 0 + - where: # test any_bit_unset_of for unsupported type (float) + - variable: + name: var1@MetaData + any_bit_unset_of: 1 + size where true: 0 + - where: # test any_bit_unset_of for unsupported type (string) + - variable: + name: station_id@MetaData + any_bit_unset_of: 1 + size where true: 0 + - where: # test matches_regex for unsupported type (float) + - variable: + name: var1@MetaData + matches_regex: abc.* + size where true: 0 + - where: # test matches_wildcard for unsupported type (float) + - variable: + name: var1@MetaData + matches_wildcard: abc* + size where true: 0 + - where: # test matches_any_wildcard for unsupported type (float) + - variable: + name: var1@MetaData + matches_any_wildcard: [abc*] + size where true: 0 + - where: # test is_not_close_to_any_of for unsupported type (int) + - variable: + name: intvar1@MetaData + relative_tolerance: 0.2 + is_not_close_to_any_of: [1] + size where true: 0 + - where: # test is_close_to_any_of for unsupported type (int) + - variable: + name: intvar1@MetaData + absolute_tolerance: 0.2 + is_close_to_any_of: [1] + size where true: 0 + - where: # test is_not_close_to_any_of when both tolerances given + - variable: + name: var1@MetaData + relative_tolerance: 0.2 + absolute_tolerance: 0.2 + is_not_close_to_any_of: [1.5] + size where true: 0 + - where: # test is_close_to_any_of when both tolerances given + - variable: + name: var1@MetaData + relative_tolerance: 0.2 + absolute_tolerance: 0.2 + is_close_to_any_of: [1.5] + size where true: 0 diff --git a/test/testinput/profileconsistencychecks_OPScomparison.yaml b/test/testinput/profileconsistencychecks_OPScomparison.yaml index 97ab4b15c..45ef6d563 100644 --- a/test/testinput/profileconsistencychecks_OPScomparison.yaml +++ b/test/testinput/profileconsistencychecks_OPScomparison.yaml @@ -10,9 +10,11 @@ Sondes: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] #Sorting is not performed for this test in order to ensure exact correspondence with OPS simulated variables: [air_temperature, geopotential_height] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_geovals.nc4 obs diagnostics: ProfileConsistencyChecks: Checks: ["Basic", "SamePDiffT", "Sign", "UnstableLayer", "Interpolation", "Hydrostatic"] diff --git a/test/testinput/profileconsistencychecks_RH_OPScomparison.yaml b/test/testinput/profileconsistencychecks_RH_OPScomparison.yaml index 95cd27e31..28f31c14c 100644 --- a/test/testinput/profileconsistencychecks_RH_OPScomparison.yaml +++ b/test/testinput/profileconsistencychecks_RH_OPScomparison.yaml @@ -10,7 +10,7 @@ Sondes: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_rh.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] #Sorting is not performed for this test in order to ensure exact correspondence with OPS simulated variables: [air_temperature, relative_humidity, dew_point_temperature] obs diagnostics: diff --git a/test/testinput/profileconsistencychecks_RH_obsfilter.yaml b/test/testinput/profileconsistencychecks_RH_obsfilter.yaml index 24e8cb7da..c6a17cdbd 100644 --- a/test/testinput/profileconsistencychecks_RH_obsfilter.yaml +++ b/test/testinput/profileconsistencychecks_RH_obsfilter.yaml @@ -11,7 +11,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_rh.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, relative_humidity] @@ -27,6 +27,6 @@ observations: BChecks_Skip: true HofX: HofX obs diagnostics: - passedBenchmark: 9586 - benchmarkFlag: 15 - flaggedBenchmark: 136 + passedBenchmark: 9654 + benchmarkFlag: 24 + flaggedBenchmark: 68 diff --git a/test/testinput/profileconsistencychecks_UInterpAlternative_OPScomparison.yaml b/test/testinput/profileconsistencychecks_UInterpAlternative_OPScomparison.yaml new file mode 100644 index 000000000..5746fb191 --- /dev/null +++ b/test/testinput/profileconsistencychecks_UInterpAlternative_OPScomparison.yaml @@ -0,0 +1,22 @@ +# +#=== Profile consistency checks for wind speed interpolation: comparison with OPS values, alternative implementation of UInterp check ===# +# + +Sondes: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_uinterp.nc4 + obsgrouping: + group variables: [ "station_id" ] + #Sorting is not performed for this test in order to ensure exact correspondence with OPS + simulated variables: [eastward_wind, northward_wind] + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "UInterpAlternative"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 diff --git a/test/testinput/profileconsistencychecks_UInterpAlternative_obsfilter.yaml b/test/testinput/profileconsistencychecks_UInterpAlternative_obsfilter.yaml new file mode 100644 index 000000000..00e862d7f --- /dev/null +++ b/test/testinput/profileconsistencychecks_UInterpAlternative_obsfilter.yaml @@ -0,0 +1,31 @@ +# +#=== Profile consistency checks for wind speed interpolation: alternative implementation of UInterp check ===# +# + +window begin: 2018-04-14T20:30:00Z +window end: 2018-04-15T03:30:00Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_uinterp.nc4 + obsgrouping: + group variables: [ "station_id" ] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [eastward_wind, northward_wind] + obs filters: + - filter: Profile Consistency Checks + filter variables: + - name: eastward_wind + - name: northward_wind + Checks: ["Basic", "UInterpAlternative"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + HofX: HofX + obs diagnostics: + passedBenchmark: 5549 + benchmarkFlag: 24 + flaggedBenchmark: 93 diff --git a/test/testinput/profileconsistencychecks_UInterp_OPScomparison.yaml b/test/testinput/profileconsistencychecks_UInterp_OPScomparison.yaml index 8ad66bf30..e1eeffbd5 100644 --- a/test/testinput/profileconsistencychecks_UInterp_OPScomparison.yaml +++ b/test/testinput/profileconsistencychecks_UInterp_OPScomparison.yaml @@ -10,7 +10,7 @@ Sondes: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_uinterp.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] #Sorting is not performed for this test in order to ensure exact correspondence with OPS simulated variables: [eastward_wind, northward_wind] obs diagnostics: diff --git a/test/testinput/profileconsistencychecks_UInterp_obsfilter.yaml b/test/testinput/profileconsistencychecks_UInterp_obsfilter.yaml index 07448b9ea..3e7426ad4 100644 --- a/test/testinput/profileconsistencychecks_UInterp_obsfilter.yaml +++ b/test/testinput/profileconsistencychecks_UInterp_obsfilter.yaml @@ -11,7 +11,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_uinterp.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [eastward_wind, northward_wind] @@ -26,6 +26,6 @@ observations: PrintStationID: true HofX: HofX obs diagnostics: - passedBenchmark: 5456 - benchmarkFlag: 15 - flaggedBenchmark: 186 + passedBenchmark: 5549 + benchmarkFlag: 24 + flaggedBenchmark: 93 diff --git a/test/testinput/profileconsistencychecks_average_pressure_OPScomparison.yaml b/test/testinput/profileconsistencychecks_average_pressure_OPScomparison.yaml new file mode 100644 index 000000000..ed4100d6c --- /dev/null +++ b/test/testinput/profileconsistencychecks_average_pressure_OPScomparison.yaml @@ -0,0 +1,27 @@ +# +#=== Profile average pressure: comparison with OPS values ===# +# + +Sondes: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_pressure_obs_extended.nc4 + obsgrouping: + group variables: [ "station_id" ] + #Sorting is not performed for this test in order to ensure exact correspondence with OPS + simulated variables: [air_temperature] + extension: + average profiles onto model levels: 71 + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_pressure_geovals_extended.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AveragePressure"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 + diff --git a/test/testinput/profileconsistencychecks_average_pressure_obsfilter.yaml b/test/testinput/profileconsistencychecks_average_pressure_obsfilter.yaml new file mode 100644 index 000000000..9dae043f9 --- /dev/null +++ b/test/testinput/profileconsistencychecks_average_pressure_obsfilter.yaml @@ -0,0 +1,34 @@ +# +#=== Profile average pressure ===# +# + +window begin: 2019-06-14T20:30:00Z +window end: 2019-06-15T03:30:00Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_pressure_obs_extended.nc4 + obsgrouping: + group variables: [ "station_id" ] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [air_temperature] + extension: + average profiles onto model levels: 71 + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_pressure_geovals_extended.nc4 + obs filters: + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + Checks: ["Basic", "AveragePressure"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + HofX: HofX + obs diagnostics: + passedBenchmark: 18 + benchmarkFlag: 24 + flaggedBenchmark: 132 diff --git a/test/testinput/profileconsistencychecks_average_relativehumidity_OPScomparison.yaml b/test/testinput/profileconsistencychecks_average_relativehumidity_OPScomparison.yaml new file mode 100644 index 000000000..27b5e65d0 --- /dev/null +++ b/test/testinput/profileconsistencychecks_average_relativehumidity_OPScomparison.yaml @@ -0,0 +1,27 @@ +# +#=== Profile average relative humidity: comparison with OPS values ===# +# + +Sondes: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_relativehumidity_obs_extended.nc4 + obsgrouping: + group variables: [ "station_id" ] + #Sorting is not performed for this test in order to ensure exact correspondence with OPS + simulated variables: [relative_humidity] + extension: + average profiles onto model levels: 71 + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals_extended.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AveragePressure", "AverageTemperature", "AverageRelativeHumidity"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 + diff --git a/test/testinput/profileconsistencychecks_average_relativehumidity_obsfilter.yaml b/test/testinput/profileconsistencychecks_average_relativehumidity_obsfilter.yaml new file mode 100644 index 000000000..bc5ef7f46 --- /dev/null +++ b/test/testinput/profileconsistencychecks_average_relativehumidity_obsfilter.yaml @@ -0,0 +1,34 @@ +# +#=== Profile average relative humidity ===# +# + +window begin: 2019-06-14T20:30:00Z +window end: 2019-06-15T03:30:00Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_relativehumidity_obs_extended.nc4 + obsgrouping: + group variables: [ "station_id" ] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [relative_humidity] + extension: + average profiles onto model levels: 71 + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals_extended.nc4 + obs filters: + - filter: Profile Consistency Checks + filter variables: + - name: relative_humidity + Checks: ["Basic", "AveragePressure", "AverageTemperature", "AverageRelativeHumidity"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + HofX: HofX + obs diagnostics: + passedBenchmark: 1574 + benchmarkFlag: 24 + flaggedBenchmark: 33 diff --git a/test/testinput/profileconsistencychecks_average_temperature_OPScomparison.yaml b/test/testinput/profileconsistencychecks_average_temperature_OPScomparison.yaml new file mode 100644 index 000000000..c98252840 --- /dev/null +++ b/test/testinput/profileconsistencychecks_average_temperature_OPScomparison.yaml @@ -0,0 +1,27 @@ +# +#=== Profile average temperature: comparison with OPS values ===# +# + +Sondes: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_temperature_obs_extended.nc4 + obsgrouping: + group variables: [ "station_id" ] + #Sorting is not performed for this test in order to ensure exact correspondence with OPS + simulated variables: [air_temperature] + extension: + average profiles onto model levels: 71 + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals_extended.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AveragePressure", "AverageTemperature"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 + diff --git a/test/testinput/profileconsistencychecks_average_temperature_obsfilter.yaml b/test/testinput/profileconsistencychecks_average_temperature_obsfilter.yaml new file mode 100644 index 000000000..ea7540199 --- /dev/null +++ b/test/testinput/profileconsistencychecks_average_temperature_obsfilter.yaml @@ -0,0 +1,34 @@ +# +#=== Profile average temperature ===# +# + +window begin: 2019-06-14T20:30:00Z +window end: 2019-06-15T03:30:00Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_temperature_obs_extended.nc4 + obsgrouping: + group variables: [ "station_id" ] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [air_temperature] + extension: + average profiles onto model levels: 71 + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals_extended.nc4 + obs filters: + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + Checks: ["Basic", "AveragePressure", "AverageTemperature"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + HofX: HofX + obs diagnostics: + passedBenchmark: 1605 + benchmarkFlag: 24 + flaggedBenchmark: 2 diff --git a/test/testinput/profileconsistencychecks_average_windspeed_OPScomparison.yaml b/test/testinput/profileconsistencychecks_average_windspeed_OPScomparison.yaml new file mode 100644 index 000000000..27c63bf04 --- /dev/null +++ b/test/testinput/profileconsistencychecks_average_windspeed_OPScomparison.yaml @@ -0,0 +1,27 @@ +# +#=== Profile average wind speed: comparison with OPS values ===# +# + +Sondes: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_windspeed_obs_extended.nc4 + obsgrouping: + group variables: [ "station_id" ] + #Sorting is not performed for this test in order to ensure exact correspondence with OPS + simulated variables: [eastward_wind, northward_wind] + extension: + average profiles onto model levels: 71 + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals_extended.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AveragePressure", "AverageWindSpeed"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 + diff --git a/test/testinput/profileconsistencychecks_average_windspeed_obsfilter.yaml b/test/testinput/profileconsistencychecks_average_windspeed_obsfilter.yaml new file mode 100644 index 000000000..a907d7770 --- /dev/null +++ b/test/testinput/profileconsistencychecks_average_windspeed_obsfilter.yaml @@ -0,0 +1,35 @@ +# +#=== Profile average wind speed ===# +# + +window begin: 2019-06-14T20:30:00Z +window end: 2019-06-15T03:30:00Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_windspeed_obs_extended.nc4 + obsgrouping: + group variables: [ "station_id" ] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [eastward_wind, northward_wind] + extension: + average profiles onto model levels: 71 + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals_extended.nc4 + obs filters: + - filter: Profile Consistency Checks + filter variables: + - name: eastward_wind + - name: northward_wind + Checks: ["Basic", "AveragePressure", "AverageWindSpeed"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + HofX: HofX + obs diagnostics: + passedBenchmark: 3318 + benchmarkFlag: 24 + flaggedBenchmark: 264 diff --git a/test/testinput/profileconsistencychecks_bkgqc_modobs_OPScomparison.yaml b/test/testinput/profileconsistencychecks_bkgqc_modobs_OPScomparison.yaml index 710c6e59c..5e4850f44 100644 --- a/test/testinput/profileconsistencychecks_bkgqc_modobs_OPScomparison.yaml +++ b/test/testinput/profileconsistencychecks_bkgqc_modobs_OPScomparison.yaml @@ -10,11 +10,18 @@ Sondes: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_modobs.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: ["air_temperature", "relative_humidity", "eastward_wind", "northward_wind", "geopotential_height"] obs diagnostics: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_obsdiagnostics_modobs.nc4 + variables: + - name: air_temperature_background_error@ObsDiag + - name: relative_humidity_background_error@ObsDiag + - name: eastward_wind_background_error@ObsDiag + - name: northward_wind_background_error@ObsDiag + - name: geopotential_height_background_error@ObsDiag ProfileConsistencyChecks: Checks: ["Basic", "Time", "PermanentReject", "BackgroundTemperature", "BackgroundRelativeHumidity", "BackgroundWindSpeed", "BackgroundGeopotentialHeight"] compareWithOPS: true diff --git a/test/testinput/profileconsistencychecks_bkgqc_modobs_obsfilter.yaml b/test/testinput/profileconsistencychecks_bkgqc_modobs_obsfilter.yaml index 3b5d91ace..984cd4d2d 100644 --- a/test/testinput/profileconsistencychecks_bkgqc_modobs_obsfilter.yaml +++ b/test/testinput/profileconsistencychecks_bkgqc_modobs_obsfilter.yaml @@ -11,7 +11,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_modobs.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, relative_humidity, eastward_wind, northward_wind, geopotential_height] @@ -31,6 +31,13 @@ observations: ModelLevels: true HofX: HofX obs diagnostics: - passedBenchmark: 140 - benchmarkFlag: 15 - flaggedBenchmark: 212 + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_obsdiagnostics_modobs.nc4 + variables: + - name: air_temperature_background_error@ObsDiag + - name: relative_humidity_background_error@ObsDiag + - name: eastward_wind_background_error@ObsDiag + - name: northward_wind_background_error@ObsDiag + - name: geopotential_height_background_error@ObsDiag + passedBenchmark: 328 + benchmarkFlag: 24 + flaggedBenchmark: 24 diff --git a/test/testinput/profileconsistencychecks_bkgqc_modobs_separatefilters_obsfilter.yaml b/test/testinput/profileconsistencychecks_bkgqc_modobs_separatefilters_obsfilter.yaml new file mode 100644 index 000000000..871b996ba --- /dev/null +++ b/test/testinput/profileconsistencychecks_bkgqc_modobs_separatefilters_obsfilter.yaml @@ -0,0 +1,108 @@ +# +#=== Profile consistency checks for background QC on model levels using sequential separate filters ==# +# + +window begin: 2019-06-14T21:00:00Z +window end: 2019-06-15T02:59:59Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_modobs.nc4 + obsgrouping: + group variables: [ "station_id" ] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [air_temperature, relative_humidity, eastward_wind, northward_wind, geopotential_height] + obs filters: + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "Time"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: true + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "PermanentReject"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: true + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "BackgroundTemperature"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: true + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "BackgroundRelativeHumidity"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: true + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "BackgroundWindSpeed"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: true + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "BackgroundGeopotentialHeight"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: true + HofX: HofX + obs diagnostics: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_obsdiagnostics_modobs.nc4 + variables: + - name: air_temperature_background_error@ObsDiag + - name: relative_humidity_background_error@ObsDiag + - name: eastward_wind_background_error@ObsDiag + - name: northward_wind_background_error@ObsDiag + - name: geopotential_height_background_error@ObsDiag + passedBenchmark: 328 + benchmarkFlag: 24 + flaggedBenchmark: 24 diff --git a/test/testinput/profileconsistencychecks_bkgqc_repobs_OPScomparison.yaml b/test/testinput/profileconsistencychecks_bkgqc_repobs_OPScomparison.yaml index 9f0be0a4a..98d7fbfe0 100644 --- a/test/testinput/profileconsistencychecks_bkgqc_repobs_OPScomparison.yaml +++ b/test/testinput/profileconsistencychecks_bkgqc_repobs_OPScomparison.yaml @@ -10,11 +10,18 @@ Sondes: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_repobs.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: ["air_temperature", "relative_humidity", "eastward_wind", "northward_wind", "geopotential_height"] obs diagnostics: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_obsdiagnostics_repobs.nc4 + variables: + - name: air_temperature_background_error@ObsDiag + - name: relative_humidity_background_error@ObsDiag + - name: eastward_wind_background_error@ObsDiag + - name: northward_wind_background_error@ObsDiag + - name: geopotential_height_background_error@ObsDiag ProfileConsistencyChecks: Checks: ["Basic", "Time", "PermanentReject", "BackgroundTemperature", "BackgroundRelativeHumidity", "BackgroundWindSpeed", "BackgroundGeopotentialHeight"] compareWithOPS: true diff --git a/test/testinput/profileconsistencychecks_bkgqc_repobs_obsfilter.yaml b/test/testinput/profileconsistencychecks_bkgqc_repobs_obsfilter.yaml index e7cce7767..c256ba88c 100644 --- a/test/testinput/profileconsistencychecks_bkgqc_repobs_obsfilter.yaml +++ b/test/testinput/profileconsistencychecks_bkgqc_repobs_obsfilter.yaml @@ -11,7 +11,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_repobs.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, relative_humidity, eastward_wind, northward_wind, geopotential_height] @@ -31,6 +31,13 @@ observations: ModelLevels: false HofX: HofX obs diagnostics: - passedBenchmark: 449 - benchmarkFlag: 15 - flaggedBenchmark: 5 + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_obsdiagnostics_repobs.nc4 + variables: + - name: air_temperature_background_error@ObsDiag + - name: relative_humidity_background_error@ObsDiag + - name: eastward_wind_background_error@ObsDiag + - name: northward_wind_background_error@ObsDiag + - name: geopotential_height_background_error@ObsDiag + passedBenchmark: 454 + benchmarkFlag: 24 + flaggedBenchmark: 0 diff --git a/test/testinput/profileconsistencychecks_bkgqc_repobs_separatefilters_obsfilter.yaml b/test/testinput/profileconsistencychecks_bkgqc_repobs_separatefilters_obsfilter.yaml new file mode 100644 index 000000000..b2ddc67a2 --- /dev/null +++ b/test/testinput/profileconsistencychecks_bkgqc_repobs_separatefilters_obsfilter.yaml @@ -0,0 +1,108 @@ +# +#=== Profile consistency checks for background QC on reported levels using sequential separate filters ==# +# + +window begin: 2019-06-14T21:00:00Z +window end: 2019-06-15T02:59:59Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_repobs.nc4 + obsgrouping: + group variables: [ "station_id" ] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [air_temperature, relative_humidity, eastward_wind, northward_wind, geopotential_height] + obs filters: + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "Time"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: false + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "PermanentReject"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: false + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "BackgroundTemperature"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: false + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "BackgroundRelativeHumidity"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: false + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "BackgroundWindSpeed"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: false + - filter: Profile Consistency Checks + filter variables: + - name: air_temperature + - name: relative_humidity + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + Checks: ["Basic", "BackgroundGeopotentialHeight"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + BChecks_Skip: true + ModelLevels: false + HofX: HofX + obs diagnostics: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_obsdiagnostics_repobs.nc4 + variables: + - name: air_temperature_background_error@ObsDiag + - name: relative_humidity_background_error@ObsDiag + - name: eastward_wind_background_error@ObsDiag + - name: northward_wind_background_error@ObsDiag + - name: geopotential_height_background_error@ObsDiag + passedBenchmark: 454 + benchmarkFlag: 24 + flaggedBenchmark: 0 diff --git a/test/testinput/profileconsistencychecks_monolithicfilter.yaml b/test/testinput/profileconsistencychecks_monolithicfilter.yaml index fb2af0fd7..e2927e39d 100644 --- a/test/testinput/profileconsistencychecks_monolithicfilter.yaml +++ b/test/testinput/profileconsistencychecks_monolithicfilter.yaml @@ -11,10 +11,12 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, geopotential_height] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_geovals.nc4 obs filters: - filter: Profile Consistency Checks filter variables: @@ -30,5 +32,5 @@ observations: HofX: HofX obs diagnostics: passedBenchmark: 901 - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 10057 diff --git a/test/testinput/profileconsistencychecks_opr_average.yaml b/test/testinput/profileconsistencychecks_opr_average.yaml new file mode 100644 index 000000000..a605c3354 --- /dev/null +++ b/test/testinput/profileconsistencychecks_opr_average.yaml @@ -0,0 +1,74 @@ +window begin: 2019-06-14T20:30:00Z +window end: 2019-06-15T03:30:00Z + +# rms ref is zero because only the auxiliary levels are tested +# todo(ctgh): in a future PR, the simulated variables will be treated correctly. +# When this occurs, use 'vector ref' with the correct reference values. + +observations: +# Standard case. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_cxinterpolation_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [air_temperature] + extension: + average profiles onto model levels: 71 + obs operator: + name: ProfileAverage + compareWithOPS: true + numIntersectionIterations: 3 + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_cxinterpolation_geovals.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 0 + tolerance: 1.0e-06 +# Group variables configuration is empty, throwing an exception. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_cxinterpolation_obs.nc4 + simulated variables: [air_temperature] + extension: + average profiles onto model levels: 71 + expect constructor to throw exception with message: Group variables configuration is empty + obs operator: + name: ProfileAverage + compareWithOPS: true + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_cxinterpolation_geovals.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 0 + tolerance: 1.0e-06 +# Extended ObsSpace is not used, throwing an exception. +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_cxinterpolation_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [air_temperature] + obs operator: + name: ProfileAverage + compareWithOPS: true + expect constructor to throw exception with message: The extended obs space has not been produced + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_cxinterpolation_geovals.nc4 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-11 + tolerance AD: 1.0e-13 + rms ref: 0 + tolerance: 1.0e-06 diff --git a/test/testinput/profileconsistencychecks_pressure_OPScomparison.yaml b/test/testinput/profileconsistencychecks_pressure_OPScomparison.yaml new file mode 100644 index 000000000..fc115f367 --- /dev/null +++ b/test/testinput/profileconsistencychecks_pressure_OPScomparison.yaml @@ -0,0 +1,25 @@ +# +#=== Profile pressure: comparison with OPS values ===# +# + +Sondes: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_pressure_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + #Sorting is not performed for this test in order to ensure exact correspondence with OPS + simulated variables: [geopotential_height] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_pressure_geovals.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "Pressure"] + compareWithOPS: true + BChecks_Skip: true + flagBasicChecksFail: false + PrintStationID: true + Comparison_Tol: 0.1 diff --git a/test/testinput/profileconsistencychecks_pressure_obsfilter.yaml b/test/testinput/profileconsistencychecks_pressure_obsfilter.yaml new file mode 100644 index 000000000..680a5be24 --- /dev/null +++ b/test/testinput/profileconsistencychecks_pressure_obsfilter.yaml @@ -0,0 +1,31 @@ +# +#=== Profile pressure ===# +# + +window begin: 2019-06-14T20:30:00Z +window end: 2019-06-15T03:30:00Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests_vertinterp_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [geopotential_height] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests_vertinterp_geovals.nc4 + obs filters: + - filter: Profile Consistency Checks + filter variables: + - name: geopotential_height + Checks: ["Basic", "Pressure"] + compareWithOPS: false + BChecks_Skip: true + flagBasicChecksFail: false + PrintStationID: true + HofX: HofX + obs diagnostics: + passedBenchmark: 21 + benchmarkFlag: 24 + flaggedBenchmark: 0 diff --git a/test/testinput/profileconsistencychecks_separatefilters.yaml b/test/testinput/profileconsistencychecks_separatefilters.yaml index a19d8f6f6..cf2f96612 100644 --- a/test/testinput/profileconsistencychecks_separatefilters.yaml +++ b/test/testinput/profileconsistencychecks_separatefilters.yaml @@ -11,10 +11,12 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, geopotential_height] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_geovals.nc4 obs filters: - filter: Profile Consistency Checks filter variables: @@ -53,5 +55,5 @@ observations: HofX: HofX obs diagnostics: passedBenchmark: 901 - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 10057 diff --git a/test/testinput/profileconsistencychecks_sondeflags_OPScomparison.yaml b/test/testinput/profileconsistencychecks_sondeflags_OPScomparison.yaml new file mode 100644 index 000000000..11b02cc27 --- /dev/null +++ b/test/testinput/profileconsistencychecks_sondeflags_OPScomparison.yaml @@ -0,0 +1,22 @@ +# +#=== Setting a variety of sonde flags: comparison with OPS values ===# +# + +Sondes: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_sondeflags.nc4 + obsgrouping: + group variables: [ "station_id" ] + #Sorting is not performed for this test in order to ensure exact correspondence with OPS + simulated variables: [eastward_wind] + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "SondeFlags"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 diff --git a/test/testinput/profileconsistencychecks_sondeflags_obsfilter.yaml b/test/testinput/profileconsistencychecks_sondeflags_obsfilter.yaml new file mode 100644 index 000000000..1da3c02f7 --- /dev/null +++ b/test/testinput/profileconsistencychecks_sondeflags_obsfilter.yaml @@ -0,0 +1,30 @@ +# +#=== Setting a variety of sonde flags ===# +# + +window begin: 2019-06-14T20:30:00Z +window end: 2019-06-15T03:30:00Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_sondeflags.nc4 + obsgrouping: + group variables: [ "station_id" ] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [eastward_wind] + obs filters: + - filter: Profile Consistency Checks + filter variables: + - name: eastward_wind + Checks: ["Basic", "SondeFlags"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + HofX: HofX + obs diagnostics: + passedBenchmark: 1714 + benchmarkFlag: 24 + flaggedBenchmark: 77 diff --git a/test/testinput/profileconsistencychecks_unittest_oneprofileMPI.yaml b/test/testinput/profileconsistencychecks_unittest_oneprofileMPI.yaml index 372b44272..71199eb12 100644 --- a/test/testinput/profileconsistencychecks_unittest_oneprofileMPI.yaml +++ b/test/testinput/profileconsistencychecks_unittest_oneprofileMPI.yaml @@ -11,10 +11,12 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, geopotential_height] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile_geovals.nc4 obs filters: - filter: Profile Consistency Checks filter variables: @@ -29,5 +31,5 @@ observations: is_in: 0 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 69 diff --git a/test/testinput/profileconsistencychecks_unittests.yaml b/test/testinput/profileconsistencychecks_unittests.yaml index dfd5f3c9a..d5d82c3a6 100644 --- a/test/testinput/profileconsistencychecks_unittests.yaml +++ b/test/testinput/profileconsistencychecks_unittests.yaml @@ -25,7 +25,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, geopotential_height] @@ -43,7 +43,7 @@ observations: is_in: 0 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 0 #=== Standard sonde profile (test_id = 0), no sorting ===# #Should be identical to the case with sorting because the indices are already in the correct order @@ -52,7 +52,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, geopotential_height] obs filters: - filter: Profile Consistency Checks @@ -68,7 +68,7 @@ observations: is_in: 0 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 0 #=== Standard sonde profile (test_id = 0), no checks defined ===# #Basic checks should be added automatically @@ -77,7 +77,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, geopotential_height] @@ -95,7 +95,7 @@ observations: is_in: 0 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 0 #=== Sonde with highest pressure > 110e3 Pa (test_id = 1), reject if failed basic checks ===# #This profile is rejected by the basic checks so none of its observations pass @@ -104,7 +104,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, geopotential_height] @@ -122,7 +122,7 @@ observations: is_in: 1 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 69 #=== Sonde with highest pressure > 110e3 Pa (test_id = 1), don't reject if failed basic checks ===# #The sonde with a pressure too large (test_id = 1) is selected by the where clause but it fails the basic checks @@ -133,7 +133,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, geopotential_height] @@ -151,7 +151,7 @@ observations: is_in: 1 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 0 #=== Sonde with negative pressure reading (test_id = 2) ===# #This profile is rejected by the basic checks so none of its observations pass @@ -160,7 +160,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, geopotential_height] @@ -178,7 +178,7 @@ observations: is_in: 2 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 69 #=== No data (invalid test_id) ===# #No observations are expected to be processed by the filter so the number expected is the @@ -188,7 +188,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, geopotential_height] @@ -206,7 +206,7 @@ observations: is_in: 999 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 0 #=== Sonde with pressures out of order (test_id = 3), sorting by pressure ===# #After sorting this profile should be the same as the original sonde (test_id = 0), so expect the same observations to pass @@ -215,7 +215,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, geopotential_height] @@ -233,7 +233,7 @@ observations: is_in: 3 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 0 #=== Sonde with pressures out of order (test_id = 3), not sorting by pressure ===# #The lack of sorting will mean this sonde fails the basic checks @@ -242,7 +242,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, geopotential_height] obs filters: - filter: Profile Consistency Checks @@ -258,7 +258,7 @@ observations: is_in: 3 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 69 #=== Sonde with all pressures missing (test_id = 4) ===# #Expect this sonde to fail the basic checks @@ -267,7 +267,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, geopotential_height] obs filters: - filter: Profile Consistency Checks @@ -283,7 +283,7 @@ observations: is_in: 4 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 69 #=== Sonde with all temperatures missing (test_id = 5) ===# #Basic checks still passed since they only depend on pressure @@ -294,7 +294,7 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, geopotential_height] obs filters: - filter: Profile Consistency Checks @@ -310,7 +310,7 @@ observations: is_in: 5 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 0 #=== Sonde with all temperatures missing (test_id = 5), all checks ===# #Basic checks still passed since they only depend on pressure @@ -320,8 +320,10 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, geopotential_height] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_geovals.nc4 obs filters: - filter: Profile Consistency Checks filter variables: @@ -336,5 +338,5 @@ observations: is_in: 5 HofX: HofX obs diagnostics: - benchmarkFlag: 15 + benchmarkFlag: 24 flaggedBenchmark: 0 diff --git a/test/testinput/profileconsistencychecks_unittests_profiledataholder.yaml b/test/testinput/profileconsistencychecks_unittests_profiledataholder.yaml new file mode 100644 index 000000000..41610719c --- /dev/null +++ b/test/testinput/profileconsistencychecks_unittests_profiledataholder.yaml @@ -0,0 +1,22 @@ +# +#=== Profile consistency checks: unit tests of ProfileDataHolder ===# +# + +ProfileDataHolder: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [air_temperature] + extension: + average profiles onto model levels: 71 + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile_geovals.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic"] + testProfileDataHolder: true diff --git a/test/testinput/profileconsistencychecks_unittests_vertaverage.yaml b/test/testinput/profileconsistencychecks_unittests_vertaverage.yaml new file mode 100644 index 000000000..7f2b2ae6b --- /dev/null +++ b/test/testinput/profileconsistencychecks_unittests_vertaverage.yaml @@ -0,0 +1,21 @@ +# +#=== Profile consistency checks: unit tests of vertical averaging routine ===# +# + +Vertical averaging: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests_vertaverage.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [eastward_wind] + HofX: HofX + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic"] + compareWithOPS: true + BypassMismatchComparison: true + testProfileVerticalAveraging: true diff --git a/test/testinput/profileconsistencychecks_unittests_vertaverage_ascending.yaml b/test/testinput/profileconsistencychecks_unittests_vertaverage_ascending.yaml new file mode 100644 index 000000000..b039a6cdb --- /dev/null +++ b/test/testinput/profileconsistencychecks_unittests_vertaverage_ascending.yaml @@ -0,0 +1,21 @@ +# +#=== Profile consistency checks: unit tests of vertical averaging routine with ascending vertical coordinate ===# +# + +Vertical averaging: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests_vertaverage_ascending.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [eastward_wind] + HofX: HofX + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic"] + compareWithOPS: true + BypassMismatchComparison: true + testProfileVerticalAveraging: true diff --git a/test/testinput/profileconsistencychecks_unittests_vertinterp.yaml b/test/testinput/profileconsistencychecks_unittests_vertinterp.yaml new file mode 100644 index 000000000..472370222 --- /dev/null +++ b/test/testinput/profileconsistencychecks_unittests_vertinterp.yaml @@ -0,0 +1,27 @@ +# +#=== Profile consistency checks: unit tests of vertical interpolation routine ===# +# + +Vertical interpolation: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests_vertinterp_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [geopotential_height] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_unittests_vertinterp_geovals.nc4 + HofX: HofX + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "Pressure"] + compareWithOPS: true + BypassMismatchComparison: true + testProfileVerticalInterpolation: true + interpMethodNames: [Linear, Linear, LogLinear, Linear, Linear, Linear, Linear, Linear, Linear, Linear, Linear, Linear, Linear, Linear, Linear, Linear, Linear, Linear, Linear, Linear] + coordOrderNames: [Ascending, Ascending, Ascending, Descending, Descending, Descending, Descending, Descending, Descending, Descending, Ascending, Ascending, Ascending, Ascending, Ascending, Ascending, Ascending, Ascending, Ascending, Ascending] + outOfBoundsNames: [SetToBound, SetToBound, SetToBound, SetToBound, SetMissing, SetToBound, Extrapolate, SetMissing, SetToBound, Extrapolate, SetMissing, SetToBound, Extrapolate, SetMissing, SetToBound, Extrapolate, SetToBound, SetToBound, SetToBound, SetToBound] + diff --git a/test/testinput/profileconsistencychecks_winproflags_OPScomparison.yaml b/test/testinput/profileconsistencychecks_winproflags_OPScomparison.yaml new file mode 100644 index 000000000..1c80f08f8 --- /dev/null +++ b/test/testinput/profileconsistencychecks_winproflags_OPScomparison.yaml @@ -0,0 +1,22 @@ +# +#=== Checking wind profiler flags: comparison with OPS values ===# +# + +Sondes: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_winproflags.nc4 + obsgrouping: + group variables: [ "station_id" ] + #Sorting is not performed for this test in order to ensure exact correspondence with OPS + simulated variables: [eastward_wind] + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "WinProFlags"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 diff --git a/test/testinput/profileconsistencychecks_winproflags_obsfilter.yaml b/test/testinput/profileconsistencychecks_winproflags_obsfilter.yaml new file mode 100644 index 000000000..0f177b2df --- /dev/null +++ b/test/testinput/profileconsistencychecks_winproflags_obsfilter.yaml @@ -0,0 +1,30 @@ +# +#=== Checking wind profiler flags ===# +# + +window begin: 2019-06-14T20:30:00Z +window end: 2019-06-15T03:30:00Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_winproflags.nc4 + obsgrouping: + group variables: [ "station_id" ] + sort variable: "air_pressure" + sort order: "descending" + simulated variables: [eastward_wind] + obs filters: + - filter: Profile Consistency Checks + filter variables: + - name: eastward_wind + Checks: ["Basic", "WinProFlags"] + compareWithOPS: false + flagBasicChecksFail: true + PrintStationID: true + HofX: HofX + obs diagnostics: + passedBenchmark: 59 + benchmarkFlag: 24 + flaggedBenchmark: 77 diff --git a/test/testinput/profileconsistencychecks_wrongOPScomparison.yaml b/test/testinput/profileconsistencychecks_wrongOPScomparison.yaml index 831a13b1a..6876bb664 100644 --- a/test/testinput/profileconsistencychecks_wrongOPScomparison.yaml +++ b/test/testinput/profileconsistencychecks_wrongOPScomparison.yaml @@ -16,16 +16,17 @@ Incorrect and missing values, RH: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_rh_wrong.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, relative_humidity, dew_point_temperature] obs diagnostics: ProfileConsistencyChecks: - Checks: ["Basic", "SamePDiffT", "Sign", "UnstableLayer", "Interpolation", "Hydrostatic", "RH", "UInterp", "Time", "PermanentReject", "BackgroundTemperature", "BackgroundRelativeHumidity", "BackgroundWindSpeed", "BackgroundGeopotentialHeight"] + Checks: ["Basic", "SamePDiffT", "Sign", "UnstableLayer", "Interpolation", "Hydrostatic", "RH", "UInterp", "Time", "PermanentReject", "BackgroundTemperature", "BackgroundRelativeHumidity", "BackgroundWindSpeed", "BackgroundGeopotentialHeight", "Pressure"] compareWithOPS: true BChecks_Skip: true flagBasicChecksFail: true Comparison_Tol: 0.1 BypassMismatchComparison: true + IgnoreGeoVaLs: true #One of the variables is missing in a different sample (for completeness). Incorrect and missing values: @@ -36,16 +37,17 @@ Incorrect and missing values: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile_wrong.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, geopotential_height] obs diagnostics: ProfileConsistencyChecks: - Checks: ["Basic", "SamePDiffT", "Sign", "UnstableLayer", "Interpolation", "Hydrostatic", "RH", "UInterp", "Time", "PermanentReject", "BackgroundTemperature", "BackgroundRelativeHumidity", "BackgroundWindSpeed", "BackgroundGeopotentialHeight"] + Checks: ["Basic", "SamePDiffT", "Sign", "UnstableLayer", "Interpolation", "Hydrostatic", "RH", "UInterp", "Time", "PermanentReject", "BackgroundTemperature", "BackgroundRelativeHumidity", "BackgroundWindSpeed", "BackgroundGeopotentialHeight", "Pressure", "AverageTemperature", "AverageWindSpeed", "AverageRelativeHumidity"] compareWithOPS: true BChecks_Skip: true flagBasicChecksFail: true Comparison_Tol: 0.1 BypassMismatchComparison: true + IgnoreGeoVaLs: true #One of the variables is missing, basic check is run. Incorrect and missing values, Basic: @@ -56,7 +58,7 @@ Incorrect and missing values, Basic: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile_wrong.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, geopotential_height] obs diagnostics: ProfileConsistencyChecks: @@ -76,8 +78,10 @@ Wrong number of entries per profile: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, geopotential_height] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile_geovals.nc4 obs diagnostics: ProfileConsistencyChecks: Checks: ["SamePDiffT", "Basic", "Sign", "UnstableLayer", "Interpolation", "Hydrostatic"] @@ -98,7 +102,7 @@ Wrong number of entries per profile, UInterp: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_uinterp.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [eastward_wind, northward_wind] obs diagnostics: ProfileConsistencyChecks: @@ -110,6 +114,26 @@ Wrong number of entries per profile, UInterp: groups_singlevalue: ["Counters", "ObsValue"] BypassMismatchComparison: true +#UInterpAlternative: all ObsValue variables are given one value per profile. +#This is designed to fail the check that all variables are the same size. +Wrong number of entries per profile, UInterpAlternative: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_uinterp.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [eastward_wind, northward_wind] + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["UInterpAlternative"] + BChecks_Skip: true + flagBasicChecksFail: true + Comparison_Tol: 0.1 + groups_singlevalue: ["Counters", "ObsValue"] + #RH: all ObsValue variables are given one value per profile. #This is designed to fail the check that all variables are the same size. Wrong number of entries per profile, RH: @@ -120,7 +144,7 @@ Wrong number of entries per profile, RH: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_rh.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, relative_humidity, dew_point_temperature] obs diagnostics: ProfileConsistencyChecks: @@ -142,8 +166,8 @@ Wrong number of entries per profile, background: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_repobs.nc4 obsgrouping: - group variable: "station_id" - simulated variables: [eastward_wind, northward_wind] + group variables: [ "station_id" ] + simulated variables: [air_temperature, relative_humidity, eastward_wind, northward_wind, geopotential_height] obs diagnostics: ProfileConsistencyChecks: Checks: ["Time", "PermanentReject", "BackgroundTemperature", "BackgroundRelativeHumidity", "BackgroundWindSpeed", "BackgroundGeopotentialHeight"] @@ -154,6 +178,103 @@ Wrong number of entries per profile, background: groups_singlevalue: ["Counters", "ObsValue", "MetaData"] BypassMismatchComparison: true +#Pressure transformation: all ObsValue variables are given one value per profile. +#This is designed to fail the check that all variables are the same size. +Wrong number of entries per profile, pressure transformation: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_temperature_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [air_temperature] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AveragePressure"] + compareWithOPS: true + BChecks_Skip: true + flagBasicChecksFail: true + Comparison_Tol: 0.1 + groups_singlevalue: ["QCFlags"] + BypassMismatchComparison: true + ExpectThrowDuringOperation: true + +#Temperature averaging: all ObsValue variables are given one value per profile. +#This is designed to fail the check that all variables are the same size. +Wrong number of entries per profile, temperature averaging: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_temperature_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [air_temperature] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AveragePressure", "AverageTemperature"] + compareWithOPS: true + BChecks_Skip: true + flagBasicChecksFail: true + Comparison_Tol: 0.1 + groups_singlevalue: ["ObsValue"] + BypassMismatchComparison: true + +#Wind speed averaging: all ObsValue variables are given one value per profile. +#This is designed to fail the check that all variables are the same size. +Wrong number of entries per profile, wind speed averaging: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_windspeed_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AveragePressure", "AverageWindSpeed"] + compareWithOPS: true + BChecks_Skip: true + flagBasicChecksFail: true + Comparison_Tol: 0.1 + groups_singlevalue: ["ObsValue"] + BypassMismatchComparison: true + +#Relative humidity averaging: all ObsValue variables are given one value per profile. +#This is designed to fail the check that all variables are the same size. +Wrong number of entries per profile, wind speed averaging: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_relativehumidity_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [relative_humidity] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AveragePressure", "AverageRelativeHumidity"] + compareWithOPS: true + BChecks_Skip: true + flagBasicChecksFail: true + Comparison_Tol: 0.1 + groups_singlevalue: ["ObsValue"] + BypassMismatchComparison: true + #A group variable is not chosen, throwing an exception No group variable chosen: window begin: 2000-01-01T00:00:00Z @@ -182,7 +303,7 @@ Ascending sort order: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_rh.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "ascending" simulated variables: [air_temperature, relative_humidity, dew_point_temperature] @@ -205,7 +326,7 @@ Invalid check requested: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_rh.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, relative_humidity, dew_point_temperature] obs diagnostics: ProfileConsistencyChecks: @@ -215,7 +336,7 @@ Invalid check requested: flagBasicChecksFail: true Comparison_Tol: 0.1 BypassMismatchComparison: true - ExpectThrowDuringOperation: true + ExpectThrowOnInstantiation: true #A duplicate check is requested, throwing an exception Duplicate check requested: @@ -226,8 +347,10 @@ Duplicate check requested: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, geopotential_height] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile_geovals.nc4 obs diagnostics: ProfileConsistencyChecks: Checks: ["Sign"] @@ -247,8 +370,10 @@ Incorrect type in get: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, geopotential_height] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile_geovals.nc4 obs diagnostics: ProfileConsistencyChecks: Checks: ["Sign"] @@ -268,7 +393,7 @@ Standard levels out of order: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_oneprofile.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "air_pressure" sort order: "descending" simulated variables: [air_temperature, geopotential_height] @@ -292,7 +417,7 @@ Background SondeLaunchWindRej: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_repobs.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [eastward_wind, northward_wind] obs diagnostics: ProfileConsistencyChecks: @@ -313,7 +438,7 @@ Background manual QC flags: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_bkgqc_repobs.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, relative_humidity, eastward_wind, northward_wind, geopotential_height] obs diagnostics: ProfileConsistencyChecks: @@ -324,3 +449,166 @@ Background manual QC flags: Comparison_Tol: 0.1 BypassMismatchComparison: true ManualFlagModification: true + +#Do not run profile pressure routine before temperature averaging +Profile pressure not run before temperature averaging: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_temperature_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [air_temperature] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AverageTemperature"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 + BypassMismatchComparison: true + ExpectThrowDuringOperation: true + +#GeoVaLs missing for temperature averaging (because set IgnoreGeoVaLs to true): +Missing GeoVaLs for temperature averaging: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_temperature_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [air_temperature] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_temperature.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AverageTemperature"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 + BypassMismatchComparison: true + IgnoreGeoVaLs: true + ExpectThrowDuringOperation: true + +#Do not run profile pressure routine before wind speed averaging +Profile pressure not run before wind speed averaging: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_windspeed_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AverageWindSpeed"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 + BypassMismatchComparison: true + ExpectThrowDuringOperation: true + +#GeoVaLs missing for wind speed averaging (because set IgnoreGeoVaLs to true): +Missing GeoVaLs for wind speed averaging: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_windspeed_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_windspeed.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AverageWindSpeed"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 + BypassMismatchComparison: true + IgnoreGeoVaLs: true + ExpectThrowDuringOperation: true + +#Do not run profile pressure routine before relative humidity averaging +Profile pressure not run before relative humidity averaging: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_relativehumidity_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [relative_humidity] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_geovals.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AverageRelativeHumidity"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 + BypassMismatchComparison: true + ExpectThrowDuringOperation: true + +#GeoVaLs missing for relative humidity averaging (because set IgnoreGeoVaLs to true): +Missing GeoVaLs for relative humidity averaging: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_relativehumidity_obs.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [relative_humidity] + geovals: + filename: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks_average_relativehumidity.nc4 + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "AverageRelativeHumidity"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 + BypassMismatchComparison: true + IgnoreGeoVaLs: true + ExpectThrowDuringOperation: true + +#GeoVaLs missing for sign check (because set IgnoreGeoVaLs to true): +Missing GeoVaLs for sign check: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_profile_consistency_checks.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [air_temperature] + obs diagnostics: + ProfileConsistencyChecks: + Checks: ["Basic", "Sign"] + compareWithOPS: true + flagBasicChecksFail: true + PrintStationID: true + Comparison_Tol: 0.1 + BypassMismatchComparison: true + IgnoreGeoVaLs: true + ExpectThrowDuringOperation: true diff --git a/test/testinput/qc_acceptlist.yaml b/test/testinput/qc_acceptlist.yaml new file mode 100644 index 000000000..e7b43c5ff --- /dev/null +++ b/test/testinput/qc_acceptlist.yaml @@ -0,0 +1,59 @@ +window begin: 2018-01-01T00:00:00Z +window end: 2019-01-01T00:00:00Z + +observations: +# Use the default actions of the RejectList and AcceptList filters +- obs space: + name: Standard + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2] + HofX: HofX + obs filters: + # reject observations with var1 >= 6 + - filter: RejectList + # the default action for RejectList is "reject" + where: + - variable: + name: var1@MetaData # = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + minvalue: 6 + # now "accept back" all rejected observations of variable2 with var1 >= 3 and var1 <= 7 + - filter: AcceptList + # the default action for AcceptList is "accept" + filter variables: [variable2] + where: + - variable: + name: var1@MetaData # = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + minvalue: 3 + maxvalue: 7 + # all observations of variable1 with var1 <= 5 and of variable2 with var1 <= 7 should be accepted + passedBenchmark: 12 +# Check it is possible to flip the actions of the RejectList and AcceptList filters +- obs space: + name: Flipped + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2] + HofX: HofX + obs filters: + # reject observations with var1 >= 6 + - filter: AcceptList + action: + name: reject + where: + - variable: + name: var1@MetaData # = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + minvalue: 6 + # now "accept back" all rejected observations of variable2 with var1 >= 3 and var1 <= 7 + - filter: RejectList + action: + name: accept + filter variables: [variable2] + where: + - variable: + name: var1@MetaData # = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + minvalue: 3 + maxvalue: 7 + # all observations of variable1 with var1 <= 5 and of variable2 with var1 <= 7 should be accepted + passedBenchmark: 12 + diff --git a/test/testinput/qc_actions.yaml b/test/testinput/qc_actions.yaml new file mode 100644 index 000000000..153b57e14 --- /dev/null +++ b/test/testinput/qc_actions.yaml @@ -0,0 +1,148 @@ +window begin: 2018-01-01T00:00:00Z +window end: 2019-01-01T00:00:00Z + +observations: +# Test "inflate error" with a constant inflation factor +# and only second variable filtered +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2, variable3] + HofX: HofX + obs filters: + - filter: BlackList + filter variables: + - name: variable2 + action: + name: inflate error + inflation factor: 2.0 + compareVariables: + - test: + name: variable1@EffectiveError + reference: + name: variable1@ObsError + - test: + name: variable2@EffectiveError + reference: + name: variable2_inflatederror_factor@TestReference + - test: + name: variable3@EffectiveError + reference: + name: variable3@ObsError + +# Test "inflate error" with a constant inflation factor +# and all variables filtered +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2, variable3] + HofX: HofX + obs filters: + - filter: BlackList + action: + name: inflate error + inflation factor: 2.0 + compareVariables: + - test: + name: variable1@EffectiveError + reference: + name: variable1_inflatederror_factor@TestReference + - test: + name: variable2@EffectiveError + reference: + name: variable2_inflatederror_factor@TestReference + - test: + name: variable3@EffectiveError + reference: + name: variable3_inflatederror_factor@TestReference + + +# Test "inflate error" with an inflation variable (metadata) +# and only second variable filtered +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2, variable3] + HofX: HofX + obs filters: + - filter: BlackList + filter variables: + - name: variable2 + action: + name: inflate error + inflation variable: + name: var1@MetaData + compareVariables: + - test: + name: variable1@EffectiveError + reference: + name: variable1@ObsError + - test: + name: variable2@EffectiveError + reference: + name: variable2_inflatederror_var@TestReference + - test: + name: variable3@EffectiveError + reference: + name: variable3@ObsError + + +# Test "inflate error" with an inflation variable (metadata) +# and all variables filtered +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2, variable3] + HofX: HofX + obs filters: + - filter: BlackList + action: + name: inflate error + inflation variable: + name: var1@MetaData + compareVariables: + - test: + name: variable1@EffectiveError + reference: + name: variable1_inflatederror_var@TestReference + - test: + name: variable2@EffectiveError + reference: + name: variable2_inflatederror_var@TestReference + - test: + name: variable3@EffectiveError + reference: + name: variable3_inflatederror_var@TestReference + +# Test "accept" +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2] + HofX: HofX + obs filters: + # reject observations with var1 >= 6 + - filter: BlackList + action: + name: reject + where: + - variable: + name: var1@MetaData # = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + minvalue: 6 + # now "accept back" all rejected observations of variable2 with var1 >= 3 and var1 <= 7 + - filter: AcceptList + action: + name: accept + filter variables: [variable2] + where: + - variable: + name: var1@MetaData # = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + minvalue: 3 + maxvalue: 7 + # all observations of variable1 with var1 <= 5 and of variable2 with var1 <= 7 should be accepted + passedBenchmark: 12 diff --git a/test/testinput/qc_bayesian_background_check.yaml b/test/testinput/qc_bayesian_background_check.yaml new file mode 100644 index 000000000..e66879e2b --- /dev/null +++ b/test/testinput/qc_bayesian_background_check.yaml @@ -0,0 +1,58 @@ +window begin: 2020-12-31T23:59:00Z +window end: 2021-01-01T00:01:00Z + +observations: +- obs space: + name: Sea Ice + obsdatain: + obsfile: Data/ufo/testinput_tier_1/bayesianbgcheck_filter_testdata.nc4 + obsdataout: + obsfile: Data/qc_bayesian_background_check_testout_seaice.nc4 + simulated variables: [ice_area_fraction] + HofX: HofX + obs diagnostics: + filename: Data/ufo/testinput_tier_1/background_errors_for_bayesianbgcheck_test.nc4 + obs filters: + - filter: Bayesian Background Check + filter variables: + - name: ice_area_fraction + prob density bad obs: 1.0 + initial prob gross error: 0.04 + PGE threshold: 0.07 # checking can override the default + passedBenchmark: 12 # [11:14,16] missing, [2,5,6,9,10,19] fail, + # [0,1,3:5,7:9,14:16,17:19,20:22] pass == 12 pass +- obs space: + name: Surface + obsdatain: + obsfile: Data/ufo/testinput_tier_1/bayesianbgcheck_filter_testdata.nc4 + obsdataout: + obsfile: Data/qc_bayesian_background_check_testout_surface.nc4 + simulated variables: [air_temperature_at_2m, + eastward_wind, + northward_wind] + HofX: HofX + obs diagnostics: + filename: Data/ufo/testinput_tier_1/background_errors_for_bayesianbgcheck_test.nc4 + obs filters: + - filter: Bayesian Background Check + filter variables: + - name: eastward_wind + options: + first_component_of_two: true + - name: northward_wind + - name: air_temperature_at_2m + prob density bad obs: 1.0 + initial prob gross error: 0.04 + where: + - variable: + name: air_temperature_at_2m@HofX + maxvalue: 275 + - variable: + name: northward_wind@ObsValue + maxvalue: 100 + passedBenchmark: 41 # only act on [0:14,15:21], not filtering [14,21] + # air_temperature_at_2m: [2,11] missing, [1] fail, + # [0,3:11,12:20] pass, [14,21] not filtered == 19 pass + # wind: [2] missing, [0:2,3:6,12:18,21] fail, + # [6:12,18:20] pass, [14,21] not filtered == 11 each + # 19 + 11 + 11 = 41 pass altogether diff --git a/test/testinput/qc_bayesianbackgroundqcflags.yaml b/test/testinput/qc_bayesianbackgroundqcflags.yaml new file mode 100644 index 000000000..903e30794 --- /dev/null +++ b/test/testinput/qc_bayesianbackgroundqcflags.yaml @@ -0,0 +1,71 @@ +window begin: 2019-06-14T21:00:00Z +window end: 2019-06-15T02:59:59Z + +observations: +- obs space: + name: Bayesian background QC flags + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_bayesian_background_qc_flags.nc + simulated variables: [air_temperature, eastward_wind, northward_wind, geopotential_height] + obs filters: + - filter: Bayesian Background QC Flags + filter variables: + - name: air_temperature + - name: eastward_wind + - name: northward_wind + - name: geopotential_height + passedBenchmark: 1830 + compareVariables: + - reference: + name: OPS_air_temperature@QCFlags + test: + name: air_temperature@QCFlags + - reference: + name: OPS_eastward_wind@QCFlags + test: + name: eastward_wind@QCFlags + - reference: + name: OPS_northward_wind@QCFlags + test: + name: northward_wind@QCFlags + - reference: + name: OPS_geopotential_height@QCFlags + test: + name: geopotential_height@QCFlags +- obs space: + name: Missing QC flags + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_bayesian_background_qc_flags_missing_variables.nc + simulated variables: [air_temperature] + obs filters: + - filter: Bayesian Background QC Flags + filter variables: + - name: air_temperature + expectExceptionWithMessage: air_temperature@QCFlags not present + passedBenchmark: 0 +- obs space: + name: Missing PGE + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_bayesian_background_qc_flags_missing_variables.nc + simulated variables: [eastward_wind] + obs filters: + - filter: Bayesian Background QC Flags + filter variables: + - name: eastward_wind + expectExceptionWithMessage: At least one of eastward_wind@GrossErrorProbability or eastward_wind@GrossErrorProbabilityBuddyCheck must be present +- obs space: + name: Bayesian background QC flags with where clause + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_bayesian_background_qc_flags.nc + simulated variables: [air_temperature] + obs filters: + - filter: Bayesian Background QC Flags + filter variables: + - name: air_temperature + # Applies the filter to all but the first profile in the sample. + # As a result an extra 69 observations pass. + where: + - variable: + name: latitude@MetaData + maxvalue: 0.0 + passedBenchmark: 93 diff --git a/test/testinput/qc_boundscheck.yaml b/test/testinput/qc_boundscheck.yaml index 7c258ee6b..c2849ad53 100644 --- a/test/testinput/qc_boundscheck.yaml +++ b/test/testinput/qc_boundscheck.yaml @@ -20,6 +20,29 @@ observations: # variable2@ObsValue = 10, 12, 14, 16, 18, 20, 22, 24, 26, 28 # variable3@ObsValue = 25, 24, 23, 22, 21, 20, 19, 18, 17, 16 passedBenchmark: 13 +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2, variable3] + obs filters: + - filter: Bounds Check # test min/max value with all variables and a where statement + where: + - variable: + name: var1@MetaData + maxvalue: 8 +# var1@MetaData = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + filter variables: + - name: variable1 + - name: variable2 + - name: variable3 + minvalue: 14.0 + maxvalue: 19.0 +# Compare variables with minvalue/maxvalue +# variable1@ObsValue = 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 +# variable2@ObsValue = 10, 12, 14, 16, 18, 20, 22, 24, 26, 28 +# variable3@ObsValue = 25, 24, 23, 22, 21, 20, 19, 18, 17, 16 + passedBenchmark: 15 - obs space: name: test data obsdatain: @@ -173,3 +196,73 @@ observations: # Note: variable1 is not specified in filtered variables, all obs for variable1 will pass # variable1@ObsValue = 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 passedBenchmark: 24 +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2, variable3] + obs filters: + - filter: Bounds Check # reject all filter variables if any test variable is out of bounds + filter variables: + - name: variable2 + - name: variable3 + test variables: + - name: var1@MetaData + - name: var2@MetaData + flag all filter variables if any test variable is out of bounds: true + minvalue: 3 +# Filter variable2 AND variable3 based the values of var1 and var2@MetaData +# Note: variable1 is not specified in filtered variables, all obs for variable1 will pass +# var1@MetaData = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +# var2@MetaData = 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + passedBenchmark: 22 # variable2 and variable3 should be rejected at locations 0, 1, 8 and 9 +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2, variable3] + obs filters: + - filter: Bounds Check # reject filter variables if the test variable is out of bounds (but not if it's missing) + filter variables: + - name: variable2 + - name: variable3 + test variables: + - name: var5@MetaData + treat missing as out of bounds: false + maxvalue: 3 +# Filter variable2 AND variable3 based the values of var1 and var2@MetaData +# Note: variable1 is not specified in filtered variables, all obs for variable1 will pass +# var5@MetaData = 1, _, 2, _, 3, _, 4, _, 5, 0 ; + passedBenchmark: 26 # variable2 and variable3 should be rejected at locations 6 and 8 +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2, variable3] + obs filters: + - filter: Bounds Check # reject filter variables if the test variable is out of bounds or missing + filter variables: + - name: variable2 + - name: variable3 + test variables: + - name: var5@MetaData + treat missing as out of bounds: true + maxvalue: 3 +# Filter variable2 AND variable3 based the values of var1 and var2@MetaData +# Note: variable1 is not specified in filtered variables, all obs for variable1 will pass +# var5@MetaData = 1, _, 2, _, 3, _, 4, _, 5, 0 ; + passedBenchmark: 18 # variable2 and variable3 should be rejected at locations 1, 3, 5, 6, 7 and 8 +- obs space: + name: Where using datetimes + simulated variables: [brightness_temperature] + channels: 20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/ssmis_f18_obs_2018041500_m.nc4 + obs filters: + - filter: BlackList + where: + - variable: + name: datetime@MetaData + minvalue: 2018-04-14T21:00:00Z + maxvalue: 2018-04-14T22:00:00Z + failedBenchmark: 17 diff --git a/test/testinput/qc_derivative_dpdt.yaml b/test/testinput/qc_derivative_dpdt.yaml index 6d443ec62..6e4cb003e 100644 --- a/test/testinput/qc_derivative_dpdt.yaml +++ b/test/testinput/qc_derivative_dpdt.yaml @@ -5,9 +5,9 @@ observations: - obs space: name: Radiosonde obsdatain: - obsfile: Data/ioda/testinput_tier_1/sondes_tv_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sondes_tv_obs_2018041500_m.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "datetime" sort order: "ascending" simulated variables: [virtual_temperature] diff --git a/test/testinput/qc_derivative_dxdt.yaml b/test/testinput/qc_derivative_dxdt.yaml index 6c78a400e..673e3d5b7 100644 --- a/test/testinput/qc_derivative_dxdt.yaml +++ b/test/testinput/qc_derivative_dxdt.yaml @@ -5,9 +5,9 @@ observations: - obs space: name: Radiosonde obsdatain: - obsfile: Data/ioda/testinput_tier_1/sondes_tv_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sondes_tv_obs_2018041500_m.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] sort variable: "datetime" sort order: "ascending" simulated variables: [virtual_temperature] diff --git a/test/testinput/qc_differencecheck.yaml b/test/testinput/qc_differencecheck.yaml index dc955a81b..1852c0e3b 100644 --- a/test/testinput/qc_differencecheck.yaml +++ b/test/testinput/qc_differencecheck.yaml @@ -78,3 +78,33 @@ observations: minvalue: -3 maxvalue: 3 passedBenchmark: 4 +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1] + obs filters: + - filter: Difference Check # test min and maxvalue (equal), equivalent to previous test + filter variables: + - name: variable1 + value: + name: ObsErrorModelRamp@ObsFunction + options: + xvar: + name: variable2@ObsValue # variable2@ObsValue = 10, 12, 14, 16, 18, 20, 22, 24, 26, 28 + x0: [10] + x1: [30] + err0: [20] + err1: [40] # obsfunction produces 20, 22, 24, 26, 28, 30, 32, 34, 36, 38 + reference: + name: ObsErrorModelRamp@ObsFunction + options: + xvar: + name: variable1@ObsValue # variable1@ObsValue = 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 + x0: [10] + x1: [20] + err0: [20] + err1: [10] # obsfunction produces 20, 19, 18, 17, 16, 15, 14, 13, 12, 11 + minvalue: -4 + maxvalue: 4 + passedBenchmark: 2 # only the first two observations should pass diff --git a/test/testinput/qc_gauss_thinning.yaml b/test/testinput/qc_gauss_thinning.yaml index bee8cad21..d7ede3900 100644 --- a/test/testinput/qc_gauss_thinning.yaml +++ b/test/testinput/qc_gauss_thinning.yaml @@ -5,7 +5,7 @@ observations: - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 simulated variables: [air_temperature] obs filters: - filter: Gaussian Thinning @@ -14,7 +14,7 @@ observations: - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 simulated variables: [air_temperature] obs filters: - filter: Gaussian Thinning @@ -25,7 +25,7 @@ observations: - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 simulated variables: [air_temperature] obs filters: - filter: Gaussian Thinning @@ -145,7 +145,7 @@ observations: - 9645 - 9652 - 9999 -# Category variable combining satellite IDs and thinning rounds +# Category variable combining station IDs and thinning rounds - obs space: name: Aircraft obsdatain: @@ -642,7 +642,7 @@ observations: priority_variable: name: priority@MetaData passedBenchmark: 145 - passedObservationsBenchmark: + passedObservationsBenchmark: ®ularSpatialGridPassedObsIds - 223 - 230 - 237 @@ -788,3 +788,105 @@ observations: - 9771 - 9778 - 9999 +# Same as above; category variable used to group observations into records +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_thinning.nc4 + obsgrouping: + group variables: [ "round" ] + simulated variables: [air_temperature] + obs filters: + - filter: Gaussian Thinning + distance_norm: maximum + round_horizontal_bin_count_to_nearest: true + use_reduced_horizontal_grid: false + horizontal_mesh: 3333.333333 + vertical_mesh: 1000.000000 + vertical_min: -500.000000 + vertical_max: 10500.000000 + time_mesh: PT01H15M00S + time_min: 2018-04-14T20:52:30Z + time_max: 2018-04-15T03:07:30Z + category_variable: + name: round@MetaData + priority_variable: + name: priority@MetaData + passedBenchmark: 145 + passedObservationsBenchmark: *regularSpatialGridPassedObsIds +# Same as above; a variable other than the category variable used to group observations into records +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_thinning.nc4 + obsgrouping: + group variables: [ "priority" ] + simulated variables: [air_temperature] + obs filters: + - filter: Gaussian Thinning + distance_norm: maximum + round_horizontal_bin_count_to_nearest: true + use_reduced_horizontal_grid: false + horizontal_mesh: 3333.333333 + vertical_mesh: 1000.000000 + vertical_min: -500.000000 + vertical_max: 10500.000000 + time_mesh: PT01H15M00S + time_min: 2018-04-14T20:52:30Z + time_max: 2018-04-15T03:07:30Z + category_variable: + name: round@MetaData + priority_variable: + name: priority@MetaData + passedBenchmark: 145 + passedObservationsBenchmark: *regularSpatialGridPassedObsIds +# Observations not grouped into records; inefficient distribution +- obs space: + name: Aircraft + distribution: InefficientDistribution + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_thinning.nc4 + simulated variables: [air_temperature] + obs filters: + - filter: Gaussian Thinning + distance_norm: maximum + round_horizontal_bin_count_to_nearest: true + use_reduced_horizontal_grid: false + horizontal_mesh: 3333.333333 + vertical_mesh: 1000.000000 + vertical_min: -500.000000 + vertical_max: 10500.000000 + time_mesh: PT01H15M00S + time_min: 2018-04-14T20:52:30Z + time_max: 2018-04-15T03:07:30Z + category_variable: + name: round@MetaData + priority_variable: + name: priority@MetaData + passedBenchmark: 145 + passedObservationsBenchmark: *regularSpatialGridPassedObsIds +# Observations not grouped into records; halo distribution +- obs space: + name: Aircraft + distribution: Halo + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_thinning.nc4 + simulated variables: [air_temperature] + obs filters: + - filter: Gaussian Thinning + distance_norm: maximum + round_horizontal_bin_count_to_nearest: true + use_reduced_horizontal_grid: false + horizontal_mesh: 3333.333333 + vertical_mesh: 1000.000000 + vertical_min: -500.000000 + vertical_max: 10500.000000 + time_mesh: PT01H15M00S + time_min: 2018-04-14T20:52:30Z + time_max: 2018-04-15T03:07:30Z + category_variable: + name: round@MetaData + priority_variable: + name: priority@MetaData + passedBenchmark: 145 + passedObservationsBenchmark: *regularSpatialGridPassedObsIds diff --git a/test/testinput/qc_gauss_thinning_unittests.yaml b/test/testinput/qc_gauss_thinning_unittests.yaml index f39ea9e03..7e52f0b55 100644 --- a/test/testinput/qc_gauss_thinning_unittests.yaml +++ b/test/testinput/qc_gauss_thinning_unittests.yaml @@ -13,7 +13,6 @@ Horizontal mesh 20000: obs errors: [1.0] air_pressures: [ 100000, 100000, 100000, 100000, 100000] GaussianThinning: - variables: [air_temperature] horizontal_mesh: 20000 round_horizontal_bin_count_to_nearest: true expected_thinned_obs_indices: [1, 2, 3] @@ -33,7 +32,6 @@ Horizontal mesh 20000, extreme longitudes, 0 to 360 degrees: obs errors: [1.0] air_pressures: [ 100000, 100000, 100000, 100000, 100000, 100000] GaussianThinning: - variables: [air_temperature] horizontal_mesh: 20000 round_horizontal_bin_count_to_nearest: true expected_thinned_obs_indices: [0, 2, 3, 5] @@ -53,7 +51,6 @@ Horizontal mesh 20000, extreme longitudes, -180 to 180 degrees: obs errors: [1.0] air_pressures: [ 100000, 100000, 100000, 100000, 100000, 100000] GaussianThinning: - variables: [air_temperature] horizontal_mesh: 20000 round_horizontal_bin_count_to_nearest: true expected_thinned_obs_indices: [0, 2, 3, 5] @@ -73,7 +70,6 @@ Horizontal mesh 20000, extreme latitudes: obs errors: [1.0] air_pressures: [ 100000, 100000, 100000, 100000, 100000] GaussianThinning: - variables: [air_temperature] horizontal_mesh: 20000 round_horizontal_bin_count_to_nearest: true expected_thinned_obs_indices: [0, 1, 3, 4] @@ -94,7 +90,6 @@ Horizontal mesh 10000: obs errors: [1.0] air_pressures: [1, 1, 1, 1, 1, 1, 1, 1, 1] GaussianThinning: - variables: [air_temperature] horizontal_mesh: 10000 round_horizontal_bin_count_to_nearest: true expected_thinned_obs_indices: [0, 1, 4, 5, 7, 8] @@ -115,7 +110,6 @@ Vertical mesh, single bin: obs errors: [1.0] air_pressures: [8, 7, 6, 5, 4, 3, 2, 1] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: 2 vertical_min: 3 @@ -139,7 +133,6 @@ Vertical mesh, two bins: obs errors: [1.0] air_pressures: [8, 7, 6, 5, 4, 3, 2, 1] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: 2 vertical_min: 1 @@ -163,7 +156,6 @@ Vertical mesh, two bins, all observations in single bin: obs errors: [1.0] air_pressures: [8, 7, 6, 5, 4, 3, 2, 1] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: 2 vertical_min: 7 @@ -187,7 +179,6 @@ Thinning in time, single bin: obs errors: [1.0] air_pressures: [0, 1, 2, 3, 4, 5, 6, 7] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: -1 time_mesh: 'PT02S' @@ -212,7 +203,6 @@ Thinning in time, two bins: obs errors: [1.0] air_pressures: [0, 1, 2, 3, 4, 5, 6, 7] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: -1 time_mesh: 'PT02S' @@ -237,7 +227,6 @@ Thinning in time, two bins, all observations in single bin: obs errors: [1.0] air_pressures: [0, 1, 2, 3, 4, 5, 6, 7] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 time_mesh: 'PT02S' time_min: '2010-01-01T00:04:07Z' @@ -262,15 +251,12 @@ Vertical mesh, two bins, single category: air_pressures: [8, 7, 6, 5, 4, 3, 2, 1] category: [0, 0, 0, 0, 0, 0, 0, 0] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: 2 vertical_min: 1 vertical_max: 5 category_variable: name: category@MetaData - group: MetaData - type: int distance_norm: maximum expected_thinned_obs_indices: [0, 1, 2, 3, 5, 7] @@ -291,15 +277,12 @@ Vertical mesh, two bins, two categories: air_pressures: [4, 3, 4, 3, 2, 1, 2, 1] category: [1, 1, 0, 0, 0, 0, 1, 1] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: 2 vertical_min: 1 vertical_max: 5 category_variable: name: category@MetaData - group: MetaData - type: int distance_norm: maximum expected_thinned_obs_indices: [1, 3, 5, 7] @@ -320,15 +303,42 @@ Vertical mesh, two bins, two categories, where clause: air_pressures: [4, 4.4, 4, 4.3, 4.2, 4, 4.1, 4] category: [1, 1, 0, 0, 0, 0, 1, 1] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: 2 vertical_min: 1 vertical_max: 5 category_variable: name: category@MetaData - group: MetaData - type: int + distance_norm: maximum + where: + - variable: + name: latitude@MetaData + maxvalue: 0 + expected_thinned_obs_indices: [1, 3] + +Vertical mesh, two bins, two string-valued categories, where clause: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [1, 0, 1, 0, 0, 1, 0, 1] + lons: [0, 1, 2, 3, 4, 5, 6, 7] + datetimes: [ '2010-01-01T00:04:01Z', '2010-01-01T00:04:02Z', '2010-01-01T00:04:03Z', + '2010-01-01T00:04:04Z', '2010-01-01T00:04:05Z', '2010-01-01T00:04:06Z', + '2010-01-01T00:04:07Z', '2010-01-01T00:04:08Z'] + obs errors: [1.0] + air_pressures: [4, 4.4, 4, 4.3, 4.2, 4, 4.1, 4] + string_category: ["1", "1", "0", "0", "0", "0", "1", "1"] + GaussianThinning: + horizontal_mesh: -1 + vertical_mesh: 2 + vertical_min: 1 + vertical_max: 5 + category_variable: + name: string_category@MetaData distance_norm: maximum where: - variable: @@ -353,15 +363,12 @@ Vertical mesh, two bins, equal priorities: air_pressures: [8, 7, 6, 5, 4, 3, 2, 1] priority: [0, 0, 0, 0, 0, 0, 0, 0] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: 2 vertical_min: 1 vertical_max: 5 priority_variable: name: priority@MetaData - group: MetaData - type: int distance_norm: maximum expected_thinned_obs_indices: [0, 1, 2, 3, 5, 7] @@ -382,15 +389,12 @@ Vertical mesh, two bins, nonequal priorities: air_pressures: [4, 3, 4, 3, 2, 1, 2, 1] priority: [1, 1, 0, 0, 0, 0, 1, 1] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: 2 vertical_min: 1 vertical_max: 5 priority_variable: name: priority@MetaData - group: MetaData - type: int distance_norm: maximum expected_thinned_obs_indices: [1, 2, 3, 4, 5, 7] @@ -411,15 +415,12 @@ Vertical mesh, single bin, nonequal priorities, where clause: air_pressures: [4, 4.4, 4, 4.3, 4.2, 4, 4.1, 4] priority: [0, 0, 0, 1, 1, 0, 0, 0] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: 2 vertical_min: 3 vertical_max: 5 priority_variable: name: priority@MetaData - group: MetaData - type: int distance_norm: maximum where: - variable: @@ -448,19 +449,14 @@ Vertical mesh, single bin, two categories, nonequal priorities, where clause: category: [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1] priority: [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0] GaussianThinning: - variables: [air_temperature] horizontal_mesh: -1 vertical_mesh: 2 vertical_min: 3 vertical_max: 5 category_variable: name: category@MetaData - group: MetaData - type: int priority_variable: name: priority@MetaData - group: MetaData - type: int distance_norm: maximum where: - variable: diff --git a/test/testinput/qc_historycheck_unittests.yaml b/test/testinput/qc_historycheck_unittests.yaml new file mode 100644 index 000000000..5036a65b7 --- /dev/null +++ b/test/testinput/qc_historycheck_unittests.yaml @@ -0,0 +1,520 @@ +# The below tests are identical to the stuck check unit tests. +# Although less computationally efficient, history check will have the same results as stuck check +# when the observation subtype is LANDSYB/LANDSYN and the history before window is set to 0. + +All observations' number and time stuck tolerated: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: &obsspace1 + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z', + '2010-01-01T05:00:00Z', '2010-01-01T06:00:00Z', '2010-01-01T08:00:00Z' ] + obs errors: [1.0] + air_temperatures: [ 281.0, 281.0, 282.0, 282.0, 283.0, 283.0] + air_temperatures_wide: [ 281.0, 281.0, 282.0, 282.0, 283.0, 283.0] + History Check: &referenceStuckFilterArguments + input category: 'SHPSYN' + time before start of window: PT0S + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 2 + time stuck tolerance: PT2H + obs space: *obsspace1 + reset larger obs space variables: true + expected rejected obs indices: [] + +Time stuck tolerated, number stuck untolerated, more than 1 value: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: &obsspace2 + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:30:00Z', '2010-01-01T00:31:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T03:01:00Z', '2010-01-01T03:02:00Z' ] + obs errors: [1.0] + air_temperatures: [ 282.0, 282.0, 282.0, 283.0, 283.0, 283.0] + air_temperatures_wide: [ 282.0, 282.0, 282.0, 283.0, 283.0, 283.0] + History Check: + input category: 'SHPSYN' + time before start of window: PT0S + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 2 + time stuck tolerance: PT2H + obs space: *obsspace2 + reset larger obs space variables: true + expected rejected obs indices: [] + +Time stuck tolerated, number stuck untolerated, 1 value: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: &obsspace3 + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:01Z', '2010-01-01T00:00:02Z', + '2010-01-01T00:00:03Z', '2010-01-01T00:00:04Z', '2010-01-01T00:00:05Z' ] + obs errors: [1.0] + air_temperatures: [ 282.0, 282.0, 282.0, 282.0, 282.0, 282.0 ] + air_temperatures_wide: [ 282.0, 282.0, 282.0, 282.0, 282.0, 282.0 ] + History Check: + input category: 'SHPSYN' + time before start of window: PT0S + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 2 + time stuck tolerance: PT2H + obs space: *obsspace3 + reset larger obs space variables: true + expected rejected obs indices: [ 0, 1, 2, 3, 4, 5 ] + +Time stuck tolerated, number stuck tolerated, 1 value: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: &obsspace4 + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1 ] + lons: [ 0, 1 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:01Z' ] + obs errors: [1.0] + air_temperatures: [ 282.0, 282.0 ] + air_temperatures_wide: [ 282.0, 282.0 ] + History Check: + input category: 'SHPSYN' + time before start of window: PT0S + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 2 + time stuck tolerance: PT2H + obs space: *obsspace4 + reset larger obs space variables: true + expected rejected obs indices: [] + +Time stuck untolerated for both streaks, number stuck tolerated for second streak (but not first): + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: &obsspace5 + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T04:00:00Z', + '2010-01-01T06:00:00Z', '2010-01-01T08:00:00Z', '2010-01-01T12:00:00Z' ] + obs errors: [1.0] + air_temperatures: [ 282.0, 282.0, 282.0, 282.0, 283.0, 283.0 ] + air_temperatures_wide: [ 282.0, 282.0, 282.0, 282.0, 283.0, 283.0 ] + History Check: + input category: 'SHPSYN' + time before start of window: PT0S + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 2 + time stuck tolerance: PT2H + obs space: *obsspace5 + reset larger obs space variables: true + expected rejected obs indices: [ 0, 1, 2, 3 ] + +Time stuck tolerated for first streak (but not second), number stuck untolerated for both streaks: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: &obsspace6 + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:01Z', '2010-01-01T00:00:02Z', + '2010-01-01T06:00:00Z', '2010-01-01T08:00:00Z', '2010-01-01T12:00:00Z' ] + obs errors: [1.0] + air_temperatures: [ 282.0, 282.0, 282.0, 283.0, 283.0, 283.0 ] + air_temperatures_wide: [ 282.0, 282.0, 282.0, 283.0, 283.0, 283.0 ] + History Check: + input category: 'SHPSYN' + time before start of window: PT0S + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 2 + time stuck tolerance: PT2H + obs space: *obsspace6 + reset larger obs space variables: true + expected rejected obs indices: [ 3, 4, 5 ] + +Stuck check historical data: + window begin: 2010-01-01T04:00:00Z + window end: 2030-01-01T12:01:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [4, 5, 6, 7] + lons: [4, 5, 6, 7] + datetimes: [ '2010-01-01T04:00:00Z', '2010-01-01T05:00:00Z', '2010-01-01T06:00:00Z', + '2010-01-01T07:00:00Z'] + obs errors: [1.0] + air_temperatures: [ 282.0, 282.0, 283.0, 284.0] + air_temperatures_wide: [ 282.0, 282.0, 282.0, 283.0, 284.0] + History Check: + input category: 'SHPSYN' + time before start of window: PT3H + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 2 + time stuck tolerance: PT2H + reset larger obs space variables: true + obs space: + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [3, 4, 5, 6, 7] + lons: [3, 4, 5, 6, 7] + datetimes: [ '2010-01-01T02:00:00Z', + '2010-01-01T04:00:00Z', '2010-01-01T05:00:00Z', '2010-01-01T06:00:00Z', + '2010-01-01T07:00:00Z'] + obs errors: [1.0] + expected rejected obs indices: [ 0, 1 ] + +All temperatures different: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: &obsspace7 + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:01Z', '2010-01-01T00:00:02Z', + '2010-01-01T06:00:00Z', '2010-01-01T08:00:00Z', '2010-01-01T12:00:00Z' ] + obs errors: [1.0] + air_temperatures: [ 280.0, 281.0, 282.0, 283.0, 284.0, 285.0] + air_temperatures_wide: [ 280.0, 281.0, 282.0, 283.0, 284.0, 285.0] + History Check: + input category: 'SHPSYN' + time before start of window: PT0S + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 2 + time stuck tolerance: PT2H + obs space: *obsspace7 + reset larger obs space variables: true + expected rejected obs indices: [] + +# Similarly, History Check will have the same results as track check ship when the observation +# subtype is TEMP/BATHY/TESAC/BUOYPROF and the history before window is set to 0. +# N.B. station_id_variable is referring to the station ids of both obs spaces, so +# they must correspond across the two obs spaces. +Ship Track Segment from Comparison Test early break false: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: &trackCheckShipObsSpace + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [ -37.00999832, -37.00999832, -37.02000046, -37.02000046, -37.02000046] + lons: [ 82.5, 82.51000214, 82.52999878, 82.55999756, 82.56999969] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T04:00:00Z', '2010-01-01T05:00:00Z'] + obs errors: [1.0] + station_ids: [ 1, 1, 1, 1, 1 ] + station_ids_wide: [ 1, 1, 1, 1, 1 ] + History Check: + input category: 'TEMP' + time before start of window: PT0S + filter variables: [air_temperature] + ship track check parameters: + temporal resolution: PT1S + spatial resolution (km): 0.0000001 + max speed (m/s): 0.01 + rejection threshold: 0.5 + early break check: false + station_id_variable: + name: station_id@MetaData + obs space: *trackCheckShipObsSpace + reset larger obs space variables: true + expected rejected obs indices: [0, 1, 2, 3, 4] +Ship Track Segment from Comparison Test early break true: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: &trackCheckShipObsSpace2 + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [ -4.94999981, -4.94999981, -4.94999981, -4.96000004, -4.96000004, -4.96000004] + lons: [ 94.98000336, 94.98000336, 94.98000336, 94.98000336, 94.98000336, 94.98000336 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z', '2010-01-01T05:00:00Z'] + obs errors: [1.0] + station_ids: [ 1, 1, 1, 1, 1, 1 ] + station_ids_wide: [ 1, 1, 1, 1, 1, 1] + History Check: + input category: 'TEMP' + time before start of window: PT0S + filter variables: [air_temperature] + ship track check parameters: + temporal resolution: PT1S + spatial resolution (km): 0.0000001 + max speed (m/s): 0.01 + rejection threshold: 0.5 + early break check: true + station_id_variable: + name: station_id@MetaData + obs space: *trackCheckShipObsSpace2 + reset larger obs space variables: true + expected rejected obs indices: [0, 1, 2, 3, 4, 5] +Ship track check historical data: + window begin: 2010-01-01T02:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [ -37.02000046 ] + lons: [ 82.56999969 ] + datetimes: [ '2010-01-01T05:00:00Z' ] + obs errors: [1.0] + station_ids: [ 1 ] + station_ids_wide: [ 1, 1, 1, 1, 1 ] + History Check: + input category: 'TEMP' + time before start of window: PT6H + filter variables: [air_temperature] + ship track check parameters: + temporal resolution: PT1S + spatial resolution (km): 0.0000001 + max speed (m/s): 0.01 + rejection threshold: 0.5 + early break check: false + station_id_variable: + name: station_id@MetaData + obs space: *trackCheckShipObsSpace + reset larger obs space variables: true + expected rejected obs indices: [0] +Only ship track check, compatible with stuck check (no historical data): + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: &genericObsSpace + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [ 0.0, .05, 0.1, 0.2, 2.0, 0.3] + lons: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:30:00Z', '2010-01-01T01:00:00Z', + '2010-01-01T02:00:00Z', '2010-01-01T02:30:00Z', '2010-01-01T03:00:00Z'] + obs errors: [1.0] + air_temperatures: [ 281.0, 281.0, 281.0, 282.0, 283.0, 284.0 ] + air_temperatures_wide: [ 281.0, 281.0, 281.0, 282.0, 283.0, 284.0 ] + station_ids: [ 1, 1, 1, 1, 1, 1 ] + station_ids_wide: [ 1, 1, 1, 1, 1, 1 ] + History Check: + input category: 'OPENROAD' + time before start of window: PT0S + filter variables: [air_temperature] + ship track check parameters: + temporal resolution: PT1S + spatial resolution (km): 0.0000001 + max speed (m/s): 5.0 + rejection threshold: 0.5 + early break check: false + station_id_variable: + name: station_id@MetaData + obs space: *genericObsSpace + reset larger obs space variables: true + expected rejected obs indices: [4] +Only stuck check, compatible with trackcheckship check (no historical data): + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: *genericObsSpace + air_temperatures: [ 281.0, 281.0, 281.0, 282.0, 283.0, 284.0 ] + air_temperatures_wide: [ 281.0, 281.0, 281.0, 282.0, 283.0, 284.0 ] + station_ids: [ 1, 1, 1, 1, 1, 1 ] + station_ids_wide: [ 1, 1, 1, 1, 1, 1] + History Check: + input category: 'OPENROAD' + time before start of window: PT0S + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 2 + time stuck tolerance: PT45M + station_id_variable: + name: station_id@MetaData + obs space: *genericObsSpace + reset larger obs space variables: true + expected rejected obs indices: [0, 1, 2] +Both checks (no historical data): + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: *genericObsSpace + air_temperatures: [ 281.0, 281.0, 281.0, 282.0, 283.0, 284.0 ] + air_temperatures_wide: [ 281.0, 281.0, 281.0, 282.0, 283.0, 284.0 ] + station_ids: [ 1, 1, 1, 1, 1, 1] + station_ids_wide: [ 1, 1, 1, 1, 1, 1] + History Check: + input category: 'OPENROAD' + time before start of window: PT0S + filter variables: [air_temperature] + ship track check parameters: + temporal resolution: PT1S + spatial resolution (km): 0.0000001 + max speed (m/s): 5.0 + rejection threshold: 0.5 + early break check: false + stuck check parameters: + number stuck tolerance: 2 + time stuck tolerance: PT45M + station_id_variable: + name: station_id@MetaData + obs space: *genericObsSpace + reset larger obs space variables: true + expected rejected obs indices: [0, 1, 2, 4] +Both checks (with historical data): + window begin: 2010-01-01T01:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [ 0.1, 0.2, 2.0, 0.3] + lons: [ 0.0, 0.0, 0.0, 0.0 ] + datetimes: [ '2010-01-01T01:00:00Z', + '2010-01-01T02:00:00Z', '2010-01-01T02:30:00Z', '2010-01-01T03:00:00Z'] + obs errors: [1.0] + air_temperatures: [ 281.0, 282.0, 283.0, 284.0 ] + air_temperatures_wide: [ 281.0, 281.0, 281.0, 282.0, 283.0, 284.0 ] + station_ids: [ 1, 1, 1, 1 ] + station_ids_wide: [ 1, 1, 1, 1, 1, 1 ] + History Check: + input category: 'OPENROAD' + time before start of window: PT1H1M + filter variables: [air_temperature] + ship track check parameters: + temporal resolution: PT1S + spatial resolution (km): 0.0000001 + max speed (m/s): 5.0 + rejection threshold: 0.5 + early break check: false + stuck check parameters: + number stuck tolerance: 2 + time stuck tolerance: PT45M + station_id_variable: + name: station_id@MetaData + obs space: *genericObsSpace + reset larger obs space variables: true + expected rejected obs indices: [0, 2] +Identical observations stuck check: + window begin: 2010-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: &identicalObservations + name: Ship + distribution: InefficientDistribution + simulated variables: [air_temperature] + generate: + list: + lats: [ 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 2.0, 2.0, 2.0, 2.0 ] + lons: [ 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 2.0, 2.0, 2.0, 2.0 ] + datetimes: [ '2010-01-01T01:00:00Z', + '2010-01-01T01:00:00Z', + '2010-01-01T01:00:00Z', + '2010-01-01T01:00:00Z', + '2010-01-01T01:46:00Z', + '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', + '2010-01-01T03:00:00Z', + '2010-01-01T03:00:00Z', + '2010-01-01T03:00:00Z', + '2010-01-01T03:46:00Z' ] + obs errors: [1.0] + air_temperatures: [ 281.0, 281.0, 281.0, 281.0, 281.0, 282.0, 283.0, 283.0, 283.0, 283.0, 283.0 ] + air_temperatures_wide: [ 281.0, 281.0, 281.0, 281.0, 281.0, 282.0, 283.0, 283.0, 283.0, + 283.0, 283.0 ] + station_ids: [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + station_ids_wide: [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + History Check: + input category: 'LNDSYB' + time before start of window: PT0S + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 3 + time stuck tolerance: PT45M + station_id_variable: + name: station_id@MetaData + obs space: *identicalObservations + reset larger obs space variables: true + expected rejected obs indices: [0, 1, 2, 3, 4, 6, 7, 8, 9, 10] +Different Station Ids: + window begin: 2010-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: *identicalObservations + air_temperatures: [ 281.0, 281.0, 281.0, 281.0, 281.0, 282.0, 283.0, 283.0, 283.0, 283.0, 283.0 ] + air_temperatures_wide: [ 281.0, 281.0, 281.0, 281.0, 281.0, 282.0, 283.0, 283.0, 283.0, 283.0, + 283.0 ] + station_ids: [ 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3 ] + station_ids_wide: [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + History Check: + input category: 'LNDSYB' + time before start of window: PT0S + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 3 + time stuck tolerance: PT45M + station_id_variable: + name: station_id@MetaData + obs space: *identicalObservations + reset larger obs space variables: true + expected rejected obs indices: [0, 1, 2, 3] +String-labelled station ids: + window begin: 2010-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: *identicalObservations + air_temperatures: [ 281.0, 281.0, 281.0, 281.0, 281.0, 282.0, 283.0, 283.0, 283.0, 283.0, 283.0 ] + air_temperatures_wide: [ 281.0, 281.0, 281.0, 281.0, 281.0, 282.0, 283.0, + 283.0, 283.0, 283.0, 283.0 ] + station_ids_string: [ "aaa", "aaa", "aaa", "aaa", "bbb", "bbb", "bbb", "ccc", "ccc", "ccc", "ccc" ] + station_ids_wide_string: [ "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa" ] + History Check: + input category: 'LNDSYB' + time before start of window: PT0S + filter variables: [air_temperature] + stuck check parameters: + number stuck tolerance: 3 + time stuck tolerance: PT45M + station_id_variable: + name: station_id@MetaData + obs space: *identicalObservations + reset larger obs space variables: true + expected rejected obs indices: [0, 1, 2, 3] diff --git a/test/testinput/qc_met_office_buddy_check.yaml b/test/testinput/qc_met_office_buddy_check.yaml index 9a5ff417d..d62ed609a 100644 --- a/test/testinput/qc_met_office_buddy_check.yaml +++ b/test/testinput/qc_met_office_buddy_check.yaml @@ -7,8 +7,15 @@ observations: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_buddy_check.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, eastward_wind, northward_wind] + obs operator: + name: Composite + components: + # operator used to evaluate H(x) + - name: Identity + # operator used to evaluate background errors + - name: BackgroundErrorIdentity obs filters: - filter: Met Office Buddy Check filter variables: @@ -20,7 +27,6 @@ observations: # Maps latitudes to kms horizontal_correlation_scale: {"90": 7200, "30": 7200, "20": 8400, "-20": 8400, "-30": 9600, "-90": 9600} - vertical_correlation_scale: 6 temporal_correlation_scale: PT6H num_zonal_bands: 36 search_radius: 3000 # km @@ -36,26 +42,34 @@ observations: max_latitude: 90 min_longitude: -180 max_longitude: 180 - HofX: HofX + geovals: + filename: Data/ufo/testinput_tier_1/met_office_buddy_check_geovals.nc4 passedBenchmark: 2940 compareVariables: - reference: name: air_temperature@GrossErrorProbabilityAfterOpsBuddyCheck1 test: name: air_temperature@GrossErrorProbability - absTol: 5e-5 # The relative difference in Earth radius assumed by OPS and JEDI is ~4e-5 + absTol: 5.0e-5 # The relative difference in Earth radius assumed by OPS and JEDI is ~4e-5 - reference: name: eastward_wind@GrossErrorProbabilityAfterOpsBuddyCheck1 test: name: eastward_wind@GrossErrorProbability - absTol: 5e-5 + absTol: 5.0e-5 - obs space: # Test of the sort_by_pressure option name: Aircraft obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_buddy_check.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, eastward_wind, northward_wind] + obs operator: + name: Composite + components: + # operator used to evaluate H(x) + - name: Identity + # operator used to evaluate background errors + - name: BackgroundErrorIdentity obs filters: - filter: Met Office Buddy Check filter variables: @@ -68,7 +82,6 @@ observations: sort_by_pressure: true horizontal_correlation_scale: {"90": 7200, "30": 7200, "20": 8400, "-20": 8400, "-30": 9600, "-90": 9600} - vertical_correlation_scale: 6 temporal_correlation_scale: PT6H num_zonal_bands: 36 search_radius: 3000 # km @@ -84,26 +97,34 @@ observations: max_latitude: 90 min_longitude: -180 max_longitude: 180 - HofX: HofX + geovals: + filename: Data/ufo/testinput_tier_1/met_office_buddy_check_geovals.nc4 passedBenchmark: 2940 compareVariables: - reference: name: air_temperature@GrossErrorProbabilityAfterOpsBuddyCheck2 test: name: air_temperature@GrossErrorProbability - absTol: 5e-5 # The relative difference in Earth radius assumed by OPS and JEDI is ~4e-5 + absTol: 5.0e-5 # The relative difference in Earth radius assumed by OPS and JEDI is ~4e-5 - reference: name: eastward_wind@GrossErrorProbabilityAfterOpsBuddyCheck2 test: name: eastward_wind@GrossErrorProbability - absTol: 5e-5 + absTol: 5.0e-5 - obs space: # Test of the 'where' clause handler name: Aircraft obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_buddy_check.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [air_temperature, eastward_wind, northward_wind] + obs operator: + name: Composite + components: + # operator used to evaluate H(x) + - name: Identity + # operator used to evaluate background errors + - name: BackgroundErrorIdentity obs filters: - filter: Met Office Buddy Check filter variables: @@ -119,7 +140,6 @@ observations: # Maps latitudes to kms horizontal_correlation_scale: {"90": 7200, "30": 7200, "20": 8400, "-20": 8400, "-30": 9600, "-90": 9600} - vertical_correlation_scale: 6 temporal_correlation_scale: PT6H num_zonal_bands: 36 search_radius: 3000 # km @@ -135,25 +155,33 @@ observations: max_latitude: 90 min_longitude: -180 max_longitude: 180 - HofX: HofX + geovals: + filename: Data/ufo/testinput_tier_1/met_office_buddy_check_geovals.nc4 passedBenchmark: 2958 compareVariables: - reference: name: air_temperature@GrossErrorProbabilityAfterOpsBuddyCheck3 test: name: air_temperature@GrossErrorProbability - absTol: 5e-5 # The relative difference in Earth radius assumed by OPS and JEDI is ~4e-5 + absTol: 5.0e-5 # The relative difference in Earth radius assumed by OPS and JEDI is ~4e-5 - reference: name: eastward_wind@GrossErrorProbabilityAfterOpsBuddyCheck3 test: name: eastward_wind@GrossErrorProbability - absTol: 5e-5 + absTol: 5.0e-5 - obs space: # Test of station ID assignment directly from a string-valued variable # (rather than record numbers) name: Aircraft obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_buddy_check.nc4 simulated variables: [air_temperature, eastward_wind, northward_wind] + obs operator: + name: Composite + components: + # operator used to evaluate H(x) + - name: Identity + # operator used to evaluate background errors + - name: BackgroundErrorIdentity obs filters: - filter: Met Office Buddy Check filter variables: @@ -165,7 +193,6 @@ observations: # Maps latitudes to kms horizontal_correlation_scale: {"90": 7200, "30": 7200, "20": 8400, "-20": 8400, "-30": 9600, "-90": 9600} - vertical_correlation_scale: 6 temporal_correlation_scale: PT6H num_zonal_bands: 36 search_radius: 3000 # km @@ -183,16 +210,17 @@ observations: max_latitude: 90 min_longitude: -180 max_longitude: 180 - HofX: HofX + geovals: + filename: Data/ufo/testinput_tier_1/met_office_buddy_check_geovals.nc4 passedBenchmark: 2940 compareVariables: - reference: name: air_temperature@GrossErrorProbabilityAfterOpsBuddyCheck1 test: name: air_temperature@GrossErrorProbability - absTol: 5e-5 # The relative difference in Earth radius assumed by OPS and JEDI is ~4e-5 + absTol: 5.0e-5 # The relative difference in Earth radius assumed by OPS and JEDI is ~4e-5 - reference: name: eastward_wind@GrossErrorProbabilityAfterOpsBuddyCheck1 test: name: eastward_wind@GrossErrorProbability - absTol: 5e-5 + absTol: 5.0e-5 diff --git a/test/testinput/qc_met_office_buddy_check_unittests.yaml b/test/testinput/qc_met_office_buddy_check_unittests.yaml index 13d3e0c0b..4e3b562c1 100644 --- a/test/testinput/qc_met_office_buddy_check_unittests.yaml +++ b/test/testinput/qc_met_office_buddy_check_unittests.yaml @@ -19,21 +19,22 @@ Pair of observations: air_temperature@ObsError: [ 1, 2] air_temperature@GrossErrorProbability: [0.05, 0.1] air_temperature@HofX: [ 289, 293] - air_temperature@HofXError: [ 2, 1] eastward_wind@ObsValue: [ 20, 21] eastward_wind@ObsError: [ 0.5, 1.0] eastward_wind@GrossErrorProbability: [0.07, 0.12] eastward_wind@HofX: [ 19, 23] - eastward_wind@HofXError: [ 1, 0.5] northward_wind@ObsValue: [ -30, -31] northward_wind@ObsError: [ 0.5, 1.0] northward_wind@GrossErrorProbability: [0.07, 0.12] northward_wind@HofX: [ -29, -33] - northward_wind@HofXError: [ 1, 0.5] IntVariables: station_id@MetaData: [ 1, 2] + obs diagnostics: + air_temperature_background_error: [ 2, 1] + eastward_wind_background_error: [ 1, 0.5] + northward_wind_background_error: [ 1, 0.5] Met Office Buddy Check: filter variables: - name: eastward_wind @@ -43,7 +44,6 @@ Pair of observations: - name: air_temperature horizontal_correlation_scale: {"90": 150, "30": 150, "20": 200, "-20": 200, "-30": 250, "-90": 250} - vertical_correlation_scale: 10 temporal_correlation_scale: PT1H num_zonal_bands: 12 search_radius: 500 # km diff --git a/test/testinput/qc_met_office_buddy_pair_finder.yaml b/test/testinput/qc_met_office_buddy_pair_finder.yaml index f8cf68b25..0cdbf9489 100644 --- a/test/testinput/qc_met_office_buddy_pair_finder.yaml +++ b/test/testinput/qc_met_office_buddy_pair_finder.yaml @@ -7,7 +7,7 @@ Duplicates, constraints on buddy counts, legacy pair collector: obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_buddy_check.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] Met Office Buddy Check, modern: use_legacy_buddy_collector: false num_zonal_bands: 12 diff --git a/test/testinput/qc_modelobthreshold.yaml b/test/testinput/qc_modelobthreshold.yaml new file mode 100644 index 000000000..ce8c35a72 --- /dev/null +++ b/test/testinput/qc_modelobthreshold.yaml @@ -0,0 +1,40 @@ +window begin: 2020-10-01T03:00:00Z +window end: 2020-10-01T09:00:00Z + +observations: +- obs space: + name: Satwind + obsdatain: + obsfile: Data/ufo/testinput_tier_1/satwind_obs_1d_2020100106_noinv.nc4 + obsdataout: + obsfile: Data/satwind_obs_1d_2020100106_noinv_rhcheck_out.nc4 + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/satwind_geoval_20201001T0600Z_noinv.nc4 + obs filters: + - filter: ModelOb Threshold + # Reject any observed Satwind wind components where the model RH at the observation pressure + # is less than 30% at 200 hPa or less. The threshold then increases with increasing pressure, + # using linear interpolation between points. + model profile: + name: relative_humidity@GeoVaLs + model vertical coordinate: + name: air_pressure@GeoVaLs + observation height: + name: air_pressure_levels@MetaData + thresholds: [50,45,40,30] + coordinate values: [100000,80000,50000,20000] + threshold type: min + - filter: ModelOb Threshold + # Reject any observed Satwind wind components where the model temperature at the observation + # pressure is greater than 300K + model profile: + name: air_temperature@GeoVaLs + model vertical coordinate: + name: air_pressure@GeoVaLs + observation height: + name: air_pressure_levels@MetaData + thresholds: [300,300] + coordinate values: [100000,20000] + threshold type: max + passedBenchmark: 157166 diff --git a/test/testinput/qc_oceancolor_preqc.yaml b/test/testinput/qc_oceancolor_preqc.yaml index ab66a1808..b0fb01aba 100644 --- a/test/testinput/qc_oceancolor_preqc.yaml +++ b/test/testinput/qc_oceancolor_preqc.yaml @@ -5,10 +5,10 @@ observations: - obs space: name: Chlorophyll obsdatain: - obsfile: Data/ioda/testinput_tier_1/viirs_jpss1_oc_l2_2018-04-15.nc + obsfile: Data/ufo/testinput_tier_1/viirs_jpss1_oc_l2_2018-04-15.nc simulated variables: [mass_concentration_of_chlorophyll_in_sea_water] obs filters: - - filter: Domain Check + - filter: BlackList filter variables: - name: mass_concentration_of_chlorophyll_in_sea_water where: diff --git a/test/testinput/qc_performaction.yaml b/test/testinput/qc_performaction.yaml new file mode 100644 index 000000000..1c9bf34a5 --- /dev/null +++ b/test/testinput/qc_performaction.yaml @@ -0,0 +1,31 @@ +window begin: 2018-01-01T00:00:00Z +window end: 2019-01-01T00:00:00Z + +observations: +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 + simulated variables: [variable1, variable2] + HofX: HofX + obs filters: + # reject observations with var1 >= 6 + - filter: Perform Action + action: + name: reject + where: + - variable: + name: var1@MetaData # = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + minvalue: 6 + # now "accept back" all rejected observations of variable2 with var1 >= 3 and var1 <= 7 + - filter: Perform Action + action: + name: accept + filter variables: [variable2] + where: + - variable: + name: var1@MetaData # = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + minvalue: 3 + maxvalue: 7 + # all observations of variable1 with var1 <= 5 and of variable2 with var1 <= 7 should be accepted + passedBenchmark: 12 diff --git a/test/testinput/qc_poisson_disk_thinning.yaml b/test/testinput/qc_poisson_disk_thinning.yaml index c68902b47..bd6393988 100644 --- a/test/testinput/qc_poisson_disk_thinning.yaml +++ b/test/testinput/qc_poisson_disk_thinning.yaml @@ -18,7 +18,6 @@ observations: name: is_superob@MetaData is_in: 1 - filter: Poisson Disk Thinning - variables: [air_temperature] min_horizontal_spacing: { "0": 2000, "1": 1000 } exclusion_volume_shape: ellipsoid shuffle: false # visit observations in the same order as the Met Office function did @@ -27,7 +26,7 @@ observations: priority_variable: name: priority@MetaData passedBenchmark: 34 - passedObservationsBenchmark: + passedObservationsBenchmark: &referencePassedObsIds - 2500 - 2501 - 2502 @@ -62,3 +61,55 @@ observations: - 7597 - 7603 - 7635 +# Same as above, but with records grouped by the category variable +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_poisson_disk_thinning.nc4 + obsgrouping: + group variables: [ "scan_index" ] + simulated variables: [air_temperature] + obs filters: + - filter: Domain Check + filter variables: + - name: air_temperature + where: + - variable: + name: is_superob@MetaData + is_in: 1 + - filter: Poisson Disk Thinning + min_horizontal_spacing: { "0": 2000, "1": 1000 } + exclusion_volume_shape: ellipsoid + shuffle: false # visit observations in the same order as the Met Office function did + category_variable: + name: scan_index@MetaData + priority_variable: + name: priority@MetaData + passedBenchmark: 34 + passedObservationsBenchmark: *referencePassedObsIds +# Same as above, but with records grouped by something else than the category variable +- obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_poisson_disk_thinning.nc4 + obsgrouping: + group variables: [ "priority" ] + simulated variables: [air_temperature] + obs filters: + - filter: Domain Check + filter variables: + - name: air_temperature + where: + - variable: + name: is_superob@MetaData + is_in: 1 + - filter: Poisson Disk Thinning + min_horizontal_spacing: { "0": 2000, "1": 1000 } + exclusion_volume_shape: ellipsoid + shuffle: false # visit observations in the same order as the Met Office function did + category_variable: + name: scan_index@MetaData + priority_variable: + name: priority@MetaData + passedBenchmark: 34 + passedObservationsBenchmark: *referencePassedObsIds diff --git a/test/testinput/qc_poisson_disk_thinning_parallel.yaml b/test/testinput/qc_poisson_disk_thinning_parallel.yaml new file mode 100644 index 000000000..afda37b0c --- /dev/null +++ b/test/testinput/qc_poisson_disk_thinning_parallel.yaml @@ -0,0 +1,102 @@ +# Test names have numeric prefixes to force them to be run in the specified order. +01 Seed from clock, inefficient distribution: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + distribution: InefficientDistribution + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_thinning.nc4 + simulated variables: [air_temperature] + Poisson Disk Thinning: + min_vertical_spacing: 1000 + shuffle: true + +02 Seed from clock, inefficient distribution, categories: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + distribution: InefficientDistribution + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_thinning.nc4 + simulated variables: [air_temperature] + Poisson Disk Thinning: + min_vertical_spacing: 1000 + category_variable: + name: round@MetaData + shuffle: true + +03 Seed from clock, inefficient distribution, categories, records: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + distribution: InefficientDistribution + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_thinning.nc4 + obsgrouping: + group variables: [ round ] + simulated variables: [air_temperature] + Poisson Disk Thinning: + min_vertical_spacing: 1000 + category_variable: + name: round@MetaData + shuffle: true + +04 Seed from clock, round-robin distribution: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_thinning.nc4 + simulated variables: [air_temperature] + Poisson Disk Thinning: + min_vertical_spacing: 1000 + shuffle: true + +05 Seed from clock, round-robin distribution, categories: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_thinning.nc4 + simulated variables: [air_temperature] + Poisson Disk Thinning: + min_vertical_spacing: 1000 + category_variable: + name: round@MetaData + shuffle: true + +06 Seed from clock, round-robin distribution, categories, records: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_thinning.nc4 + obsgrouping: + group variables: [ round ] + simulated variables: [air_temperature] + Poisson Disk Thinning: + min_vertical_spacing: 1000 + category_variable: + name: round@MetaData + shuffle: true + +07 Fixed seed, round-robin distribution, categories: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_thinning.nc4 + simulated variables: [air_temperature] + Poisson Disk Thinning: + min_vertical_spacing: 1000 + category_variable: + name: round@MetaData + shuffle: true + random_seed: 12345 diff --git a/test/testinput/qc_poisson_disk_thinning_unittests.yaml b/test/testinput/qc_poisson_disk_thinning_unittests.yaml index c55b5466e..0854392d0 100644 --- a/test/testinput/qc_poisson_disk_thinning_unittests.yaml +++ b/test/testinput/qc_poisson_disk_thinning_unittests.yaml @@ -9,7 +9,6 @@ No thinning: obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] exclusion_volume_shape: ellipsoid shuffle: false expected_thinned_obs_indices: [] @@ -23,7 +22,6 @@ Horizontal thinning, min spacing smaller than nearest neighbor spacing: obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] min_horizontal_spacing: 999 exclusion_volume_shape: ellipsoid shuffle: false @@ -45,7 +43,6 @@ Horizontal thinning, min spacing larger than nearest neighbor spacing: obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] min_horizontal_spacing: 1100 exclusion_volume_shape: ellipsoid shuffle: false @@ -68,7 +65,6 @@ Vertical thinning, min spacing smaller than nearest neighbor spacing: obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] min_vertical_spacing: 9999 exclusion_volume_shape: ellipsoid shuffle: false @@ -91,7 +87,6 @@ Vertical thinning, min spacing larger than nearest neighbor spacing: obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] min_vertical_spacing: 10001 exclusion_volume_shape: ellipsoid shuffle: false @@ -114,7 +109,6 @@ Vertical thinning, where clause: obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] min_vertical_spacing: 10001 exclusion_volume_shape: ellipsoid shuffle: false @@ -138,7 +132,6 @@ Time thinning, min spacing equal to nearest neighbor spacing: obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] min_time_spacing: PT01H exclusion_volume_shape: ellipsoid shuffle: false @@ -161,7 +154,6 @@ Time thinning, min spacing larger than nearest neighbor spacing: obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] min_time_spacing: PT01H01S exclusion_volume_shape: ellipsoid shuffle: false @@ -184,7 +176,6 @@ Horizontal and vertical thinning, min spacing larger than nearest neighbor spaci obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] min_horizontal_spacing: 1100 min_vertical_spacing: 10001 exclusion_volume_shape: ellipsoid @@ -208,7 +199,6 @@ Horizontal and time thinning, min spacing larger than nearest neighbor spacing: obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] min_horizontal_spacing: 1100 min_time_spacing: PT01H01S exclusion_volume_shape: ellipsoid @@ -232,7 +222,6 @@ Vertical and time thinning, min spacing larger than nearest neighbor spacing: obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] min_vertical_spacing: 10001 min_time_spacing: PT01H01S exclusion_volume_shape: cylinder @@ -256,7 +245,6 @@ Horizontal, vertical and time thinning, min spacing larger than nearest neighbor obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] Poisson Disk Thinning: - variables: [air_temperature] min_horizontal_spacing: 1100 min_vertical_spacing: 10001 min_time_spacing: PT01H01S @@ -288,7 +276,6 @@ Priorities: air_pressures: [ 10000, 20000, 30000, 40000, 50000 ] priority: [0, 1, 1, 0, 0] Poisson Disk Thinning: - variables: [air_temperature] min_vertical_spacing: 10001 shuffle: false exclusion_volume_shape: ellipsoid @@ -296,7 +283,7 @@ Priorities: name: priority@MetaData expected_thinned_obs_indices: [ 0, 2, 4 ] -Categories: +Int-valued categories: window begin: 2000-01-01T00:00:00Z window end: 2030-01-01T00:00:00Z obs space: @@ -312,7 +299,6 @@ Categories: air_pressures: [ 10000, 10000, 20000, 20000, 30000, 30000 ] category: [0, 1, 0, 1, 0, 1] Poisson Disk Thinning: - variables: [air_temperature] min_vertical_spacing: 10001 shuffle: false exclusion_volume_shape: ellipsoid @@ -320,6 +306,29 @@ Categories: name: category@MetaData expected_thinned_obs_indices: [ 2, 3 ] +String-valued categories: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 0, 0, 0, 0, 0 ] + lons: [ 0, 0, 0, 0, 0, 0 ] + datetimes: [ '2010-01-01T00:04:00Z', '2010-01-01T00:04:12Z', '2010-01-01T00:04:24Z', + '2010-01-01T00:04:36Z', '2010-01-01T00:04:48Z', '2010-01-01T00:05:00Z' ] + obs errors: [1.0] + air_pressures: [ 10000, 10000, 20000, 20000, 30000, 30000 ] + string_category: ["a", "b", "a", "b", "a", "b"] + Poisson Disk Thinning: + min_vertical_spacing: 10001 + shuffle: false + exclusion_volume_shape: ellipsoid + category_variable: + name: string_category@MetaData + expected_thinned_obs_indices: [ 2, 3 ] + Variable min spacings: window begin: 2000-01-01T00:00:00Z window end: 2030-01-01T00:00:00Z @@ -338,7 +347,6 @@ Variable min spacings: air_pressures: [ 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4 ] priority: [ 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 ] Poisson Disk Thinning: - variables: [air_temperature] min_vertical_spacing: { "1": 2.1, "2": 1.1 } exclusion_volume_shape: ellipsoid shuffle: false @@ -364,7 +372,6 @@ Variable min spacings, shuffling: air_pressures: [ 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4 ] priority: [ 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 ] Poisson Disk Thinning: - variables: [air_temperature] min_vertical_spacing: { "1": 2.1, "2": 1.1 } exclusion_volume_shape: ellipsoid shuffle: true @@ -388,7 +395,6 @@ Cylindrical exclusion volumes: obs errors: [1.0] air_pressures: [ 0, 10, 20, 30, 40 ] Poisson Disk Thinning: - variables: [air_temperature] min_vertical_spacing: 21 min_time_spacing: PT25S exclusion_volume_shape: cylinder @@ -410,7 +416,6 @@ Ellipsoidal exclusion volumes: obs errors: [1.0] air_pressures: [ 0, 10, 20, 30, 40 ] Poisson Disk Thinning: - variables: [air_temperature] min_vertical_spacing: 21 min_time_spacing: PT25S exclusion_volume_shape: ellipsoid @@ -432,7 +437,6 @@ Incorrectly ordered min horizontal spacings: air_pressures: [ 0, 0, 0 ] priority: [ 1, 2, 3 ] Poisson Disk Thinning: - variables: [air_temperature] min_horizontal_spacing: { "1": 1, "2": 2, "3": 3 } exclusion_volume_shape: ellipsoid shuffle: false @@ -455,7 +459,6 @@ Incorrectly ordered min vertical spacings: air_pressures: [ 0, 0, 0 ] priority: [ 1, 2, 3 ] Poisson Disk Thinning: - variables: [air_temperature] min_vertical_spacing: { "1": 1, "2": 2, "3": 3 } exclusion_volume_shape: ellipsoid shuffle: false @@ -478,7 +481,6 @@ Incorrectly ordered min time spacings: air_pressures: [ 0, 0, 0 ] priority: [ 1, 2, 3 ] Poisson Disk Thinning: - variables: [air_temperature] min_time_spacing: { "1": PT1H, "2": PT2H, "3": PT3H } exclusion_volume_shape: ellipsoid shuffle: false diff --git a/test/testinput/qc_preqc.yaml b/test/testinput/qc_preqc.yaml index 1d4cc9688..0fa3a57d4 100644 --- a/test/testinput/qc_preqc.yaml +++ b/test/testinput/qc_preqc.yaml @@ -8,7 +8,7 @@ observations: obs space: name: Radiosonde obsdatain: - obsfile: Data/ioda/testinput_tier_1/sondes_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_m.nc4 simulated variables: [air_temperature] geovals: filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_m.nc4 @@ -19,7 +19,7 @@ observations: - obs space: name: amsua_n19 obsdatain: - obsfile: Data/ioda/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 4-8 obs filters: diff --git a/test/testinput/qc_preqc_halo.yaml b/test/testinput/qc_preqc_halo.yaml new file mode 100644 index 000000000..0f7e57e5a --- /dev/null +++ b/test/testinput/qc_preqc_halo.yaml @@ -0,0 +1,31 @@ +window begin: 2018-04-14T20:30:00Z +window end: 2018-04-15T03:30:00Z + +observations: +- obs operator: + name: VertInterp + VertCoord: air_pressure + obs space: + name: Radiosonde + distribution: Halo + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_m.nc4 + simulated variables: [air_temperature] + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_m.nc4 + obs filters: + - filter: PreQC + maxvalue: 3 + passedBenchmark: 155 +- obs space: + name: amsua_n19 + distribution: Halo + obsdatain: + obsfile: Data/ufo/testinput_tier_1/amsua_n19_obs_2018041500_m.nc4 + simulated variables: [brightness_temperature] + channels: 4-8 + obs filters: + - filter: PreQC + minvalue: -2 + maxvalue: 8 + passedBenchmark: 377 diff --git a/test/testinput/qc_profile_background_check.yaml b/test/testinput/qc_profile_background_check.yaml new file mode 100644 index 000000000..caaf80023 --- /dev/null +++ b/test/testinput/qc_profile_background_check.yaml @@ -0,0 +1,100 @@ +window begin: 2020-12-31T23:59:00Z +window end: 2021-01-01T00:01:00Z + +observations: +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/profile_filter_testdata.nc4 + obsgrouping: + group variables: [ "record_number" ] + sort variable: "latitude" + sort order: "descending" + simulated variables: [variable] + HofX: HofX + obs filters: + - filter: Profile Background Check + filter variables: + - name: variable + absolute threshold: 2.5 + passedBenchmark: 59 # Accepts profiles 1, 2, 4, 6, 7, 9 (note that profile 1 has some missing data) +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/profile_filter_testdata.nc4 + obsgrouping: + group variables: [ "record_number" ] + sort variable: "latitude" + sort order: "descending" + simulated variables: [variable] + HofX: HofX + obs filters: + - filter: Profile Background Check + filter variables: + - name: variable + absolute threshold: 1.1 # Accepts profiles 1, 4, 6, 9 + passedBenchmark: 39 +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/profile_filter_testdata.nc4 + obsgrouping: + group variables: [ "record_number" ] + sort variable: "latitude" + sort order: "descending" + simulated variables: [variable] + HofX: HofX + obs filters: + - filter: Profile Background Check + filter variables: + - name: variable + absolute threshold: 110 # Accepts all profiles + passedBenchmark: 99 +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/profile_filter_testdata.nc4 + obsgrouping: + group variables: [ "record_number" ] + sort variable: "latitude" + sort order: "descending" + simulated variables: [variable] + HofX: HofX + obs filters: + - filter: Profile Background Check + filter variables: + - name: variable + relative threshold: 1.1 + passedBenchmark: 59 # Accepts profiles 1, 2, 3, 7, 9, 10 +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/profile_filter_testdata.nc4 + obsgrouping: + group variables: [ "record_number" ] + sort variable: "latitude" + sort order: "descending" + simulated variables: [variable] + HofX: HofX + obs filters: + - filter: Profile Background Check + filter variables: + - name: variable + relative threshold: 0.5 # Accepts profile 7 + passedBenchmark: 10 +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/profile_filter_testdata.nc4 + obsgrouping: + group variables: [ "record_number" ] + sort variable: "latitude" + sort order: "descending" + simulated variables: [variable] + HofX: HofX + obs filters: + - filter: Profile Background Check + filter variables: + - name: variable + relative threshold: 110 # Accepts all profiles + passedBenchmark: 99 diff --git a/test/testinput/qc_profile_fewobs_check.yaml b/test/testinput/qc_profile_fewobs_check.yaml new file mode 100644 index 000000000..2a40c5b71 --- /dev/null +++ b/test/testinput/qc_profile_fewobs_check.yaml @@ -0,0 +1,61 @@ +window begin: 2020-12-31T23:59:00Z +window end: 2021-01-01T00:01:00Z + +observations: +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/profile_filter_testdata.nc4 + obsgrouping: + group variables: ["record_number"] + simulated variables: [variable] + obs filters: + - filter: Profile Few Observations Check + filter variables: + - name: variable + threshold: 10 + passedBenchmark: 90 # Rejects only profile 1, which has missing data +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/profile_filter_testdata.nc4 + obsgrouping: + group variables: ["record_number"] + simulated variables: [variable] + obs filters: + - filter: Profile Few Observations Check + filter variables: + - name: variable + threshold: 9 + passedBenchmark: 99 # Accepts all profiles +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/profile_filter_testdata.nc4 + obsgrouping: + group variables: ["record_number"] + simulated variables: [variable] + obs filters: + - filter: Profile Few Observations Check + filter variables: + - name: variable + threshold: 11 + passedBenchmark: 0 # Rejects all profiles +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/profile_filter_testdata.nc4 + obsgrouping: + group variables: ["record_number"] + simulated variables: [variable] + obs filters: + - filter: Profile Few Observations Check + filter variables: + - name: variable + threshold: 11 + where: + - variable: + name: record_number@MetaData + maxvalue: 4 # First five profiles + passedBenchmark: 50 # Rejects all profiles, except those removed by where + diff --git a/test/testinput/qc_recursive_splitter.yaml b/test/testinput/qc_recursive_splitter.yaml deleted file mode 100644 index 531583e27..000000000 --- a/test/testinput/qc_recursive_splitter.yaml +++ /dev/null @@ -1 +0,0 @@ -# No input parameters needed. diff --git a/test/testinput/qc_stuckcheck.yaml b/test/testinput/qc_stuckcheck.yaml new file mode 100644 index 000000000..88a61cfc8 --- /dev/null +++ b/test/testinput/qc_stuckcheck.yaml @@ -0,0 +1,70 @@ +# This tests that the Stuck Check filter flags the expected observations. + +window begin: 2000-01-01T00:00:00Z +window end: 2029-12-12T23:59:59Z + +observations: +- obs space: + name: Ship + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfcship_obs_2018041500_m.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [northward_wind] + obs filters: + - filter: Stuck Check + filter variables: [northward_wind] + number stuck tolerance: 7 + time stuck tolerance: PT2M + flaggedObservationsBenchmark: &referenceFlaggedObsIds + - 15 + - 16 + - 17 + - 18 + - 19 + - 20 + - 21 + - 22 + flaggedBenchmark: 8 + benchmarkFlag: 21 # track +- obs space: + name: Ship + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfcship_obs_2018041500_m.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [northward_wind] + obs filters: + - filter: Stuck Check + number stuck tolerance: 7 + time stuck tolerance: PT2M + flaggedObservationsBenchmark: *referenceFlaggedObsIds + flaggedBenchmark: 8 + benchmarkFlag: 21 # track +- obs space: + name: Ship + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfcship_obs_2018041500_m.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [northward_wind] + obs filters: + - filter: Stuck Check + number stuck tolerance: 7 + time stuck tolerance: PT2H + flaggedBenchmark: 0 + benchmarkFlag: 21 # track +- obs space: + name: Ship + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfcship_obs_2018041500_m.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [northward_wind] + obs filters: + - filter: Stuck Check + filter variables: [northward_wind] + number stuck tolerance: 8 + time stuck tolerance: PT2M + flaggedBenchmark: 0 + benchmarkFlag: 21 # track diff --git a/test/testinput/qc_stuckcheck_unittests.yaml b/test/testinput/qc_stuckcheck_unittests.yaml new file mode 100644 index 000000000..243a3df2e --- /dev/null +++ b/test/testinput/qc_stuckcheck_unittests.yaml @@ -0,0 +1,161 @@ +All observations' number and time stuck tolerated: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z', + '2010-01-01T05:00:00Z', '2010-01-01T06:00:00Z', '2010-01-01T08:00:00Z' ] + obs errors: [1.0] + air_temperatures: [ 281.0, 281.0, 282.0, 282.0, 283.0, 283.0] + Stuck Check: &referenceFilterArguments + filter variables: [air_temperature] + number stuck tolerance: 2 + time stuck tolerance: PT2H + expected_rejected_obs_indices: [] + +Time stuck tolerated, number stuck untolerated, more than 1 value: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:30:00Z', '2010-01-01T00:31:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T03:01:00Z', '2010-01-01T03:02:00Z' ] + obs errors: [1.0] + air_temperatures: [ 282.0, 282.0, 282.0, 283.0, 283.0, 283.0] + Stuck Check: *referenceFilterArguments + expected_rejected_obs_indices: [] + +Time stuck tolerated, number stuck untolerated, 1 value: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:01Z', '2010-01-01T00:00:02Z', + '2010-01-01T00:00:03Z', '2010-01-01T00:00:04Z', '2010-01-01T00:00:05Z' ] + obs errors: [1.0] + air_temperatures: [ 282.0, 282.0, 282.0, 282.0, 282.0, 282.0 ] + Stuck Check: *referenceFilterArguments + expected_rejected_obs_indices: [ 0, 1, 2, 3, 4, 5 ] + +Time stuck tolerated, number stuck tolerated, 1 value: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1 ] + lons: [ 0, 1 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:01Z' ] + obs errors: [1.0] + air_temperatures: [ 282.0, 282.0 ] + Stuck Check: *referenceFilterArguments + expected_rejected_obs_indices: [] + +Time stuck untolerated for both streaks, number stuck tolerated for second streak (but not first): + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T04:00:00Z', + '2010-01-01T06:00:00Z', '2010-01-01T08:00:00Z', '2010-01-01T12:00:00Z' ] + obs errors: [1.0] + air_temperatures: [ 282.0, 282.0, 282.0, 282.0, 283.0, 283.0 ] + Stuck Check: *referenceFilterArguments + expected_rejected_obs_indices: [ 0, 1, 2, 3 ] + +Time stuck tolerated for first streak (but not second), number stuck untolerated for both streaks: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:01Z', '2010-01-01T00:00:02Z', + '2010-01-01T06:00:00Z', '2010-01-01T08:00:00Z', '2010-01-01T12:00:00Z' ] + obs errors: [1.0] + air_temperatures: [ 282.0, 282.0, 282.0, 283.0, 283.0, 283.0 ] + Stuck Check: *referenceFilterArguments + expected_rejected_obs_indices: [ 3, 4, 5 ] + +All temperatures different: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:01Z', '2010-01-01T00:00:02Z', + '2010-01-01T06:00:00Z', '2010-01-01T08:00:00Z', '2010-01-01T12:00:00Z' ] + obs errors: [1.0] + air_temperatures: [ 280.0, 281.0, 282.0, 283.0, 284.0, 285.0] + Stuck Check: *referenceFilterArguments + expected_rejected_obs_indices: [] + +Different variable streaks: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [ air_temperature, air_pressure ] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T04:00:00Z', + '2010-01-01T06:00:00Z', '2010-01-01T08:00:00Z', '2010-01-01T10:00:00Z' ] + obs errors: [1.0, 1.0] + air_temperatures: [ 280.0, 281.0, 282.0, 283.0, 283.0, 283.0 ] + air_pressures: [ 1.0, 1.5, 1.5, 1.5, 1.2, 1.1 ] + Stuck Check: + filter variables: [ air_temperature, air_pressure ] + number stuck tolerance: 2 + time stuck tolerance: PT2H + expected_rejected_obs_indices: [ 1, 2, 3, 4, 5 ] +Different variable streaks, 1 filter variable considered: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [ air_temperature, air_pressure ] + generate: + list: + lats: [ 0, 1, 2, 3, 4, 5 ] + lons: [ 0, 1, 2, 3, 4, 5 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T04:00:00Z', + '2010-01-01T06:00:00Z', '2010-01-01T08:00:00Z', '2010-01-01T10:00:00Z' ] + obs errors: [1.0, 1.0] + air_temperatures: [ 280.0, 281.0, 282.0, 283.0, 283.0, 283.0 ] + air_pressures: [ 1.0, 1.5, 1.5, 1.5, 1.2, 1.1 ] + Stuck Check: + filter variables: [ air_temperature ] + number stuck tolerance: 2 + time stuck tolerance: PT2H + expected_rejected_obs_indices: [ 3, 4, 5 ] diff --git a/test/testinput/qc_temporal_thinning.yaml b/test/testinput/qc_temporal_thinning.yaml index 2bb4b8d5b..1a1ecbea3 100644 --- a/test/testinput/qc_temporal_thinning.yaml +++ b/test/testinput/qc_temporal_thinning.yaml @@ -3,7 +3,7 @@ window end: 2030-01-01T00:00:00Z observations: - obs space: - name: Aircraft + name: Ship obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_temporal_thinning_surface.nc4 simulated variables: [air_temperature] @@ -67,8 +67,9 @@ observations: - 8247 - 8812 passedBenchmark: 21 +# Observations not grouped into records - obs space: - name: Aircraft + name: Radiosonde obsdatain: obsfile: Data/ufo/testinput_tier_1/met_office_temporal_thinning_sonde.nc4 simulated variables: [air_temperature] @@ -84,7 +85,7 @@ observations: - variable: name: obs_type@MetaData is_not_in: 50400 - passedObservationsBenchmark: + passedObservationsBenchmark: &sondePassedObsIds - 2 - 3 - 4 @@ -180,3 +181,49 @@ observations: - 148 - 149 passedBenchmark: 94 +# Observations grouped into records by the category variable +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_temporal_thinning_sonde.nc4 + obsgrouping: + group variables: [ "call_sign" ] + simulated variables: [air_temperature] + obs filters: + - filter: Temporal Thinning + min_spacing: PT00H56M00S + tolerance: PT00H02M00S + category_variable: + name: call_sign@MetaData + priority_variable: + name: num_levels@MetaData + where: + - variable: + name: obs_type@MetaData + is_not_in: 50400 + passedObservationsBenchmark: + *sondePassedObsIds + passedBenchmark: 94 +# Observations grouped into records by a variable different from the category variable +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_temporal_thinning_sonde.nc4 + obsgrouping: + group variables: [ "num_levels" ] + simulated variables: [air_temperature] + obs filters: + - filter: Temporal Thinning + min_spacing: PT00H56M00S + tolerance: PT00H02M00S + category_variable: + name: call_sign@MetaData + priority_variable: + name: num_levels@MetaData + where: + - variable: + name: obs_type@MetaData + is_not_in: 50400 + passedObservationsBenchmark: + *sondePassedObsIds + passedBenchmark: 94 diff --git a/test/testinput/qc_temporal_thinning_unittests.yaml b/test/testinput/qc_temporal_thinning_unittests.yaml index 4c23f0d91..7531f3982 100644 --- a/test/testinput/qc_temporal_thinning_unittests.yaml +++ b/test/testinput/qc_temporal_thinning_unittests.yaml @@ -18,7 +18,6 @@ Min_spacing below observation spacing: - 2010-01-01T00:05:00Z obs errors: [1.0] TemporalThinning: - variables: [air_temperature] min_spacing: PT9S expected_thinned_obs_indices: [] @@ -42,7 +41,6 @@ Min spacing equal to observation spacing: - 2010-01-01T00:05:00Z obs errors: [1.0] TemporalThinning: - variables: [air_temperature] min_spacing: PT10S expected_thinned_obs_indices: [] @@ -66,11 +64,10 @@ Min spacing above observation spacing: - 2010-01-01T00:05:00Z obs errors: [1.0] TemporalThinning: - variables: [air_temperature] min_spacing: PT11S expected_thinned_obs_indices: [1, 3, 5] -Categories: +Int-valued categories: window begin: 2000-01-01T00:00:00Z window end: 2030-01-01T00:00:00Z obs space: @@ -91,12 +88,37 @@ Categories: obs errors: [1.0] category: [0, 0, 0, 1, 1, 1, 1] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S category_variable: name: category@MetaData expected_thinned_obs_indices: [1, 4, 6] +String-valued categories: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Aircraft + simulated variables: [air_temperature] + generate: + list: + lats: [ 0, 0, 0, 0, 0, 0, 0 ] + lons: [ 0, 0, 0, 0, 0, 0, 0 ] + datetimes: + - 2010-01-01T00:04:00Z + - 2010-01-01T00:04:10Z + - 2010-01-01T00:04:20Z + - 2010-01-01T00:04:30Z + - 2010-01-01T00:04:40Z + - 2010-01-01T00:04:50Z + - 2010-01-01T00:05:00Z + obs errors: [1.0] + string_category: [a, a, a, b, b, b, b] + TemporalThinning: + min_spacing: PT15S + category_variable: + name: string_category@MetaData + expected_thinned_obs_indices: [1, 4, 6] + Categories, observations sorted in descending order: window begin: 2000-01-01T00:00:00Z window end: 2030-01-01T00:00:00Z @@ -118,7 +140,6 @@ Categories, observations sorted in descending order: obs errors: [1.0] category: [1, 1, 1, 1, 0, 0, 0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S category_variable: name: category@MetaData @@ -145,7 +166,6 @@ Categories, where clause: obs errors: [1.0] category: [1, 1, 1, 1, 0, 0, 0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S category_variable: name: category@MetaData @@ -199,7 +219,6 @@ Tolerance and priorities, first observation in each group to be retained: 0, 0, 0, 0, 0, 0, 0, 0, 0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S tolerance: PT05S priority_variable: @@ -253,7 +272,6 @@ Tolerance and priorities, second observation in each group to be retained: 0, 1, 1, 0, 1, 1, 0, 1, 1] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S tolerance: PT05S priority_variable: @@ -307,7 +325,6 @@ Tolerance and priorities, third observation in each group to be retained: 0, 0, 1, 0, 0, 1, 0, 0, 1] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S tolerance: PT05S priority_variable: @@ -357,7 +374,6 @@ Tolerance but no priorities: - 2010-01-01T00:05:02Z obs errors: [1.0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S tolerance: PT05S expected_thinned_obs_indices: [ 1, 2, 3, 4, 5, @@ -385,7 +401,6 @@ Seed time inside observation time range (should pick preceding observation): - 2010-01-01T00:05:00Z obs errors: [1.0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S seed_time: 2010-01-01T00:04:34Z expected_thinned_obs_indices: [0, 2, 4, 6] @@ -410,7 +425,6 @@ Seed time inside observation time range (should pick following observation): - 2010-01-01T00:05:00Z obs errors: [1.0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S seed_time: 2010-01-01T00:04:36Z expected_thinned_obs_indices: [1, 3, 5] @@ -435,7 +449,6 @@ Seed time midway between two observations (should pick following observation): - 2010-01-01T00:05:00Z obs errors: [1.0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S seed_time: 2010-01-01T00:04:25Z expected_thinned_obs_indices: [0, 2, 4, 6] @@ -460,7 +473,6 @@ Seed time at earliest observation: - 2010-01-01T00:05:00Z obs errors: [1.0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S seed_time: 2010-01-01T00:04:00Z expected_thinned_obs_indices: [1, 3, 5] @@ -485,7 +497,6 @@ Seed time before earliest observation: - 2010-01-01T00:05:00Z obs errors: [1.0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S seed_time: 2010-01-01T00:03:00Z expected_thinned_obs_indices: [1, 3, 5] @@ -510,7 +521,6 @@ Seed time at latest observation: - 2010-01-01T00:05:00Z obs errors: [1.0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S seed_time: 2010-01-01T00:06:00Z expected_thinned_obs_indices: [1, 3, 5] @@ -535,7 +545,6 @@ Seed time after latest observation: - 2010-01-01T00:05:00Z obs errors: [1.0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S seed_time: 2010-01-01T00:06:00Z expected_thinned_obs_indices: [1, 3, 5] @@ -584,7 +593,6 @@ Tolerance, priorities and seed time at a low-priority observation followed by a 0, 0, 1, 0, 0, 1, 0, 0, 1] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S tolerance: PT02S priority_variable: @@ -639,7 +647,6 @@ Tolerance, priorities and seed time at a low-priority observation preceded by a 1, 0, 0, 1, 0, 0, 1, 0, 0] TemporalThinning: - variables: [air_temperature] min_spacing: PT15S tolerance: PT02S priority_variable: diff --git a/test/testinput/qc_thinning.yaml b/test/testinput/qc_thinning.yaml index 43ee82049..7cc15b390 100644 --- a/test/testinput/qc_thinning.yaml +++ b/test/testinput/qc_thinning.yaml @@ -5,7 +5,7 @@ observations: - obs space: name: Radiosonde obsdatain: - obsfile: Data/ioda/testinput_tier_1/sondes_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_m.nc4 simulated variables: [air_temperature, eastward_wind, northward_wind] obs filters: - filter: Thinning diff --git a/test/testinput/qc_trackcheck.yaml b/test/testinput/qc_trackcheck.yaml index cec3a7333..79613cdf6 100644 --- a/test/testinput/qc_trackcheck.yaml +++ b/test/testinput/qc_trackcheck.yaml @@ -1,8 +1,9 @@ # These tests compare the output of the Track Check filter against reference # results obtained with the Met Office OPS code (Ops_AirTrackCheck). The first test case is used as # a baseline; in subsequent test cases the filter's configuration contains one parameter -# whose value differs from the first case. The last case is identical to the first one except that -# observations are grouped explicitly by the station_id variable rather than the record number. +# whose value differs from the first case. The last two cases are identical to the first one +# except that observations are grouped explicitly by the station_id variable rather than the +# record number. window begin: 2000-01-01T00:00:00Z window end: 2029-12-12T23:59:59Z @@ -11,13 +12,12 @@ observations: - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [specific_humidity] obs filters: - filter: Track Check - variables: [specific_humidity] temporal_resolution: PT00H00M30S spatial_resolution: 20.000000 distinct_buddy_resolution_multiplier: 3 @@ -25,7 +25,7 @@ observations: max_climb_rate: 200.000000 max_speed_interpolation_points: {"0": 1000.000000, "20000": 400.000000, "100000": 200.000000, "110000": 200.000000} rejection_threshold: 0.500000 - flaggedObservationsBenchmark: + flaggedObservationsBenchmark: &referenceCaseFlaggedObsIds - 184 - 187 - 188 @@ -63,17 +63,16 @@ observations: - 2089 - 2098 flaggedBenchmark: 36 - benchmarkFlag: 12 # track + benchmarkFlag: 21 # track - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [specific_humidity] obs filters: - filter: Track Check - variables: [specific_humidity] temporal_resolution: PT00H00M30S spatial_resolution: 20.000000 distinct_buddy_resolution_multiplier: 3 @@ -130,17 +129,16 @@ observations: - 2089 - 2098 flaggedBenchmark: 47 - benchmarkFlag: 12 # track + benchmarkFlag: 21 # track - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [specific_humidity] obs filters: - filter: Track Check - variables: [specific_humidity] temporal_resolution: PT00H00M30S spatial_resolution: 20.000000 distinct_buddy_resolution_multiplier: 3 @@ -200,17 +198,16 @@ observations: - 2089 - 2098 flaggedBenchmark: 50 - benchmarkFlag: 12 # track + benchmarkFlag: 21 # track - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [specific_humidity] obs filters: - filter: Track Check - variables: [specific_humidity] temporal_resolution: PT00H00M30S spatial_resolution: 20.000000 distinct_buddy_resolution_multiplier: 3 @@ -302,17 +299,16 @@ observations: - 2094 - 2099 flaggedBenchmark: 82 - benchmarkFlag: 12 # track + benchmarkFlag: 21 # track - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [specific_humidity] obs filters: - filter: Track Check - variables: [specific_humidity] temporal_resolution: PT00H00M30S spatial_resolution: 0.500000 distinct_buddy_resolution_multiplier: 3 @@ -356,17 +352,16 @@ observations: - 2089 - 2098 flaggedBenchmark: 34 - benchmarkFlag: 12 # track + benchmarkFlag: 21 # track - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [specific_humidity] obs filters: - filter: Track Check - variables: [specific_humidity] temporal_resolution: PT00H00M45S spatial_resolution: 20.000000 distinct_buddy_resolution_multiplier: 3 @@ -378,17 +373,16 @@ observations: - 193 - 195 flaggedBenchmark: 2 - benchmarkFlag: 12 # track + benchmarkFlag: 21 # track - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [specific_humidity] obs filters: - filter: Track Check - variables: [specific_humidity] temporal_resolution: PT00H00M30S spatial_resolution: 20.000000 distinct_buddy_resolution_multiplier: 3 @@ -432,17 +426,16 @@ observations: - 2089 - 2098 flaggedBenchmark: 34 - benchmarkFlag: 12 # track + benchmarkFlag: 21 # track - obs space: name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 obsgrouping: - group variable: "station_id" + group variables: [ "station_id" ] simulated variables: [specific_humidity] obs filters: - filter: Track Check - variables: [specific_humidity] temporal_resolution: PT00H00M30S spatial_resolution: 20.000000 distinct_buddy_resolution_multiplier: 3 @@ -484,15 +477,14 @@ observations: - 2089 - 2098 flaggedBenchmark: 32 - benchmarkFlag: 12 # track -- obs space: + benchmarkFlag: 21 # track +- obs space: # No grouping into records name: Aircraft obsdatain: - obsfile: Data/ioda/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 simulated variables: [specific_humidity] obs filters: - filter: Track Check - variables: [specific_humidity] temporal_resolution: PT00H00M30S spatial_resolution: 20.000000 distinct_buddy_resolution_multiplier: 3 @@ -502,42 +494,27 @@ observations: rejection_threshold: 0.500000 station_id_variable: name: station_id@MetaData - flaggedObservationsBenchmark: - - 184 - - 187 - - 188 - - 193 - - 268 - - 270 - - 307 - - 312 - - 370 - - 377 - - 466 - - 467 - - 700 - - 712 - - 751 - - 753 - - 755 - - 762 - - 788 - - 795 - - 806 - - 808 - - 990 - - 991 - - 996 - - 998 - - 1005 - - 1013 - - 1018 - - 1023 - - 1029 - - 1030 - - 1809 - - 1810 - - 2089 - - 2098 + flaggedObservationsBenchmark: *referenceCaseFlaggedObsIds + flaggedBenchmark: 36 + benchmarkFlag: 21 # track +- obs space: # Group into records (perversely) by something else than the station ID + name: Aircraft + obsdatain: + obsfile: Data/ufo/testinput_tier_1/aircraft_obs_2018041500_m.nc4 + obsgrouping: + group variables: [ "latitude" ] + simulated variables: [specific_humidity] + obs filters: + - filter: Track Check + temporal_resolution: PT00H00M30S + spatial_resolution: 20.000000 + distinct_buddy_resolution_multiplier: 3 + num_distinct_buddies_per_direction: 3 + max_climb_rate: 200.000000 + max_speed_interpolation_points: {"0": 1000.000000, "20000": 400.000000, "100000": 200.000000, "110000": 200.000000} + rejection_threshold: 0.500000 + station_id_variable: + name: station_id@MetaData + flaggedObservationsBenchmark: *referenceCaseFlaggedObsIds flaggedBenchmark: 36 - benchmarkFlag: 12 # track + benchmarkFlag: 21 # track diff --git a/test/testinput/qc_trackcheck_unittests.yaml b/test/testinput/qc_trackcheck_unittests.yaml index ccd6dd88b..2c51bd573 100644 --- a/test/testinput/qc_trackcheck_unittests.yaml +++ b/test/testinput/qc_trackcheck_unittests.yaml @@ -14,7 +14,6 @@ Only valid observations: air_pressures: [ 100000, 90000, 80000, 90000, 100000] station_ids: [ 1, 1, 1, 1, 1] Track Check: - variables: [air_temperature] temporal_resolution: PT4S spatial_resolution: 1 # km distinct_buddy_resolution_multiplier: 3 @@ -42,7 +41,6 @@ One observation with excessive speed: air_pressures: [ 100000, 90000, 80000, 90000, 100000] station_ids: [ 1, 1, 1, 1, 1] Track Check: - variables: [air_temperature] temporal_resolution: PT4S spatial_resolution: 1 # km distinct_buddy_resolution_multiplier: 3 @@ -70,7 +68,6 @@ One observation with excessive climb rate: air_pressures: [ 100000, 90000, 1e20, 90000, 100000] station_ids: [ 1, 1, 1, 1, 1] Track Check: - variables: [air_temperature] temporal_resolution: PT4S spatial_resolution: 1 # km distinct_buddy_resolution_multiplier: 3 @@ -98,7 +95,6 @@ Climb rate checks disabled: air_pressures: [ 100000, 90000, 1e20, 90000, 100000] station_ids: [ 1, 1, 1, 1, 1] Track Check: - variables: [air_temperature] temporal_resolution: PT4S spatial_resolution: 1 # km distinct_buddy_resolution_multiplier: 3 @@ -125,7 +121,6 @@ Where clause: air_pressures: [ 1e20, 90000, 1e20, 90000, 100000] station_ids: [ 1, 1, 1, 1, 1] Track Check: - variables: [air_temperature] temporal_resolution: PT4S spatial_resolution: 1 # km distinct_buddy_resolution_multiplier: 3 @@ -199,7 +194,6 @@ Multiple clusters of observations with excessive speed: 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] Track Check: - variables: [air_temperature] temporal_resolution: PT1M spatial_resolution: 112 # km distinct_buddy_resolution_multiplier: 3 @@ -230,7 +224,6 @@ Multiple station ids: air_pressures: [ 100000, 100000, 90000, 90000, 80000, 80000, 90000, 90000, 100000, 100000] station_ids: [ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2] Track Check: - variables: [air_temperature] temporal_resolution: PT4S spatial_resolution: 1 # km distinct_buddy_resolution_multiplier: 3 @@ -257,7 +250,6 @@ No station id variable, no observation grouping: obs errors: [1.0] air_pressures: [ 100000, 90000, 80000, 90000, 100000] Track Check: - variables: [air_temperature] temporal_resolution: PT4S spatial_resolution: 1 # km distinct_buddy_resolution_multiplier: 3 diff --git a/test/testinput/qc_trackcheckship.yaml b/test/testinput/qc_trackcheckship.yaml new file mode 100644 index 000000000..680fe6ffc --- /dev/null +++ b/test/testinput/qc_trackcheckship.yaml @@ -0,0 +1,125 @@ +# This test compares the output of the Track Check Ship filter against reference +# results obtained with the Met Office OPS code (Ops_CheckShipTrack/Ops_OceanTrackCheck). + +window begin: 2000-01-01T00:00:00Z +window end: 2029-12-12T23:59:59Z + +observations: +- obs space: + name: Ship + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfcship_synthetic_airtemp_m.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [air_temperature] + obs filters: + - filter: Ship Track Check + unit testing mode: false + early break check: false + temporal resolution: PT00H00M1S + spatial resolution (km): 0.0000001 + max speed (m/s): 0.01 + rejection threshold: 0.5 + flaggedObservationsBenchmark: &referenceFlaggedObsIds + - 0 + - 1 + - 2 + - 3 + - 4 + - 87 + - 88 + - 89 + - 90 + - 91 + - 92 + - 165 + - 166 + - 167 + - 168 + - 169 + - 170 + - 171 + - 172 + - 173 + - 174 + - 175 + - 176 + - 237 + - 238 + - 239 + - 240 + - 241 + - 242 + - 243 + - 244 + - 245 + - 246 + - 247 + - 248 + flaggedBenchmark: 35 + benchmarkFlag: 21 # track +- obs space: + name: Ship + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfcship_synthetic_airtemp_m.nc4 + obsgrouping: + group variables: [ "station_id" ] + simulated variables: [air_temperature] + obs filters: + - filter: Ship Track Check + unit testing mode: false + early break check: true + temporal resolution: PT00H00M1S + spatial resolution (km): 0.0000001 + max speed (m/s): 0.01 + rejection threshold: 0.5 + flaggedObservationsBenchmark: + - 87 + - 88 + - 89 + - 90 + - 91 + - 92 + flaggedBenchmark: 6 + benchmarkFlag: 21 # track +# Like in the first test, but without grouping observations into records +- obs space: + name: Ship + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfcship_synthetic_airtemp_m.nc4 + simulated variables: [air_temperature] + obs filters: + - filter: Ship Track Check + unit testing mode: false + early break check: false + temporal resolution: PT00H00M1S + spatial resolution (km): 0.0000001 + max speed (m/s): 0.01 + rejection threshold: 0.5 + station_id_variable: + name: station_id@MetaData + flaggedObservationsBenchmark: *referenceFlaggedObsIds + flaggedBenchmark: 35 + benchmarkFlag: 21 # track +# Like in the first test, but with observations grouped into records (perversely) by +# a variable other than station_id +- obs space: + name: Ship + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfcship_synthetic_airtemp_m.nc4 + obsgrouping: + group variables: [ "latitude" ] + simulated variables: [air_temperature] + obs filters: + - filter: Ship Track Check + unit testing mode: false + early break check: false + temporal resolution: PT00H00M1S + spatial resolution (km): 0.0000001 + max speed (m/s): 0.01 + rejection threshold: 0.5 + station_id_variable: + name: station_id@MetaData + flaggedObservationsBenchmark: *referenceFlaggedObsIds + flaggedBenchmark: 35 + benchmarkFlag: 21 # track diff --git a/test/testinput/qc_trackcheckship_mainloop_unittests.yaml b/test/testinput/qc_trackcheckship_mainloop_unittests.yaml index 0ca616b77..b1ba96068 100644 --- a/test/testinput/qc_trackcheckship_mainloop_unittests.yaml +++ b/test/testinput/qc_trackcheckship_mainloop_unittests.yaml @@ -12,12 +12,12 @@ Normal ship track: obs errors: [1.0] station_ids: [ 1, 1, 1 ] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT10M - spatial resolution: 1 # km - max speed: 1 #km/s (1 lat/lon unit diff/hr ~ .0308) + spatial resolution (km): 1 + max speed (m/s): 1000 # (1 lat/lon unit diff/hr ~ 30.8) + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [] @@ -35,18 +35,18 @@ Ship track error first segment category 2: generate: list: lats: [ 0, 1, 1.1, 1.1 ] - lons: [ 0, 0, 0, .1 ] + lons: [ 0, 0, 0, 0.1 ] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z' ] obs errors: [1.0] station_ids: [ 1, 1, 1, 1 ] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT10M - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [1] @@ -66,12 +66,12 @@ Ship track error first segment category 1: obs errors: [1.0] station_ids: [ 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT10M - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [0] @@ -84,19 +84,19 @@ Ship track error last segment category 2: simulated variables: [air_temperature] generate: list: - lats: [ 0, .5, 0, 1 ] + lats: [ 0, 0.5, 0, 1 ] lons: [ 0, 0, 0, 0 ] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z'] obs errors: [1.0] station_ids: [ 1, 1, 1, 1 ] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT10M - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [2] @@ -116,12 +116,12 @@ Ship track error last segment category 1: obs errors: [1.0] station_ids: [ 1, 1, 1 ] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT10M - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [2] @@ -134,19 +134,19 @@ Ship track error middle segment prev fast category 4: simulated variables: [air_temperature] generate: list: - lats: [ 0, .999, -.001, -.002] + lats: [ 0, 0.999, -0.001, -0.002] lons: [ 0, 0, 0, 0] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z'] obs errors: [1.0] station_ids: [ 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT10M - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [1] @@ -159,19 +159,19 @@ Ship track error middle segment prev fast category 104: simulated variables: [air_temperature] generate: list: - lats: [ 0, .999, 1.999, 2.5 ] + lats: [ 0, 0.999, 1.999, 2.5 ] lons: [ 0, 0, 0, 0 ] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z' ] obs errors: [ 1.0 ] station_ids: [ 1, 1, 1, 1 ] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT10M - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [ 1, 2 ] @@ -184,19 +184,19 @@ Ship track error middle segment next fast category 4: simulated variables: [air_temperature] generate: list: - lats: [ 0, .5, 1.5, .501] + lats: [ 0, 0.5, 1.5, 0.501] lons: [ 0, 0, 0, 0] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z'] obs errors: [1.0] station_ids: [ 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT10M - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [2] @@ -211,19 +211,19 @@ Ship track error middle segment next fast category 104: simulated variables: [air_temperature] generate: list: - lats: [ 0, .5, 1.5, 2.499] + lats: [ 0, 0.5, 1.5, 2.499] lons: [ 0, 0, 0, 0] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z'] obs errors: [1.0] station_ids: [ 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT10M - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [1,2] @@ -236,19 +236,19 @@ Ship track error middle segment average fast category 5: #uses time res to contr simulated variables: [air_temperature] generate: list: - lats: [ 0, .1, 1.1, 1.1] - lons: [ 0, 0, 0, .5] + lats: [ 0, 0.1, 1.1, 1.1] + lons: [ 0, 0, 0, 0.5] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T02:00:00Z'] obs errors: [1.0] station_ids: [ 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT1H - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [1] @@ -261,19 +261,19 @@ Ship track error middle segment prev average fast category 5: simulated variables: [air_temperature] generate: list: - lats: [ 0, .5, 1.5, .6 ] + lats: [ 0, 0.5, 1.5, 0.6 ] lons: [ 0, 0, 0, 0] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z'] obs errors: [1.0] station_ids: [ 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT1H - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [ 2 ] @@ -286,19 +286,19 @@ Ship track error middle segment prev average fast category 105: simulated variables: [air_temperature] generate: list: - lats: [ 0, .5, 1.5, 2.0 ] + lats: [ 0, 0.5, 1.5, 2.0 ] lons: [ 0, 0, 0, 0 ] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z' ] obs errors: [ 1.0 ] station_ids: [ 1, 1, 1, 1 ] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT1H - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [ 1, 2 ] @@ -311,19 +311,19 @@ Ship track error middle segment prev angle large category 6: simulated variables: [air_temperature] generate: list: - lats: [ 0, .1, .1, .1] + lats: [ 0, 0.1, 0.1, 0.1] lons: [ 0, 0, 1, 1.5] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z'] obs errors: [1.0] station_ids: [ 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT1H - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [1] @@ -336,19 +336,19 @@ Ship track error middle segment angle large category 6: simulated variables: [air_temperature] generate: list: - lats: [ 0, .1, 1.1, 1.1] - lons: [ 0, 0, 0, .1] + lats: [ 0, 0.1, 1.1, 1.1] + lons: [ 0, 0, 0, 0.1] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z'] obs errors: [1.0] station_ids: [ 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT1H - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [2] @@ -361,20 +361,20 @@ Ship track error middle segment previous previous angle large category 7: simulated variables: [air_temperature] generate: list: - lats: [ 0, 0, .1, 1.1, 1.1] - lons: [ 0, .1, .1, .1, .1] + lats: [ 0, 0, 0.1, 1.1, 1.1] + lons: [ 0, 0.1, 0.1, 0.1, 0.1] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z'] obs errors: [1.0] station_ids: [ 1, 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT1H - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [2] @@ -387,20 +387,20 @@ Ship track error middle segment next angle large category 7: simulated variables: [air_temperature] generate: list: - lats: [ 0, .1, 1.1, 1.2, 1.2] - lons: [ 0, 0, 0, 0, .1] + lats: [ 0, 0.1, 1.1, 1.2, 1.2] + lons: [ 0, 0, 0, 0, 0.1] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z'] obs errors: [1.0] station_ids: [ 1, 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT1H - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [2] @@ -420,12 +420,12 @@ Ship track error middle segment prev speed < half next speed category 8: obs errors: [1.0] station_ids: [ 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT1H - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [1] @@ -438,19 +438,19 @@ Ship track error middle segment next speed < half prev speed category 8: simulated variables: [air_temperature] generate: list: - lats: [ 0, .2, 1.2, 1.2] + lats: [ 0, 0.2, 1.2, 1.2] lons: [ 0, 0, 0, 0] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z' ] obs errors: [1.0] station_ids: [ 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT1H - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [2] @@ -463,19 +463,19 @@ Ship track error middle segment category 100: simulated variables: [air_temperature] generate: list: - lats: [ 0, .1, 1.1, 1.2 ] + lats: [ 0, 0.1, 1.1, 1.2 ] lons: [ 0, 0, 0, 0 ] datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z' ] obs errors: [1.0] station_ids: [ 1, 1, 1, 1] Ship Track Check: - testing mode: true + unit testing mode: true early break check: false - variables: [air_temperature] temporal resolution: PT1H - spatial resolution: 1 # km - max speed: .03 + spatial resolution (km): 1 + max speed (m/s): 30.0 + rejection threshold: 1.0 station_id_variable: name: station_id@MetaData expected first rejection index: [ 1, 2 ] diff --git a/test/testinput/qc_trackcheckship_unittests.yaml b/test/testinput/qc_trackcheckship_unittests.yaml index b88430eec..7a565c1ce 100644 --- a/test/testinput/qc_trackcheckship_unittests.yaml +++ b/test/testinput/qc_trackcheckship_unittests.yaml @@ -13,24 +13,24 @@ Normal ship track: obs errors: [1.0] station_ids: [ 1, 1, 1, 1, 1] Ship Track Check: - testing mode: true - variables: [air_temperature] + unit testing mode: true temporal resolution: PT10M - spatial resolution: 1 # km + spatial resolution (km): 1 + max speed (m/s): 1000.0 + rejection threshold: 1.0 + early break check: false station_id_variable: name: station_id@MetaData expected distance: [0, 157, 157, 157, 157] - expected speed: [0.0, .0435, .0435, .0435, .0435] + expected speed: [0.0, 43.5, 43.5, 43.5, 43.5] expected distance averaged: [0, 314, 314, 314, 0] - expected speed averaged: [0.0, .0435, .0435, .0435, 0.0] + expected speed averaged: [0.0, 43.5, 43.5, 43.5, 0.0] expected angle: [0.0, 0.0, 0.0, 0.0, 0.0] expected short: [0] expected fast: [0] expected bends: [0] - expected dist0: [0] - expected simultaneous: [0] - expected sum speed: [.174] - expected mean speed: [.0435] + expected sum speed: [174.0] + expected mean speed: [43.5] expected early breaks: [0] Bending ship track: window begin: 2000-01-01T00:00:00Z @@ -47,24 +47,24 @@ Bending ship track: obs errors: [1.0] station_ids: [ 1, 1, 1, 1, 1] Ship Track Check: - testing mode: true - variables: [air_temperature] + unit testing mode: true temporal resolution: PT10M - spatial resolution: 1 # km + spatial resolution (km): 1 + max speed (m/s): 1000.0 + rejection threshold: 1.0 + early break check: false station_id_variable: name: station_id@MetaData expected distance: [0, 111, 111, 111, 157] - expected speed: [0.0, .0308, .0308, .0308, .0435] + expected speed: [0.0, 30.8, 30.8, 30.8, 43.5] expected distance averaged: [0, 157, 0, 248, 0] - expected speed averaged: [0, .0218, 0, .0344, 0] + expected speed averaged: [0, 21.8, 0, 34.4, 0] expected angle: [0.0, 90.0, 180.0, 45.0, 0.0] expected short: [0] expected fast: [0] expected bends: [2] - expected dist0: [0] - expected simultaneous: [0] - expected sum speed: [.135] - expected mean speed: [.0339] + expected sum speed: [135.0] + expected mean speed: [33.9] expected early breaks: [0] Fast ship track: window begin: 2000-01-01T00:00:00Z @@ -81,23 +81,22 @@ Fast ship track: obs errors: [1.0] station_ids: [ 1, 1, 1] Ship Track Check: - testing mode: true - variables: [air_temperature] + unit testing mode: true temporal resolution: PT10M - spatial resolution: 1 # km - max speed: .01 #km/s (change from default) + spatial resolution (km): 1 + max speed (m/s): 10 + rejection threshold: 1.0 + early break check: false station_id_variable: name: station_id@MetaData expected distance: [0, 111, 111] - expected speed: [0.0, .0308, .0308] + expected speed: [0.0, 30.8, 30.8] expected distance averaged: [0, 222, 0] - expected speed averaged: [0, .0308, 0] + expected speed averaged: [0, 30.8, 0] expected angle: [0.0, 0.0, 0.0] expected short: [0] expected fast: [2] expected bends: [0] - expected dist0: [0] - expected simultaneous: [0] expected sum speed: [0.0] expected mean speed: [0.0] expected early breaks: [1] @@ -116,24 +115,23 @@ Short ship track: obs errors: [1.0] station_ids: [ 1, 1, 1 ] Ship Track Check: - input category: 2 - testing mode: true - variables: [air_temperature] + input category: 'SHPSYN' + unit testing mode: true temporal resolution: PT10M - spatial resolution: 1 # km - max speed: 1 #km/s (default value) + spatial resolution (km): 1 + max speed (m/s): 1000.0 + rejection threshold: 1.0 + early break check: false station_id_variable: name: station_id@MetaData expected distance: [0, 111, 111] - expected speed: [0.0, .0617, .0617] + expected speed: [0.0, 61.7, 61.7] expected distance averaged: [0, 222, 0] - expected speed averaged: [0, .0617, 0] + expected speed averaged: [0, 61.7, 0] expected angle: [0.0, 0.0, 0.0] expected short: [2] expected fast: [0] expected bends: [0] - expected dist0: [0] - expected simultaneous: [0] expected sum speed: [0.0] expected mean speed: [0.0] expected early breaks: [1] @@ -152,24 +150,23 @@ Short buoy track: obs errors: [1.0] station_ids: [ 1, 1, 1 ] Ship Track Check: - input category: 1 - testing mode: true - variables: [air_temperature] + input category: 'BUOYPROF' + unit testing mode: true temporal resolution: PT10M - spatial resolution: 1 # km - max speed: 1 #km/s (default value) + spatial resolution (km): 1 + max speed (m/s): 1000 + rejection threshold: 1.0 + early break check: false station_id_variable: name: station_id@MetaData expected distance: [0, 111, 111] - expected speed: [0.0, .0617, .0617] + expected speed: [0.0, 61.7, 61.7] expected distance averaged: [0, 222, 0] - expected speed averaged: [0, .0617, 0] + expected speed averaged: [0, 61.7, 0] expected angle: [0.0, 0.0, 0.0] expected short: [2] expected fast: [0] expected bends: [0] - expected dist0: [0] - expected simultaneous: [0] expected sum speed: [0.0] expected mean speed: [0.0] expected early breaks: [0] @@ -188,23 +185,22 @@ Fast/short ship track: obs errors: [1.0] station_ids: [ 1, 1, 1 ] Ship Track Check: - testing mode: true - variables: [air_temperature] + unit testing mode: true temporal resolution: PT10M - spatial resolution: 1 # km - max speed: .01 #km/s (default value) + spatial resolution (km): 1 + max speed (m/s): 10 + rejection threshold: 1.0 + early break check: false station_id_variable: name: station_id@MetaData expected distance: [0, 111, 111] - expected speed: [0.0, .185, .185] + expected speed: [0.0, 185.0, 185.0] expected distance averaged: [0, 222, 0] - expected speed averaged: [0, .185, 0] + expected speed averaged: [0, 185.0, 0] expected angle: [0.0, 0.0, 0.0] expected short: [2] expected fast: [0] expected bends: [0] - expected dist0: [0] - expected simultaneous: [0] expected sum speed: [0.0] expected mean speed: [0.0] expected early breaks: [1] @@ -222,10 +218,12 @@ Ship Track Dist0: obs errors: [1.0] station_ids: [ 1, 1, 1 ] Ship Track Check: - testing mode: true - variables: [air_temperature] + unit testing mode: true temporal resolution: PT10M - spatial resolution: 1 # km + spatial resolution (km): 1 + max speed (m/s): 1000.0 + rejection threshold: 1.0 + early break check: false station_id_variable: name: station_id@MetaData expected distance: [0, 0, 0] @@ -236,8 +234,6 @@ Ship Track Dist0: expected short: [0] expected fast: [0] expected bends: [0] - expected dist0: [2] - expected simultaneous: [0] expected sum speed: [0.0] expected mean speed: [0.0] expected early breaks: [0] @@ -255,45 +251,12 @@ Ship Track Simultaneous Same Location: obs errors: [1.0] station_ids: [ 1, 1, 1] Ship Track Check: - testing mode: true - variables: [air_temperature] + unit testing mode: true temporal resolution: PT10M - spatial resolution: 1 # km - station_id_variable: - name: station_id@MetaData - expected distance: [0, 0, 0] - expected speed: [0.0, 0.0, 0.0] - expected distance averaged: [0, 0, 0] - expected speed averaged: [0, 0, 0] - expected angle: [0.0, 0.0, 0.0] - expected short: [2] - expected fast: [0] - expected bends: [0] - expected dist0: [2] - expected simultaneous: [2] - expected sum speed: [0] - expected mean speed: [0] - expected early breaks: [1] -Ship Track Simultaneous Same Location Deferred Simultaneous: - window begin: 2000-01-01T00:00:00Z - window end: 2030-01-01T00:00:00Z - obs space: - name: Ship - simulated variables: [air_temperature] - generate: - list: - lats: [ 0, 0, 0] - lons: [ 0, 0, 0] - datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z', '2010-01-01T00:00:00Z'] - obs errors: [1.0] - station_ids: [ 1, 1, 1] - Ship Track Check: - testing mode: true + spatial resolution (km): 1 + max speed (m/s): 10 + rejection threshold: 1.0 early break check: false - deferred check simultaneous: true - variables: [air_temperature] - temporal resolution: PT10M - spatial resolution: 1 # km station_id_variable: name: station_id@MetaData expected distance: [0, 0, 0] @@ -304,16 +267,9 @@ Ship Track Simultaneous Same Location Deferred Simultaneous: expected short: [2] expected fast: [0] expected bends: [0] - expected dist0: [2] - expected simultaneous: [2] - expected deferred distance: [0] - expected deferred speed: [0] - expected deferred distance averaged: [0] - expected deferred speed averaged: [0] - expected deferred angle: [0] expected sum speed: [0] expected mean speed: [0] - expected early breaks: [0] + expected early breaks: [1] Ship Track Simultaneous Different Location: window begin: 2000-01-01T00:00:00Z window end: 2030-01-01T00:00:00Z @@ -328,65 +284,25 @@ Ship Track Simultaneous Different Location: obs errors: [1.0] station_ids: [ 1, 1, 1] Ship Track Check: - testing mode: true - variables: [air_temperature] + unit testing mode: true temporal resolution: PT10M - spatial resolution: 1 # km + spatial resolution (km): 1 + max speed (m/s): 10 + rejection threshold: 1.0 + early break check: false station_id_variable: name: station_id@MetaData expected distance: [0, 111, 111] - expected speed: [0.0, .185, .185] #because assumes a time difference of 10 mins btwn each obs + expected speed: [0.0, 185.0, 185.0] #because assumes a time difference of 10 mins btwn each obs expected distance averaged: [0, 222, 0] - expected speed averaged: [0, .370, 0] # because assumes a time difference of 10 mins btwn 1st/last obs + expected speed averaged: [0, 370.0, 0] # because assumes a time difference of 10 mins btwn 1st/last obs expected angle: [0.0, 0.0, 0.0] expected short: [2] expected fast: [0] expected bends: [0] - expected dist0: [0] - expected simultaneous: [3] expected sum speed: [0] expected mean speed: [0] expected early breaks: [1] -Ship Track Simultaneous Different Location Deferred Simultaneous: - window begin: 2000-01-01T00:00:00Z - window end: 2030-01-01T00:00:00Z - obs space: - name: Ship - simulated variables: [air_temperature] - generate: - list: - lats: [ 0, 1, 2, 3, 4, 5] - lons: [ 0, 0, 0, 0, 0, 0] - datetimes: ['2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', '2010-01-01T03:00:00Z'] - obs errors: [1.0] - station_ids: [ 1, 1, 1, 1, 1, 1] - Ship Track Check: - testing mode: true - early break check: false - deferred check simultaneous: true - variables: [air_temperature] - temporal resolution: PT10M - spatial resolution: 1 # km - station_id_variable: - name: station_id@MetaData - expected distance: [0, 111, 111, 111, 111, 111] - expected speed: [0.0, .0308, .185, .185, .0308, .0308] #because assumes a time difference of 10 mins btwn each obs - expected distance averaged: [0, 222, 222, 222, 222, 0] - expected speed averaged: [0, .0616, .370, .0616, .0308, 0] # because assumes a time difference of 10 mins btwn any simultaneous obs - expected angle: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - expected short: [2] - expected fast: [0] - expected bends: [0] - expected dist0: [0] - expected simultaneous: [3] - expected sum speed: [0.0924] - expected mean speed: [0.0308] - expected deferred distance: [0, 444, 111] - expected deferred speed: [0.0, .0616, .0308] - expected deferred distance averaged: [0, 555, 0] - expected deferred speed averaged: [0, .0513, 0] - expected deferred angle: [0.0, 0.0, 0.0] - expected early breaks: [0] Ship Tracks Different Station IDs: window begin: 2000-01-01T00:00:00Z window end: 2030-01-01T00:00:00Z @@ -403,25 +319,24 @@ Ship Tracks Different Station IDs: obs errors: [1.0] station_ids: [ 1, 1, 1, 1, 1, 2, 2, 2] Ship Track Check: - testing mode: true - variables: [air_temperature] + unit testing mode: true temporal resolution: PT10M - spatial resolution: 1 # km - max speed: 1 #km/s (default value) + spatial resolution (km): 1 + max speed (m/s): 1000 + rejection threshold: 1.0 + early break check: false station_id_variable: name: station_id@MetaData expected distance: [0, 111, 111, 111, 157, 0, 111, 111] - expected speed: [0.0, .0308, .0308, .0308, .0435, 0, .0617, .0617] + expected speed: [0.0, 30.8, 30.8, 30.8, 43.5, 0, 61.7, 61.7] expected distance averaged: [0, 157, 0, 248, 0, 0, 222, 0] - expected speed averaged: [0, .0218, 0, .0344, 0, 0, .0617, 0] + expected speed averaged: [0, 21.8, 0, 34.4, 0, 0, 61.7, 0] expected angle: [0.0, 90.0, 180.0, 45.0, 0.0, 0.0, 0.0, 0.0] expected short: [0, 2] expected fast: [0, 0] expected bends: [2, 0] - expected dist0: [0, 0] - expected simultaneous: [0, 0] - expected sum speed: [.135, 0.0] - expected mean speed: [.0339, 0.0] + expected sum speed: [135.0, 0.0] + expected mean speed: [33.9, 0.0] expected early breaks: [0, 1] Ship Tracks Different Station IDs Shuffled: window begin: 2000-01-01T00:00:00Z @@ -439,23 +354,72 @@ Ship Tracks Different Station IDs Shuffled: obs errors: [1.0] station_ids: [ 1, 1, 1, 1, 2, 2, 1, 2] Ship Track Check: - testing mode: true - variables: [air_temperature] + unit testing mode: true temporal resolution: PT10M - spatial resolution: 1 # km - max speed: 1 #km/s (default value) + spatial resolution (km): 1 + max speed (m/s): 1000 + rejection threshold: 1.0 + early break check: false station_id_variable: name: station_id@MetaData expected distance: [0, 111, 111, 111, 157, 0, 111, 111] - expected speed: [0.0, .0308, .0308, .0308, .0435, 0, .0617, .0617] + expected speed: [0.0, 30.8, 30.8, 30.8, 43.5, 0, 61.7, 61.7] expected distance averaged: [0, 157, 0, 248, 0, 0, 222, 0] - expected speed averaged: [0, .0218, 0, .0344, 0, 0, .0617, 0] + expected speed averaged: [0, 21.8, 0, 34.4, 0, 0, 61.7, 0] expected angle: [0.0, 90.0, 180.0, 45.0, 0.0, 0.0, 0.0, 0.0] expected short: [0, 2] expected fast: [0, 0] expected bends: [2, 0] - expected dist0: [0, 0] - expected simultaneous: [0, 0] - expected sum speed: [.135, 0.0] - expected mean speed: [.0339, 0.0] + expected sum speed: [135.0, 0.0] + expected mean speed: [33.9, 0.0] expected early breaks: [0, 1] +Ship Track Segment from Comparison Test early break false: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [ -37.00999832, -37.00999832, -37.02000046, -37.02000046, -37.02000046] + lons: [ 82.5, 82.51000214, 82.52999878, 82.55999756, 82.56999969] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T04:00:00Z', '2010-01-01T05:00:00Z'] + obs errors: [1.0] + station_ids: [ 1, 1, 1, 1, 1] + Ship Track Check: + comparison test: true + unit testing mode: false + temporal resolution: PT1S + spatial resolution (km): 0.0000001 + max speed (m/s): 0.01 + rejection threshold: 0.5 + early break check: false + station_id_variable: + name: station_id@MetaData + expected rejected obs indices: [0, 1, 2, 3, 4] +Ship Track Segment from Comparison Test early break true: + window begin: 2000-01-01T00:00:00Z + window end: 2030-01-01T00:00:00Z + obs space: + name: Ship + simulated variables: [air_temperature] + generate: + list: + lats: [ -4.94999981, -4.94999981, -4.94999981, -4.96000004, -4.96000004, -4.96000004] + lons: [ 94.98000336, 94.98000336, 94.98000336, 94.98000336, 94.98000336, 94.98000336 ] + datetimes: [ '2010-01-01T00:00:00Z', '2010-01-01T01:00:00Z', '2010-01-01T02:00:00Z', + '2010-01-01T03:00:00Z', '2010-01-01T04:00:00Z', '2010-01-01T05:00:00Z'] + obs errors: [1.0] + station_ids: [ 1, 1, 1, 1, 1, 1] + Ship Track Check: + comparison test: true + unit testing mode: false + temporal resolution: PT1S + spatial resolution (km): 0.0000001 + max speed (m/s): 0.01 + rejection threshold: 0.5 + early break check: false + station_id_variable: + name: station_id@MetaData + expected rejected obs indices: [0, 1, 2, 3, 4, 5] diff --git a/test/testinput/qc_variableassignment.yaml b/test/testinput/qc_variableassignment.yaml new file mode 100644 index 000000000..9e126fe0b --- /dev/null +++ b/test/testinput/qc_variableassignment.yaml @@ -0,0 +1,599 @@ +window begin: 2000-01-01T00:00:00Z +window end: 2030-01-01T00:00:00Z + +observations: +- obs space: &ObsSpace + name: Assign a fixed value to a pre-existing float variable, without a where clause + obsdatain: + obsfile: Data/ufo/testinput_tier_1/variable_assignment_testdata.nc + simulated variables: [air_temperature] + obs filters: + - filter: Variable Assignment + assignments: + - name: float_variable_1@MetaData + value: 75.5 + compareVariables: + - test: + name: float_variable_1@MetaData + reference: + name: assigned_float_variable_0@TestReference + +- obs space: + <<: *ObsSpace + name: Assign a fixed value to a pre-existing float variable + obs filters: + - filter: Variable Assignment + where: &Where + - variable: # select observations 1, 2 and 3 + name: float_variable_1@MetaData + minvalue: 4 + maxvalue: 8 + assignments: + - name: float_variable_1@MetaData + value: 75.5 + compareVariables: + - test: + name: float_variable_1@MetaData + reference: + name: assigned_float_variable_1@TestReference + +- obs space: + <<: *ObsSpace + name: Assign a fixed value to a new float variable + obs filters: + - filter: Variable Assignment + where: *Where + assignments: + - name: new_variable@MetaData + value: 75.5 + type: float + compareVariables: + - test: + name: new_variable@MetaData + reference: + name: assigned_float_variable_2@TestReference + +- obs space: + <<: *ObsSpace + name: Assign a fixed value to a pre-existing integer variable + obs filters: + - filter: Variable Assignment + where: *Where + assignments: + - name: int_variable_1@MetaData + value: 75 + compareVariables: + - test: + name: int_variable_1@MetaData + reference: + name: assigned_int_variable_1@TestReference + +- obs space: + <<: *ObsSpace + name: Assign a fixed value to a new integer variable + obs filters: + - filter: Variable Assignment + where: *Where + assignments: + - name: new_variable@MetaData + value: 75 + type: int + compareVariables: + - test: + name: new_variable@MetaData + reference: + name: assigned_int_variable_2@TestReference + +- obs space: + <<: *ObsSpace + name: Assign a fixed value to a pre-existing string variable + obs filters: + - filter: Variable Assignment + where: *Where + assignments: + - name: string_variable_1@MetaData + value: XYZ + compareVariables: + - test: + name: string_variable_1@MetaData + reference: + name: assigned_string_variable_1@TestReference + +- obs space: + <<: *ObsSpace + name: Assign a fixed value to a new string variable + obs filters: + - filter: Variable Assignment + where: *Where + assignments: + - name: new_variable@MetaData + value: XYZ + type: string + compareVariables: + - test: + name: new_variable@MetaData + reference: + name: assigned_string_variable_2@TestReference + +# We can't test datetime variables yet because there's no way to store reference data in the +# ObsSpace: the loader recognizes only a single variable of type DateTime: datetime@MetaData. +# +#- obs space: +# <<: *ObsSpace +# name: Assign a fixed value to a pre-existing datetime variable +# obs filters: +# - filter: Variable Assignment +# where: *Where +# assignments: +# - name: datetime@MetaData +# value: 2000-01-02T03:04:05Z +# compareVariables: +# - test: +# name: datetime@MetaData +# reference: +# name: assigned_datetime_variable_1@TestReference +# +#- obs space: +# <<: *ObsSpace +# name: Assign a fixed value to a new datetime variable +# obs filters: +# - filter: Variable Assignment +# where: *Where +# assignments: +# - name: new_variable@MetaData +# value: 2000-01-02T03:04:05Z +# type: string +# compareVariables: +# - test: +# name: new_variable@MetaData +# reference: +# name: assigned_datetime_variable_2@TestReference + +- obs space: + <<: *ObsSpace + name: Assign a fixed value to two variables, one new and the other pre-existing + obs filters: + - filter: Variable Assignment + where: *Where + assignments: + - name: new_variable_1@MetaData + value: 75.5 + type: float + - name: int_variable_1@MetaData + value: 75 + compareVariables: + - test: + name: new_variable_1@MetaData + reference: + name: assigned_float_variable_2@TestReference + - test: + name: int_variable_1@MetaData + reference: + name: assigned_int_variable_1@TestReference + +- obs space: + <<: *ObsSpace + name: Assign values produced by an ObsFunction to a pre-existing float variable + obs filters: + - filter: Variable Assignment + where: *Where + assignments: + - name: float_variable_1@MetaData + function: # will produce 0.5 * float_variable_1@MetaData + name: ObsErrorModelRamp@ObsFunction + options: + xvar: + name: float_variable_1@MetaData + x0: [0.5] + x1: [10] + err0: [0.25] + err1: [5] + compareVariables: + - test: + name: float_variable_1@MetaData + reference: + name: assigned_float_variable_3@TestReference + +- obs space: + <<: *ObsSpace + name: Assign values produced by an ObsFunction to a new float variable + obs filters: + - filter: Variable Assignment + where: *Where + assignments: + - name: new_variable@MetaData + type: float + function: # will produce 0.5 * float_variable_1@MetaData + name: ObsErrorModelRamp@ObsFunction + options: + xvar: + name: float_variable_1@MetaData + x0: [0.5] + x1: [10] + err0: [0.25] + err1: [5] + compareVariables: + - test: + name: new_variable@MetaData + reference: + name: assigned_float_variable_4@TestReference + +- obs space: + <<: *ObsSpace + name: Assign values produced by an ObsFunction to a pre-existing int variable + obs filters: + - filter: Variable Assignment + where: *Where + assignments: + - name: int_variable_1@MetaData + function: # will produce 0.5 * float_variable_1@MetaData (then truncated to int during assignment) + name: ObsErrorModelRamp@ObsFunction + options: + xvar: + name: float_variable_1@MetaData + x0: [0.5] + x1: [10] + err0: [0.25] + err1: [5] + compareVariables: + - test: + name: int_variable_1@MetaData + reference: + name: assigned_int_variable_3@TestReference + +- obs space: + <<: *ObsSpace + name: Assign values produced by an ObsFunction to a new int variable + obs filters: + - filter: Variable Assignment + where: *Where + assignments: + - name: new_variable@MetaData + type: int + function: # will produce 0.5 * float_variable_1@MetaData (then truncated to int during assignment) + name: ObsErrorModelRamp@ObsFunction + options: + xvar: + name: float_variable_1@MetaData + x0: [0.5] + x1: [10] + err0: [0.25] + err1: [5] + compareVariables: + - test: + name: new_variable@MetaData + reference: + name: assigned_int_variable_4@TestReference + +## Test Conditional ObsFunction "add on" - Example 1 +## Float variable assignment including multiple where in a case +- obs space: + <<: *ObsSpace + name: Assign values produced by the Condtional ObsFunction to a new float variable + obs filters: + - filter: Variable Assignment + assignments: + - name: conditional_assignment_variable_0@MetaData + type: float + function: + name: Conditional@ObsFunction + options: + defaultvalue: 9.0 + cases: + - where: + - variable: + name: float_variable_1@MetaData + minvalue: 4 + - variable: + name: float_variable_2@MetaData + minvalue: -11.75 + value: 75.5 + - where: + - variable: + name: int_variable_1@MetaData + is_in: 1 + value: 3.0 + compareVariables: + - test: + name: conditional_assignment_variable_0@MetaData + reference: + name: assigned_float_variable_1@TestReference + +## Test Conditional ObsFunction "add on" - Example 2 +## Cast to integer and no default value +- obs space: + <<: *ObsSpace + name: Assign values produced by the Condtional ObsFunction to a new int variable + obs filters: + - filter: Variable Assignment + assignments: + - name: conditional_assignment_variable_1@MetaData + type: int + function: + name: Conditional@ObsFunction + options: + cases: + - where: + - variable: + name: float_variable_1@MetaData + minvalue: 4.0 + maxvalue: 8.0 + value: 75 + # This next where clause shouldn't affect the output + # because firstmatchingcase = true by default + - where: + - variable: + name: int_variable_1@MetaData + is_in: 2 + value: 2 + compareVariables: + - test: + name: conditional_assignment_variable_1@MetaData + reference: + name: assigned_int_variable_2@TestReference + +## Tests on the obsspace with multiple channels + +- obs space: &MultichannelObsSpace + name: Assign a fixed value to a pre-existing multichannel float variable + obsdatain: + obsfile: Data/ufo/testinput_tier_1/multichannel_variable_assignment_testdata.nc + simulated variables: [air_temperature] + channels: 1, 2, 4 + obs filters: + - filter: Variable Assignment + where: &MultichannelWhere + - variable: # select observations 1, 2 and 3 + name: float_variable_1@MetaData + minvalue: 4 + maxvalue: 8 + assignments: + - name: float_variable@MetaData + channels: 1, 4 + value: 75.5 + compareVariables: + - test: + name: float_variable_1@MetaData + reference: + name: assigned_float_variable_1@TestReference + - test: + name: float_variable_2@MetaData # shouldn't be modified + reference: + name: float_variable_2@TestReference + - test: + name: float_variable_4@MetaData + reference: + name: assigned_float_variable_2@TestReference + +- obs space: + <<: *MultichannelObsSpace + name: Assign a fixed value to a new multichannel float variable + obs filters: + - filter: Variable Assignment + where: *MultichannelWhere + assignments: + - name: new_variable@MetaData + channels: 1, 4 + value: 75.5 + type: float + compareVariables: + - test: + name: new_variable_1@MetaData + reference: + name: assigned_float_variable_3@TestReference + - test: + name: new_variable_4@MetaData + reference: + name: assigned_float_variable_3@TestReference + +- obs space: + <<: *MultichannelObsSpace + name: Assign a fixed value to a pre-existing multichannel string variable + obs filters: + - filter: Variable Assignment + where: *MultichannelWhere + assignments: + - name: string_variable@MetaData + channels: 1, 4 + value: XYZ + compareVariables: + - test: + name: string_variable_1@MetaData + reference: + name: assigned_string_variable_1@TestReference + - test: + name: string_variable_4@MetaData + reference: + name: assigned_string_variable_2@TestReference + +- obs space: + <<: *MultichannelObsSpace + name: Assign a fixed value to a new multichannel string variable + obs filters: + - filter: Variable Assignment + where: *MultichannelWhere + assignments: + - name: new_variable@MetaData + channels: 1, 4 + value: XYZ + type: string + compareVariables: + - test: + name: new_variable_1@MetaData + reference: + name: assigned_string_variable_3@TestReference + - test: + name: new_variable_4@MetaData + reference: + name: assigned_string_variable_3@TestReference + +- obs space: + <<: *MultichannelObsSpace + name: Assign values produced by an ObsFunction to a pre-existing multichannel float variables + obs filters: + - filter: Variable Assignment + where: *MultichannelWhere + assignments: + - name: float_variable@MetaData + channels: 1, 4 + function: # will produce 0.5 * float_variable_1@MetaData and 0.25 * float_variable_1@MetaData + name: ObsErrorModelRamp@ObsFunction + options: + xvar: + name: float_variable_1@MetaData + channels: 1, 4 + x0: [0.5, 0.5] + x1: [10, 10] + err0: [0.25, 0.125] + err1: [5, 2.5] + compareVariables: + - test: + name: float_variable_1@MetaData + reference: + name: assigned_float_variable_4@TestReference + - test: + name: float_variable_2@MetaData # shouldn't be modified + reference: + name: float_variable_2@TestReference + - test: + name: float_variable_4@MetaData + reference: + name: assigned_float_variable_5@TestReference + +- obs space: + <<: *MultichannelObsSpace + name: Assign values produced by an ObsFunction to a new multichannel float variables + obs filters: + - filter: Variable Assignment + where: *MultichannelWhere + assignments: + - name: new_variable@MetaData + channels: 1, 4 + type: float + function: # will produce 0.5 * float_variable_1@MetaData and 0.25 * float_variable_1@MetaData + name: ObsErrorModelRamp@ObsFunction + options: + xvar: + name: float_variable_1@MetaData + channels: 1, 4 + x0: [0.5, 0.5] + x1: [10, 10] + err0: [0.25, 0.125] + err1: [5, 2.5] + compareVariables: + - test: + name: new_variable_1@MetaData + reference: + name: assigned_float_variable_6@TestReference + - test: + name: new_variable_4@MetaData + reference: + name: assigned_float_variable_7@TestReference + +## Test Conditional ObsFunction "add on" - Example 3 +## Create array for multiple channels +## test firstmatchingcase flag +- obs space: + <<: *MultichannelObsSpace + name: Assign values produced by the Condtional ObsFunction for multiple channels + obs filters: + - filter: Variable Assignment + assignments: + - name: conditional_assignment_new@MetaData + channels: 1, 4 + type: float + function: + name: Conditional@ObsFunction + options: + firstmatchingcase: false + defaultvalue: -10.0 + cases: + - where: + - variable: + name: float_variable_1@MetaData + minvalue: 4.0 + value: 75.5 + - where: + - variable: + name: float_variable_2@MetaData + maxvalue: 6.25 + value: -12.0 + compareVariables: + - test: + name: conditional_assignment_new_1@MetaData + reference: + name: assigned_float_variable_2@TestReference + - test: + name: conditional_assignment_new_4@MetaData + reference: + name: assigned_float_variable_2@TestReference + +- obs space: &ObsSpace + name: Try to assign a string to a float variable + obsdatain: + obsfile: Data/ufo/testinput_tier_1/variable_assignment_testdata.nc + simulated variables: [air_temperature] + obs filters: + - filter: Variable Assignment + assignments: + - name: float_variable_1@MetaData + value: ABCDEF + expectExceptionWithMessage: could not be converted to the required type + +- obs space: &ObsSpace + name: Try to assign values produced by an ObsFunction to a string variable + obsdatain: + obsfile: Data/ufo/testinput_tier_1/variable_assignment_testdata.nc + simulated variables: [air_temperature] + obs filters: + - filter: Variable Assignment + where: *Where + assignments: + - name: string_variable_1@MetaData + function: # will produce 0.5 * float_variable_1@MetaData + name: ObsErrorModelRamp@ObsFunction + options: + xvar: + name: float_variable_1@MetaData + x0: [0.5] + x1: [10] + err0: [0.25] + err1: [5] + expectExceptionWithMessage: ObsFunction values cannot be assigned to non-numeric variables + +- obs space: &ObsSpace + name: Try to assign values to an existing variable from the ObsValue group + obsdatain: + obsfile: Data/ufo/testinput_tier_1/variable_assignment_testdata.nc + simulated variables: [air_temperature] + obs filters: + - filter: Variable Assignment + assignments: + - name: air_temperature@ObsValue + value: 75.5 + expectExceptionWithMessage: Assignment to variables from the ObsValue group is not allowed + +- obs space: &ObsSpace + name: Try to assign values to a new variable from the ObsValue group + obsdatain: + obsfile: Data/ufo/testinput_tier_1/variable_assignment_testdata.nc + simulated variables: [air_temperature] + obs filters: + - filter: Variable Assignment + assignments: + - name: new_variable@ObsValue + value: 75.5 + type: float + expectExceptionWithMessage: Assignment to variables from the ObsValue group is not allowed + +- obs space: &ObsSpace + name: Try to assign values to a new variable without specifying its type + obsdatain: + obsfile: Data/ufo/testinput_tier_1/variable_assignment_testdata.nc + simulated variables: [air_temperature] + obs filters: + - filter: Variable Assignment + assignments: + - name: new_variable@MetaData + value: ABCDEF + expectExceptionWithMessage: You need to specify the type of the variable to be created diff --git a/test/testinput/qc_velocitycheck.yaml b/test/testinput/qc_velocitycheck.yaml index bd5dd9c12..2c03f21c2 100644 --- a/test/testinput/qc_velocitycheck.yaml +++ b/test/testinput/qc_velocitycheck.yaml @@ -5,7 +5,7 @@ observations: - obs space: name: windspeedTest obsdatain: - obsfile: Data/ioda/testinput_tier_1/satwind_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/satwind_obs_2018041500_m.nc4 simulated variables: [eastward_wind, northward_wind] obs filters: - filter: Bounds Check # test min/max value of a single test var, but filter 2 or more vars diff --git a/test/testinput/radialvelocity.yaml b/test/testinput/radialvelocity.yaml index 569a4328a..8b5cdd235 100644 --- a/test/testinput/radialvelocity.yaml +++ b/test/testinput/radialvelocity.yaml @@ -1,5 +1,5 @@ -window begin: 2019-05-22T21:55:00Z -window end: 2019-05-22T22:05:00Z +window begin: 2020-10-12T23:30:00Z +window end: 2020-10-13T00:30:00Z observations: - obs operator: @@ -7,10 +7,10 @@ observations: obs space: name: Radar obsdatain: - obsfile: Data/ioda/testinput_tier_1/radar_rw_obs_2019052222.nc4 + obsfile: Data/ufo/testinput_tier_1/radar_rw_obs_2020101300_m.nc4 simulated variables: [radial_velocity] geovals: - filename: Data/ufo/testinput_tier_1/radar_rw_geoval_2019052222.nc4 + filename: Data/ufo/testinput_tier_1/radar_rw_geoval_2020101300_m.nc4 vector ref: GsiHofX tolerance: 1.0e-05 linear obs operator test: diff --git a/test/testinput/radiosonde.yaml b/test/testinput/radiosonde.yaml index 220ad7a18..0cf905c91 100644 --- a/test/testinput/radiosonde.yaml +++ b/test/testinput/radiosonde.yaml @@ -12,7 +12,24 @@ observations: obs space: name: Radiosonde obsdatain: - obsfile: Data/ioda/testinput_tier_1/sondes_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_m.nc4 + simulated variables: [air_temperature] + geovals: + filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_m.nc4 + vector ref: GsiHofX + tolerance: 1.0e-06 +- obs operator: + name: VertInterp + vertical coordinate: air_pressure + observation vertical coordinate: air_pressure + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-13 + tolerance AD: 1.0e-11 + obs space: + name: Radiosonde with vertical coordinates listed explicitly + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_m.nc4 simulated variables: [air_temperature] geovals: filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_m.nc4 diff --git a/test/testinput/reference/GnssroBnd_gfs.yaml b/test/testinput/reference/GnssroBnd_gfs.yaml new file mode 100644 index 000000000..1b8e61bfb --- /dev/null +++ b/test/testinput/reference/GnssroBnd_gfs.yaml @@ -0,0 +1,52 @@ +window begin: 2020-06-30T21:00:00Z +window end: 2020-07-01T03:00:00Z + +observations: +- obs operator: + name: GnssroBndNBAM + obs options: + use_compress: 1 + sr_steps: 2 + vertlayer: full + super_ref_qc: NBAM + obs space: + name: GnssroBnd + obsdatain: + obsfile: ${obsdir}/gnssro_obs_2020070100_f.nc4 + obsgrouping: + group variables: [ "record_number" ] + sort variable: "impact_height" + sort order: "ascending" + obsdataout: + obsfile: ${outputdir}/gnssro_bndnbam_2020070100_f_output.nc4 + simulated variables: [bending_angle] + geovals: + filename: ${geovaldir}/gnssro_geovals_2020070100_f.nc4 + obs filters: + - filter: Domain Check + filter variables: + - name: [bending_angle] + where: + - variable: + name: impact_height@MetaData + minvalue: 0 + maxvalue: 50000 + - filter: Bounds Check + filter variables: + - name: [bending_angle] + where: + - variable: + name: occulting_sat_id@MetaData + is_in: 3-5 + test variables: + - name: impact_height@MetaData + minvalue: 8000 + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: NBAM + - filter: Background Check RONBAM + filter variables: + - name: [bending_angle] + threshold: 3 + passedBenchmark: 158651 diff --git a/test/testinput/reference/GnssroBnd_nrt.yaml b/test/testinput/reference/GnssroBnd_nrt.yaml new file mode 100644 index 000000000..86aff8a31 --- /dev/null +++ b/test/testinput/reference/GnssroBnd_nrt.yaml @@ -0,0 +1,131 @@ +window begin: '2020-10-01T03:00:00Z' +window length: PT6H +forecast length: PT6H +geometry: + nml_file_mpp: config/fv3files/fmsmpp.nml + trc_file: config/fv3files/field_table + akbk: config/fv3files/akbk64.nc4 + layout: [2, 2] + io_layout: [1,1] + npx: 769 + npy: 769 + npz: 64 + ntiles: 6 + fieldsets: + - fieldset: config/fieldsets/dynamics.yaml + - fieldset: config/fieldsets/ufo.yaml + interp_method: barycent +forecasts: + filetype: gfs + datapath: input/bg/window_center/ + filename_core: 20201001.060000.fv_core.res.nc + filename_trcr: 20201001.060000.fv_tracer.res.nc + filename_sfcd: 20201001.060000.sfc_data.nc + filename_sfcw: 20201001.060000.fv_srf_wnd.res.nc + filename_cplr: 20201001.060000.coupler.res + state variables: [u,v,ua,va,T,DELP,sphum,ice_wat,liq_wat,o3mr,phis, + slmsk,sheleg,tsea,vtype,stype,vfrac,stc,smc,snwdph, + u_srf,v_srf,f10m] +observations: +- obs space: + name: GnssroBndNBAM + obsdatain: + obsfile: input/obs/ioda_nomads_gnssro_PT6H_20201001_0300Z.nc4 + obsgrouping: + group variables: [ record_number ] + sort variable: impact_height + sort order: ascending + obsdataout: + obsfile: output/hofx/3dhofx_GnssroBndNBAM_2020100106.nc4 + simulated variables: [bending_angle] + obs operator: + name: GnssroBndNBAM + obs options: + sr_steps: 2 + vertlayer: full + use_compress: 1 + super_ref_qc: NBAM + obs filters: + - filter: Domain Check + filter variables: + - name: [bending_angle] + where: + - variable: + name: impact_height@MetaData + minvalue: 0 + maxvalue: 50000 + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: NRL + - filter: Background Check + filter variables: + - name: [bending_angle] + threshold: 3 +- obs space: + name: GnssroBndROPP1D + obsdatain: + obsfile: input/obs/ioda_nomads_gnssro_PT6H_20201001_0300Z.nc4 + obsgrouping: + group variables: [ record_number ] + sort variable: impact_height + sort order: ascending + obsdataout: + obsfile: output/hofx/3dhofx_GnssroBndROPP1D_2020100106.nc4 + simulated variables: [bending_angle] + obs operator: + name: GnssroBndROPP1D + obs options: + obs filters: + - filter: Domain Check + filter variables: + - name: [bending_angle] + where: + - variable: + name: impact_height@MetaData + minvalue: 0 + maxvalue: 50000 + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: NRL + - filter: Background Check + filter variables: + - name: [bending_angle] + threshold: 3 +- obs space: + name: GnssroBndROPP2D + obsdatain: + obsfile: input/obs/ioda_nomads_gnssro_PT6H_20201001_0300Z.nc4 + obsgrouping: + group variables: [ record_number ] + sort variable: impact_height + sort order: ascending + obsdataout: + obsfile: output/hofx/3dhofx_GnssroBndROPP2D_2020100106.nc4 + simulated variables: [bending_angle] + obs operator: + name: GnssroBndROPP2D + obs options: + n_horiz: 31 + res: 40.0 + top_2d: 10.0 + obs filters: + - filter: Domain Check + filter variables: + - name: [bending_angle] + where: + - variable: + name: impact_height@MetaData + minvalue: 0 + maxvalue: 50000 + - filter: ROobserror + filter variables: + - name: bending_angle + errmodel: NRL + - filter: Background Check + filter variables: + - name: [bending_angle] + threshold: 3 +Prints: + frequency: PT3H diff --git a/test/testinput/reference/README.md b/test/testinput/reference/README.md new file mode 100644 index 000000000..6c9fc12c0 --- /dev/null +++ b/test/testinput/reference/README.md @@ -0,0 +1,20 @@ +### License: + +(C) Copyright 2017-2020 UCAR + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +### Description: +This directory contains the yaml file for specific applications. The yaml files are tested and updated regularly. + + GnssroBnd_gfs.yaml: Yaml file for GFS GNSSRO applications. The UFO and its QC are set up to minic the GSI bending angle operator. + + GnssroBnd_nrt.yaml: yaml file for the near real time (NRT) observation monitoring. Include three operators: NBAM (GFS), ROPP1D and ROPP2D. + +### Use +Incoporate yaml file with appropriate input and output directory setup. + +### Change log + +11/24/2020: uploaded GnssroBnd_gfs.yaml and GnssroBnd_nrt.yaml, including the latest configuration aiming for gfs and nrt applications, respectively + diff --git a/test/testinput/reflectivity.yaml b/test/testinput/reflectivity.yaml index 2110d8907..6e04bfede 100644 --- a/test/testinput/reflectivity.yaml +++ b/test/testinput/reflectivity.yaml @@ -3,12 +3,13 @@ window end: 2019-05-22T22:05:00Z observations: - obs operator: - name: RadarReflectivity - VertCoord: geopotential_height + name: VertInterp + vertical coordinate: geopotential_height + observation vertical coordinate: height obs space: name: Radar obsdatain: - obsfile: Data/ioda/testinput_tier_1/radar_dbz_obs_2019052222.nc4 + obsfile: Data/ufo/testinput_tier_1/radar_dbz_obs_2019052222.nc4 simulated variables: [equivalent_reflectivity_factor] geovals: filename: Data/ufo/testinput_tier_1/radar_dbz_geoval_2019052222.nc4 diff --git a/test/testinput/satname.yaml b/test/testinput/satname.yaml new file mode 100644 index 000000000..e6e5ab8bd --- /dev/null +++ b/test/testinput/satname.yaml @@ -0,0 +1,145 @@ +window begin: 2000-01-01T00:00:00Z +window end: 2050-01-01T00:00:00Z + +observations: +- obs space: + name: test data + obsdatain: + obsfile: Data/ufo/testinput_tier_1/satwind_obs_1d_2020100106_noinv.nc4 + obsdataout: + obsfile: Data/satwind_obs__1d_2020100106_noinv_out.nc4 + simulated variables: [eastward_wind] + obs filters: + - filter: satname + filter variables: + - name: eastward_wind + SatName assignments: + - min WMO Satellite id: 1 + max WMO Satellite id: 999 + Satellite_comp: + - satobchannel: 1 + min frequency: 7.5e+13 + max frequency: 8.2e+13 + wind channel: ir38 + - satobchannel: 1 + min frequency: 2.6e+13 + max frequency: 2.9e+13 + wind channel: ir108 + - satobchannel: 1 + min frequency: 1.7e+14 + max frequency: 2.0e+14 + wind channel: ir16 + - satobchannel: 1 + min frequency: 3.6e+14 + max frequency: 4.8e+14 + wind channel: irvis + - satobchannel: 3 + min frequency: 4.3e+13 + max frequency: 4.7e+13 + wind channel: wv67 + - satobchannel: 5 + min frequency: 4.3e+13 + max frequency: 4.7e+13 + wind channel: cswv67 + - satobchannel: 3 + min frequency: 4.71e+13 + max frequency: 4.9e+13 + wind channel: wv62 + - satobchannel: 5 + min frequency: 4.71e+13 + max frequency: 4.9e+13 + wind channel: cswv62 + - satobchannel: 3 + min frequency: 3.9e+13 + max frequency: 4.2e+13 + wind channel: wv73 + - satobchannel: 5 + min frequency: 3.9e+13 + max frequency: 4.2e+13 + wind channel: cswv73 + - satobchannel: 2 + min frequency: 3.8e+14 + max frequency: 4.2e+14 + wind channel: hrvis + - satobchannel: 2 + min frequency: 4.5e+14 + max frequency: 4.8e+14 + wind channel: vis06 + - satobchannel: 2 + min frequency: 3.4e+14 + max frequency: 3.8e+14 + wind channel: vis08 + Satellite_id: + - Sat ID: 3 + Sat name: MetopB + - Sat ID: 4 + Sat name: MetopA + - Sat ID: 5 + Sat name: MetopC + - Sat ID: 54 + Sat name: Meteosat7 + - Sat ID: 55 + Sat name: Meteosat8 + - Sat ID: 56 + Sat name: Meteosat9 + - Sat ID: 57 + Sat name: Meteosat10 + - Sat ID: 70 + Sat name: Meteosat11 + - Sat ID: 173 + Sat name: Himawari8 + - Sat ID: 174 + Sat name: Himawari9 + - Sat ID: 205 + Sat name: NOAA14 + - Sat ID: 206 + Sat name: NOAA15 + - Sat ID: 207 + Sat name: NOAA16 + - Sat ID: 208 + Sat name: NOAA17 + - Sat ID: 209 + Sat name: NOAA18 + - Sat ID: 223 + Sat name: NOAA19 + - Sat ID: 224 + Sat name: SoumiNPP + - Sat ID: 225 + Sat name: NOAA20 + - Sat ID: 270 + Sat name: GOES16 + - Sat ID: 271 + Sat name: GOES17 + - Sat ID: 272 + Sat name: GOES18 + - Sat ID: 273 + Sat name: GOES19 + - Sat ID: 410 + Sat name: kalpana1 + - Sat ID: 470 + Sat name: INSAT3A + - Sat ID: 471 + Sat name: INSAT3D + - Sat ID: 472 + Sat name: INSAT3E + - Sat ID: 473 + Sat name: INSAT3DR + - Sat ID: 474 + Sat name: INSAT3DS + - Sat ID: 517 + Sat name: FY2G + - Sat ID: 518 + Sat name: FY2H + - Sat ID: 530 + Sat name: FY4A + - Sat ID: 531 + Sat name: FY4B + - Sat ID: 783 + Sat name: Terra + - Sat ID: 784 + Sat name: Aqua + - Sat ID: 852 + Sat name: MixedMetop + - Sat ID: 854 + Sat name: LeoGeo + passedBenchmark: 91051 diff --git a/test/testinput/sattcwv.yaml b/test/testinput/sattcwv.yaml new file mode 100644 index 000000000..640fab2ea --- /dev/null +++ b/test/testinput/sattcwv.yaml @@ -0,0 +1,21 @@ +window begin: 2020-12-30T03:00:00Z +window end: 2020-12-30T09:00:00Z + +observations: +- obs operator: + name: SatTCWV + obs options: + obs space: + name: SatTCWV + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sattcwv_obs_20201230T0600Z.nc4 + simulated variables: [precipitableWater] + geovals: + filename: Data/ufo/testinput_tier_1/sattcwv_geovals_20201230T0600Z.nc4 + norm ref: MetOfficeHofX + tolerance: 1.0e-5 + linear obs operator test: + coef TL: 1.0e-4 + iterations TL: 10 + tolerance TL: 1.0e-14 + tolerance AD: 1.0e-14 diff --git a/test/testinput/satwind.yaml b/test/testinput/satwind.yaml index 97a3da56c..179e22ba7 100644 --- a/test/testinput/satwind.yaml +++ b/test/testinput/satwind.yaml @@ -9,7 +9,7 @@ observations: obs space: name: Satwind obsdatain: - obsfile: Data/ioda/testinput_tier_1/satwind_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/satwind_obs_2018041500_m.nc4 obsdataout: obsfile: Data/satwind_obs_2018041500_m_out.nc4 simulated variables: [eastward_wind, northward_wind] diff --git a/test/testinput/satwind_metoffice.yaml b/test/testinput/satwind_metoffice.yaml new file mode 100644 index 000000000..3befa3028 --- /dev/null +++ b/test/testinput/satwind_metoffice.yaml @@ -0,0 +1,25 @@ +window begin: 2020-10-01T03:00:00Z +window end: 2020-10-01T09:00:00Z + +observations: +- obs operator: + name: VertInterp + vertical coordinate: air_pressure_levels + linear obs operator: + name: VertInterp + vertical coordinate: air_pressure_levels + obs space: + name: Satwind + obsdatain: + obsfile: Data/ufo/testinput_tier_1/satwind_obs_1d_2020100106_noinv.nc4 + obsdataout: + obsfile: Data/satwind_obs_1d_2020100106_noinv_linopr_test_out.nc4 + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/satwind_geoval_20201001T0600Z_noinv.nc4 + vector ref: MetOfficeHofX + tolerance: 1.0e-04 + linear obs operator test: + coef TL: 0.1 + tolerance TL: 1.0e-9 + tolerance AD: 1.0e-11 diff --git a/test/testinput/sbuv2_n19.yaml b/test/testinput/sbuv2_n19.yaml index a0ac68f3d..35131ea7c 100644 --- a/test/testinput/sbuv2_n19.yaml +++ b/test/testinput/sbuv2_n19.yaml @@ -10,7 +10,7 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/sbuv2_n19_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sbuv2_n19_obs_2018041500_m.nc4 obsdataout: obsfile: Data/sbuv2_n19_obs_2018041500_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/sbuv2_n19_L127.yaml b/test/testinput/sbuv2_n19_L127.yaml index 7b52d659f..4345de5d5 100644 --- a/test/testinput/sbuv2_n19_L127.yaml +++ b/test/testinput/sbuv2_n19_L127.yaml @@ -10,7 +10,7 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/sbuv2_n19_obs_2020051712_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sbuv2_n19_obs_2020051712_m.nc4 obsdataout: obsfile: Data/sbuv2_n19_obs_2020051712_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/sbuv2_n19_flipz.yaml b/test/testinput/sbuv2_n19_flipz.yaml index 486fdf547..2de21b9d1 100644 --- a/test/testinput/sbuv2_n19_flipz.yaml +++ b/test/testinput/sbuv2_n19_flipz.yaml @@ -10,7 +10,7 @@ observations: obs space: name: OzoneLayer obsdatain: - obsfile: Data/ioda/testinput_tier_1/sbuv2_n19_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sbuv2_n19_obs_2018041500_m.nc4 obsdataout: obsfile: Data/sbuv2_n19_obs_flipz_2018041500_m_out.nc4 simulated variables: [integrated_layer_ozone_in_air] diff --git a/test/testinput/scatwind_neutral_metoffice.yaml b/test/testinput/scatwind_neutral_metoffice.yaml new file mode 100644 index 000000000..95757a3d0 --- /dev/null +++ b/test/testinput/scatwind_neutral_metoffice.yaml @@ -0,0 +1,17 @@ +window begin: 2020-10-01T03:00:00Z +window end: 2020-10-01T09:00:00Z + +observations: +- obs operator: + name: ScatwindNeutralMetOffice + obs space: + name: Scatwind + obsdatain: + obsfile: Data/ufo/testinput_tier_1/scatwind_obs_1d_2020100106.nc4 + obsdataout: + obsfile: Data/scatwind_obs_1d_2020100106_opr_test_out.nc4 + simulated variables: [eastward_wind, northward_wind] + geovals: + filename: Data/ufo/testinput_tier_1/scatwind_geoval_20201001T0600Z.nc4 + vector ref: MetOfficeHofX + tolerance: 1.0e-05 diff --git a/test/testinput/sea_surface_temp.yaml b/test/testinput/sea_surface_temp.yaml index 879af3680..a2af7ca76 100644 --- a/test/testinput/sea_surface_temp.yaml +++ b/test/testinput/sea_surface_temp.yaml @@ -9,7 +9,7 @@ observations: obs space: name: SeaSurfaceTemp obsdatain: - obsfile: Data/ioda/testinput_tier_1/sst_obs-2018-04-15.nc4 + obsfile: Data/ufo/testinput_tier_1/sst_obs-2018-04-15.nc4 simulated variables: [sea_surface_temperature] geovals: filename: Data/ufo/testinput_tier_1/sst_obs-2018-04-15_geovals.nc diff --git a/test/testinput/seaicefrac.yaml b/test/testinput/seaicefrac.yaml index cc9b4f806..30839d74d 100644 --- a/test/testinput/seaicefrac.yaml +++ b/test/testinput/seaicefrac.yaml @@ -9,7 +9,7 @@ observations: obs space: name: SeaIceFraction obsdatain: - obsfile: Data/ioda/testinput_tier_1/icec-2018-04-15.nc + obsfile: Data/ufo/testinput_tier_1/icec-2018-04-15.nc simulated variables: [sea_ice_area_fraction] geovals: filename: Data/ufo/testinput_tier_1/icec-2018-04-15_geovals.nc diff --git a/test/testinput/seaicethick.yaml b/test/testinput/seaicethick.yaml index 9756cad3b..eeb8fdf82 100644 --- a/test/testinput/seaicethick.yaml +++ b/test/testinput/seaicethick.yaml @@ -5,7 +5,7 @@ observations: - obs space: name: SeaIceThickness obsdatain: - obsfile: Data/ioda/testinput_tier_1/cryosat2-2018-04-15.nc + obsfile: Data/ufo/testinput_tier_1/cryosat2-2018-04-15.nc simulated variables: [sea_ice_thickness] obs operator: name: SeaIceThickness @@ -22,7 +22,7 @@ observations: - obs space: name: SeaIceThickness obsdatain: - obsfile: Data/ioda/testinput_tier_1/cryosat2-2018-04-15.nc + obsfile: Data/ufo/testinput_tier_1/cryosat2-2018-04-15.nc simulated variables: [sea_ice_freeboard] obs operator: name: SeaIceThickness @@ -30,7 +30,7 @@ observations: name: SeaIceThickness geovals: filename: Data/ufo/testinput_tier_1/cryosat2-2018-04-15_geovals.nc - rms ref: 0.16062500062397761 + rms ref: 0.098124999692664222 tolerance: 1.0e-8 linear obs operator test: coef TL: 1.0e-4 diff --git a/test/testinput/seviri_crtm.yaml b/test/testinput/seviri_crtm.yaml index 3a8abfe79..ab554f683 100644 --- a/test/testinput/seviri_crtm.yaml +++ b/test/testinput/seviri_crtm.yaml @@ -5,6 +5,7 @@ observations: - obs operator: name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv obs options: Sensor_ID: seviri_m08 EndianType: little_endian @@ -12,7 +13,7 @@ observations: obs space: name: seviri_m08 obsdatain: - obsfile: Data/ioda/testinput_tier_1/seviri_m08_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/seviri_m08_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 5 geovals: diff --git a/test/testinput/sfcpcorrected.yaml b/test/testinput/sfcpcorrected.yaml index 23799459b..03e8754d1 100644 --- a/test/testinput/sfcpcorrected.yaml +++ b/test/testinput/sfcpcorrected.yaml @@ -8,7 +8,7 @@ observations: obs space: name: SfcPCorrected obsdatain: - obsfile: Data/ioda/testinput_tier_1/sfc_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_m.nc4 obsdataout: obsfile: Data/sfc_2018041500_m_UKMOS_out.nc4 simulated variables: [surface_pressure] @@ -26,7 +26,7 @@ observations: obs space: name: SfcPCorrected obsdatain: - obsfile: Data/ioda/testinput_tier_1/sfc_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_m.nc4 obsdataout: obsfile: Data/sfc_2018041500_m_WRFDA_out.nc4 simulated variables: [surface_pressure] @@ -38,3 +38,38 @@ observations: coef TL: 0.1 tolerance TL: 1.0e-13 tolerance AD: 1.0e-11 +# New additions to test nc-diag file output variables that are not height and surface_altitude, +# which can be overriden with new optional parameters geovar_geomz and geovar_sfc_geomz +observations: +- obs operator: + name: SfcPCorrected + da_psfc_scheme: UKMO + geovar_geomz: geopotential_height # height (default, delete option) + geovar_sfc_geomz: surface_geopotential_height # surface_altitude (default, delete option) + obs space: + name: SfcPCorrected + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_new.nc4 + obsdataout: + obsfile: Data/sfc_2018041500_m_UKMOS_out2.nc4 + simulated variables: [surface_pressure] + geovals: + filename: Data/ufo/testinput_tier_1/sfc_geoval_2018041500_new.nc4 + rms ref: 99603.92205093 + tolerance: 1.e-08 +- obs operator: + name: SfcPCorrected + da_psfc_scheme: WRFDA + geovar_geomz: geopotential_height + geovar_sfc_geomz: surface_geopotential_height + obs space: + name: SfcPCorrected + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_new.nc4 + obsdataout: + obsfile: Data/sfc_2018041500_m_WRFDA_out2.nc4 + simulated variables: [surface_pressure] + geovals: + filename: Data/ufo/testinput_tier_1/sfc_geoval_2018041500_new.nc4 + rms ref: 99491.19738072 + tolerance: 1.e-08 diff --git a/test/testinput/smap_crtm.yaml b/test/testinput/smap_crtm.yaml index d9b235994..2e64f79a6 100644 --- a/test/testinput/smap_crtm.yaml +++ b/test/testinput/smap_crtm.yaml @@ -8,6 +8,7 @@ observations: Clouds: [Water, Ice] Cloud_Fraction: 1.0 Salinity: on + SurfaceWindGeoVars: uv linear obs operator: Absorbers: [H2O,O3,CO2] Clouds: [Water, Ice] @@ -20,7 +21,7 @@ observations: obs space: name: radiometer_smap obsdatain: - obsfile: Data/ioda/testinput_tier_1/smap_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/smap_obs_2018041500_m.nc4 obsdataout: obsfile: Data/smap_obs_2018041500_m_out.nc4 simulated variables: [brightness_temperature] diff --git a/test/testinput/sndrd1-4_crtm.yaml b/test/testinput/sndrd1-4_crtm.yaml index a1ae27975..6894e6729 100644 --- a/test/testinput/sndrd1-4_crtm.yaml +++ b/test/testinput/sndrd1-4_crtm.yaml @@ -5,6 +5,7 @@ observations: - obs operator: &obsoper name: CRTM Absorbers: [H2O,O3,CO2] + SurfaceWindGeoVars: uv linear obs operator: Absorbers: [H2O,O3,CO2] obs options: &obsopts @@ -14,7 +15,7 @@ observations: obs space: name: sndrD1_g15 obsdatain: - obsfile: "Data/ioda/testinput_tier_1/sndrd1_g15_obs_2018041500_m.nc4" + obsfile: "Data/ufo/testinput_tier_1/sndrd1_g15_obs_2018041500_m.nc4" simulated variables: [brightness_temperature] channels: 1-15 geovals: @@ -33,7 +34,7 @@ observations: obs space: name: sndrD2_g15 obsdatain: - obsfile: Data/ioda/testinput_tier_1/sndrd2_g15_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sndrd2_g15_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 1-15 geovals: @@ -52,7 +53,7 @@ observations: obs space: name: sndrD3_g15 obsdatain: - obsfile: Data/ioda/testinput_tier_1/sndrd3_g15_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sndrd3_g15_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 1-15 geovals: @@ -71,7 +72,7 @@ observations: obs space: name: sndrD4_g15 obsdatain: - obsfile: Data/ioda/testinput_tier_1/sndrd4_g15_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/sndrd4_g15_obs_2018041500_m.nc4 simulated variables: [brightness_temperature] channels: 1-15 geovals: diff --git a/test/testinput/ssmis_f17_gfs_backgroundcheck_bc.yaml b/test/testinput/ssmis_f17_gfs_backgroundcheck_bc.yaml new file mode 100644 index 000000000..89374812a --- /dev/null +++ b/test/testinput/ssmis_f17_gfs_backgroundcheck_bc.yaml @@ -0,0 +1,71 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID ssmis_f17 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: ssmis_f17 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/ssmis_f17_hofx_2020110112.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-24 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_ssmis_f17.nc4 + variational bc: + predictors: + - name: constant + - name: cloud_liquid_water + options: + satellite: SSMIS + ch19h: 12 + ch19v: 13 + ch22v: 14 + ch37h: 15 + ch37v: 16 + ch91v: 17 + ch91h: 18 + - name: cosine_of_latitude_times_orbit_node + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &ssmis_f17_tlap Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_tlapmean.txt + - name: lapse_rate + options: + tlapse: *ssmis_f17_tlap + - name: emissivity + - name: scan_angle + options: + var_name: scan_position + order: 4 + - name: scan_angle + options: + var_name: scan_position + order: 3 + - name: scan_angle + options: + var_name: scan_position + order: 2 + - name: scan_angle + options: + var_name: scan_position + obs filters: +#step1: Gross check using abs(O-B-bias) + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *channels + absolute threshold: 3.5 + action: + name: reject + passedBenchmark: 1776 diff --git a/test/testinput/ssmis_f17_gfs_backgroundcheck_nbc.yaml b/test/testinput/ssmis_f17_gfs_backgroundcheck_nbc.yaml new file mode 100644 index 000000000..25e18a6ee --- /dev/null +++ b/test/testinput/ssmis_f17_gfs_backgroundcheck_nbc.yaml @@ -0,0 +1,72 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs operator: + name: CRTM + Absorbers: [H2O,O3,CO2] + obs options: + Sensor_ID: &Sensor_ID ssmis_f17 + EndianType: little_endian + CoefficientPath: Data/ + obs space: + name: ssmis_f17 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_obs_2020110112_m.nc4 + obsdataout: + obsfile: Data/ssmis_f17_hofx_2020110112.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-24 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_geoval_2020110112_m.nc4 + obs bias: + input file: Data/ufo/testinput_tier_1/instruments/radiance/satbias_ssmis_f17.nc4 + variational bc: + predictors: + - name: constant + - name: cloud_liquid_water + options: + satellite: SSMIS + ch19h: 12 + ch19v: 13 + ch22v: 14 + ch37h: 15 + ch37v: 16 + ch91v: 17 + ch91h: 18 + - name: cosine_of_latitude_times_orbit_node + - name: sine_of_latitude + - name: lapse_rate + options: + order: 2 + tlapse: &ssmis_f17_tlap Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_tlapmean.txt + - name: lapse_rate + options: + tlapse: *ssmis_f17_tlap + - name: emissivity + - name: scan_angle + options: + var_name: scan_position + order: 4 + - name: scan_angle + options: + var_name: scan_position + order: 3 + - name: scan_angle + options: + var_name: scan_position + order: 2 + - name: scan_angle + options: + var_name: scan_position + obs filters: +#step1: Gross check using abs(O-B) + - filter: Background Check + filter variables: + - name: brightness_temperature + channels: *channels + absolute threshold: 3.5 + bias correction parameter: 1.0 + action: + name: reject + passedBenchmark: 1555 diff --git a/test/testinput/thickness_predictor.yaml b/test/testinput/thickness_predictor.yaml new file mode 100644 index 000000000..521e0fd41 --- /dev/null +++ b/test/testinput/thickness_predictor.yaml @@ -0,0 +1,53 @@ +window begin: 2019-12-29T21:00:00Z +window end: 2019-12-30T03:00:00Z + +observations: +- obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_n20_obs_20191230T0000_rttov_predictors.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/geovals_atms_20191230T0000Z_predictors.nc4 + obs bias: + variational bc: + predictors: + - name: thickness + options: + layer top: 300000 + layer base: 850000 + mean: 7.6 + standard deviation: 0.4 + expectExceptionWithMessage: layer top is greater than largest model pressure level + +- obs space: + name: atms_n20 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/atms_n20_obs_20191230T0000_rttov_predictors.nc4 + simulated variables: [brightness_temperature] + channels: &channels 1-22 + geovals: + filename: Data/ufo/testinput_tier_1/geovals_atms_20191230T0000Z_predictors.nc4 + obs bias: + variational bc: + predictors: + - name: thickness + options: + layer top: 30000 + layer base: 85000 + mean: 7.6 + standard deviation: 0.4 + - name: thickness + options: + layer top: 5000 + layer base: 20000 + mean: 8.6 + standard deviation: 0.4 + - name: thickness + options: + layer top: 85000 + layer base: 100000 + mean: 1.2 + standard deviation: 0.4 + tolerance: 5.0e-5 diff --git a/test/testinput/timeoper.yaml b/test/testinput/timeoper.yaml index 09d060926..298e999fa 100644 --- a/test/testinput/timeoper.yaml +++ b/test/testinput/timeoper.yaml @@ -10,7 +10,7 @@ observations: obs space: name: Radiosonde obsdatain: - obsfile: Data/ioda/testinput_tier_1/sondes_obs_2018041500_vs.nc4 + obsfile: Data/ufo/testinput_tier_1/sondes_obs_2018041500_vs.nc4 simulated variables: [air_temperature] geovals: filename: Data/ufo/testinput_tier_1/sondes_geoval_2018041500_vs.nc4 diff --git a/test/testinput/tprof.yaml b/test/testinput/tprof.yaml index 755087958..fc48e612f 100644 --- a/test/testinput/tprof.yaml +++ b/test/testinput/tprof.yaml @@ -7,7 +7,7 @@ observations: obs space: name: InsituTemperature obsdatain: - obsfile: Data/ioda/testinput_tier_1/profile_2018-04-15.nc + obsfile: Data/ufo/testinput_tier_1/profile_2018-04-15.nc simulated variables: [sea_water_temperature] geovals: filename: Data/ufo/testinput_tier_1/profile_2018-04-15_geovals.nc diff --git a/test/testinput/tropomi_no2.yaml b/test/testinput/tropomi_no2.yaml index 177823e31..e124afabf 100644 --- a/test/testinput/tropomi_no2.yaml +++ b/test/testinput/tropomi_no2.yaml @@ -4,7 +4,7 @@ observations: - obs space: name: NO2 obsdatain: - obsfile: Data/ioda/testinput_tier_1/tropomi_no2_2020090318_m.nc4 + obsfile: Data/ufo/testinput_tier_1/tropomi_no2_2020090318_m.nc4 simulated variables: [nitrogen_dioxide_in_tropospheric_column] obs operator: name: AvgKernel @@ -29,7 +29,47 @@ observations: total column: false model units coeff: 1e-6 # ppmv to 1/1 hofx units coeff: 1.6605392e-20 # molec/cm2 to mol/m2 + linear obs operator test: + coef TL: 1.0e-5 + tolerance TL: 1.0e-12 + tolerance AD: 1.0e-14 geovals: filename: Data/ufo/testinput_tier_1/tropomi_no2_geoval_2020090318_m.nc4 - rms ref: 2.2154632172314298e-05 + rms ref: 2.1272229395313009e-05 + tolerance: 1.0e-7 +- obs space: + name: NO2 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/tropomi_no2_2020090318_m.nc4 + simulated variables: [nitrogen_dioxide_in_total_column] + obs operator: + name: AvgKernel + obs options: + nlayers_kernel: 34 + ak: [0, 22.83594, 424.4141, 1387.547, 3057.266, 5564.383, + 8163.375, 11901.34, 14898.45, 17471.84, 19290.23, + 20361.82, 20337.86, 19859.39, 19031.29, 18308.43, + 17008.79, 15508.26, 13881.33, 12766.87, 11116.66, + 9562.683, 8608.525, 7311.869, 6156.074, 4490.817, + 3381.744, 2265.432, 1423.77, 823.9678, 427.5925, + 191.3386, 69.52058, 18.60893, 0] + bk: [1, 0.991984, 0.969513, 0.931881, 0.873929, 0.790717, + 0.704669, 0.576692, 0.466003, 0.358254, 0.263242, + 0.16891, 0.111505, 0.077958, 0.051773, 0.038026, + 0.022355, 0.011806, 0.005378, 0.002857, 0.00089, + 0.000199, 5.9e-05, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 ] + AvgKernelVar: averaging_kernel_level + tracer variables: [no2] + tropospheric column: false + total column: true + model units coeff: 1e-6 # ppmv to 1/1 + hofx units coeff: 1.6605392e-20 # molec/cm2 to mol/m2 + linear obs operator test: + coef TL: 1.0e-5 + tolerance TL: 1.0e-12 + tolerance AD: 1.0e-14 + geovals: + filename: Data/ufo/testinput_tier_1/tropomi_no2_geoval_2020090318_m.nc4 + rms ref: 1.4128255400842815e-05 tolerance: 1.0e-7 diff --git a/test/testinput/unit_tests/function_clwret_ssmis.yaml b/test/testinput/unit_tests/function_clwret_ssmis.yaml new file mode 100755 index 000000000..4c12f471c --- /dev/null +++ b/test/testinput/unit_tests/function_clwret_ssmis.yaml @@ -0,0 +1,25 @@ +window begin: 2020-11-01T09:00:00Z +window end: 2020-11-01T15:00:00Z + +observations: +- obs space: + name: ssmis_f17 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_obs_2020110112_m.nc4 + simulated variables: [brightness_temperature] + channels: 1-24 + geovals: + filename: Data/ufo/testinput_tier_1/instruments/radiance/ssmis_f17_geoval_2020110112_m.nc4 + obs function: + name: CLWRetMW_SSMIS@ObsFunction + options: + ch19h: 12 + ch19v: 13 + ch22v: 14 + ch37h: 15 + ch37v: 16 + ch91v: 17 + ch91h: 18 + varGroup: ObsValue + variables: [clw] + tolerance: 1.0e-6 diff --git a/test/testinput/unit_tests/function_lam_domaincheck.yaml b/test/testinput/unit_tests/function_lam_domaincheck.yaml new file mode 100644 index 000000000..15d8dfda8 --- /dev/null +++ b/test/testinput/unit_tests/function_lam_domaincheck.yaml @@ -0,0 +1,36 @@ +window begin: 2018-04-14T20:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: regional_domain_check + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_tv_obs_2018041500_m.nc4 + obsdataout: + obsfile: Data/lam_domaincheck_sondes_tv_obs_2018041500_m.nc4 + simulated variables: [virtual_temperature] + obs filters: + # this obs function will compute, based off of input parameters, + # if an observation is inside of a specified limited area domain + # the result is then stored as a value 1 if yes, 0 no + # and can be filtered out by the bounds check. + - filter: Bounds Check + filter variables: + - name: virtual_temperature + test variables: + - name: LAMDomainCheck@ObsFunction + options: + map_projection: gnomonic_ed # only option currently supported + save: true # will save the DerivedValue to IODA output file + # the size of the FV3-LAM grid (mass, not staggered) + npx: 200 + npy: 110 + # below comes from the global attributes in the FV3-LAM grid netCDF file + plat: 38.5 + plon: -97.5 + a: 0.21423 + k: -0.23209 + dx: 0.1124152007 + dy: 0.1124152007 + minvalue: 1.0 # will filter all obs outside of the specified limited area domain + passedBenchmark: 42 # only a couple radiosonde profiles are within the domain diff --git a/test/testinput/unit_tests/function_lam_domaincheck_circle.yaml b/test/testinput/unit_tests/function_lam_domaincheck_circle.yaml new file mode 100644 index 000000000..2fd48157f --- /dev/null +++ b/test/testinput/unit_tests/function_lam_domaincheck_circle.yaml @@ -0,0 +1,29 @@ +window begin: 2018-04-14T20:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: regional_domain_check + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sondes_tv_obs_2018041500_m.nc4 + obsdataout: + obsfile: Data/lam_domaincheck_circle_sondes_tv_obs_2018041500_m.nc4 + simulated variables: [virtual_temperature] + obs filters: + # this obs function will compute, based off of input parameters, + # if an observation is inside of a specified circle area domain + # the result is then stored as a value 1 if yes, 0 no + # and can be filtered out by the bounds check. + - filter: Bounds Check + filter variables: + - name: virtual_temperature + test variables: + - name: LAMDomainCheck@ObsFunction + options: + map_projection: circle # an option currently supported + save: true # will save the DerivedValue to IODA output file + cenlat: 38.5 # central lat + cenlon: -97.5 # central lon + radius: 2000.0 # km + minvalue: 1.0 # will filter all obs outside of the specified limited area domain + passedBenchmark: 42 # only a couple radiosonde profiles are within the domain diff --git a/test/testinput/unit_tests/function_obserrorfactorquotient.yaml b/test/testinput/unit_tests/function_obserrorfactorquotient.yaml new file mode 100644 index 000000000..7e2c5800f --- /dev/null +++ b/test/testinput/unit_tests/function_obserrorfactorquotient.yaml @@ -0,0 +1,60 @@ +window begin: 2020-10-01T03:00:00Z +window end: 2020-10-01T09:00:00Z + +observations: +- obs space: + name: test_sfcp_inflation + obsdatain: + obsfile: Data/ufo/testinput_tier_1/2020100106_metars_small.nc + obsdataout: + obsfile: Data/2020100106_metars_small_out.nc + simulated variables: [surface_pressure, air_temperature] + geovals: + filename: Data/ufo/testinput_tier_1/sfcobs_geovals_gfsC48_2020100106_small.nc + obs operator: + name: SfcPCorrected + da_psfc_scheme: UKMO +# + obs filters: + - filter: BlackList + filter variables: + - name: surface_pressure + action: + name: assign error + error function: + name: ObsErrorModelStepwiseLinear@ObsFunction + options: + xvar: + name: surface_pressure@ObsValue + xvals: [85000, 80000] + errors: [120, 140] + defer to post: true +# + - filter: BlackList + filter variables: + - name: surface_pressure + action: + name: inflate error + inflation variable: + name: ObsErrorFactorSfcPressure@ObsFunction + options: + error_min: 100 # 1 mb + error_max: 300 # 3 mb + defer to post: true +# + - filter: Bounds Check + filter variables: + - name: surface_pressure + action: + name: reject + maxvalue: 2.5 + test variables: + - name: ObsErrorFactorQuotient@ObsFunction + options: + numerator: surface_pressure@ObsErrorData + denominator: surface_pressure@ObsError + save: true + defer to post: true + passedBenchmark: 62 +# Started with 36 obs x 2 variables, so 72 possible but 3 temperature +# values missing and quotient rejects 7 out of bounds. diff --git a/test/testinput/unit_tests/function_satwindsLNVDcheck.yaml b/test/testinput/unit_tests/function_satwindsLNVDcheck.yaml new file mode 100644 index 000000000..3f64bbc9e --- /dev/null +++ b/test/testinput/unit_tests/function_satwindsLNVDcheck.yaml @@ -0,0 +1,23 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: satwindsLNVDtest + obsdatain: + obsfile: Data/ufo/testinput_tier_1/satwind_obs_2018041500_m.nc4 + simulated variables: [eastward_wind, northward_wind] + obs filters: + - filter: Bounds Check # test min/max value of a single test var, but filter 2 or more vars + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: SatWindsLNVDCheck@ObsFunction + options: + test_hofx: GsiHofX + maxvalue: 3 + passedBenchmark: 122 +# In the test data set of 100 possible values, 1 ob is 2100UTC exactly and time test is (2100-0300]. +# There are 74 valid (non-missing) observed winds (times 2 wind components making 148 possible). +# There are 13 locations where the calculated LNVD is greater than 3 so 148-26=122. diff --git a/test/testinput/unit_tests/function_satwindsSPDBcheck.yaml b/test/testinput/unit_tests/function_satwindsSPDBcheck.yaml new file mode 100644 index 000000000..4906acc8e --- /dev/null +++ b/test/testinput/unit_tests/function_satwindsSPDBcheck.yaml @@ -0,0 +1,25 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: satwindsSPDBtest + obsdatain: + obsfile: Data/ufo/testinput_tier_1/satwind_obs_2018041500_m.nc4 + simulated variables: [eastward_wind, northward_wind] + obs filters: + - filter: Bounds Check # test min/max value of a single test var, but filter 2 or more vars + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: SatWindsSPDBCheck@ObsFunction + options: + error_min: 1.4 + error_max: 20.0 + test_hofx: GsiHofX + maxvalue: 1.75 + passedBenchmark: 144 +# In the test data set of 100 possible values, 1 ob is 2100UTC exactly since time test is (2100-0300]. +# There are 74 valid (non-missing) observed winds (times 2 wind components making 148 possible). +# There are 2 locations where the calculated threshold test is greater than max so 148-4=144. diff --git a/test/testinput/unit_tests/function_tropo_estimate.yaml b/test/testinput/unit_tests/function_tropo_estimate.yaml new file mode 100644 index 000000000..ab7f685b3 --- /dev/null +++ b/test/testinput/unit_tests/function_tropo_estimate.yaml @@ -0,0 +1,27 @@ +window begin: 2018-04-14T20:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: tropopause_estimate + obsdatain: + obsfile: Data/ufo/testinput_tier_1/satwind_obs_2018041500_m.nc4 + obsdataout: + obsfile: Data/tropo_obs_2018041500.nc4 + simulated variables: [eastward_wind, northward_wind] + obs filters: +# In real usage, a satwind obs that is more than 50 hPa above tropopause is rejected, but for +# testing purposes shown here, the difference check is only 5 hPa to reject 12 obs from sample. +# The options shown here do not work yet in Difference Check because it implements differently +# than Bounds Check and cannot yet accept options sub-config. This is UFO number 492. + - filter: Difference Check + filter variables: + - name: eastward_wind + - name: northward_wind + reference: TropopauseEstimate@ObsFunction +# options: +# - tropo_equator: 13000 # 130 hPa +# - tropo_pole: 37000 # 370 hPa + value: air_pressure@MetaData + minvalue: -500 # 5 hPa above tropopause level, negative p-diff + passedBenchmark: 132 diff --git a/test/testinput/unit_tests/function_wdirdiff.yaml b/test/testinput/unit_tests/function_wdirdiff.yaml new file mode 100644 index 000000000..dca5052fc --- /dev/null +++ b/test/testinput/unit_tests/function_wdirdiff.yaml @@ -0,0 +1,21 @@ +window begin: 2018-04-14T20:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: windDirAngleDiff + obsdatain: + obsfile: Data/ufo/testinput_tier_1/satwind_obs_2018041500_m.nc4 + simulated variables: [eastward_wind, northward_wind] + obs filters: +# If obs minus model difference of wind direction is more than 50 degrees, reject obs. + - filter: Bounds Check + filter variables: + - name: eastward_wind + - name: northward_wind + test variables: + - name: WindDirAngleDiff@ObsFunction + options: + test_hofx: GsiHofX + maxvalue: 50.0 + passedBenchmark: 136 # 25 obs missing, 7 rejected by filter, leaving 68x2. diff --git a/test/testinput/unit_tests/variabletransforms_SondePressureFromHeight.yaml b/test/testinput/unit_tests/variabletransforms_SondePressureFromHeight.yaml new file mode 100644 index 000000000..9ce3d7dd4 --- /dev/null +++ b/test/testinput/unit_tests/variabletransforms_SondePressureFromHeight.yaml @@ -0,0 +1,28 @@ +# +#=== Heigth to presure conversion for vertical profile ===# +# + +window begin: 2015-01-08T20:30:00Z +window end: 2015-01-09T03:30:00Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_conversion_height2pressure.nc4 + obsgrouping: + group variables: ["station_id"] + sort variable: "datetime" + sort order: "descending" + simulated variables: [air_temperature, relative_humidity, geopotential_height, dew_point_temperature] + obs filters: + - filter: Variable Transforms + Transform: ["PressureFromHeightForProfile"] + Method: UKMO + compareVariables: + - test: + name: air_pressure@DerivedValue + reference: + name: air_pressure_reference@ObsValue + relTol: 1.0e-5 + diff --git a/test/testinput/unit_tests/variabletransforms_SondePressureFromHeightICAO.yaml b/test/testinput/unit_tests/variabletransforms_SondePressureFromHeightICAO.yaml new file mode 100644 index 000000000..73bc1af48 --- /dev/null +++ b/test/testinput/unit_tests/variabletransforms_SondePressureFromHeightICAO.yaml @@ -0,0 +1,28 @@ +# +#=== Heigth to presure conversion for vertical profile ===# +# + +window begin: 2015-01-08T20:30:00Z +window end: 2015-01-09T03:30:00Z + +observations: +- obs space: + name: Radiosonde + obsdatain: + obsfile: Data/ufo/testinput_tier_1/met_office_conversion_height2pressure.nc4 + obsgrouping: + group variables: ["station_id"] + sort variable: "datetime" + sort order: "descending" + simulated variables: [air_temperature, relative_humidity, geopotential_height, dew_point_temperature] + obs filters: + - filter: Variable Transforms + Transform: ["PressureFromHeightForICAO"] + Method: UKMO + compareVariables: + - test: + name: air_pressure@DerivedValue + reference: + name: air_pressure_ICAO_reference@ObsValue + relTol: 1.0e-5 + diff --git a/test/testinput/unit_tests/variabletransforms_rhumidity.yaml b/test/testinput/unit_tests/variabletransforms_rhumidity.yaml new file mode 100644 index 000000000..9e0beb029 --- /dev/null +++ b/test/testinput/unit_tests/variabletransforms_rhumidity.yaml @@ -0,0 +1,93 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: test_relative_humidity1 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small.nc + simulated variables: [specific_humidity, air_temperature, surface_pressure] + obs filters: + - filter: Variable Transforms + Transform: ["RelativeHumidity"] # calculate relative humidity from specific humidity (also T, P) + # using Default method and default Formulation (Roger 1989) + compareVariables: # test output matches precalculated values + - reference: + name: relative_humidity_NOAA@TestReference + test: + name: relative_humidity@DerivedValue + absTol: 1.0e-5 + passedBenchmark: 99 # (33 sites with 3 vars listed in simulated variables) + +# Exercise the method using UKMO saturation vapor pressure method. +- obs space: + name: test_relative_humidity2 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small.nc + simulated variables: [specific_humidity, air_temperature, surface_pressure] + obs filters: + - filter: Variable Transforms + Transform: ["RelativeHumidity"] + Method: UKMO # Using UKMO method and UKMO default formulation + compareVariables: # test output matches precalculated values + - reference: + name: relative_humidity_UKMO@TestReference + test: + name: relative_humidity@DerivedValue + absTol: 1.0e-5 + passedBenchmark: 99 + +# Exercise the method using UKMO saturation vapor pressure method. +- obs space: + name: test_relative_humidity2 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small.nc + simulated variables: [specific_humidity, air_temperature, surface_pressure] + obs filters: + - filter: Variable Transforms + Transform: ["RelativeHumidity"] + Method: UKMO # Using UKMO method + Formulation: Sonntag # Using Sonntag formulation (whihc is the default formulation for UKMO method) + compareVariables: # test output matches precalculated values + - reference: + name: relative_humidity_UKMO@TestReference + test: + name: relative_humidity@DerivedValue + absTol: 1.0e-5 + passedBenchmark: 99 + +# Exercise the method using the Walko saturation vapor pressure method. +- obs space: + name: test_relative_humidity3 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small.nc + simulated variables: [specific_humidity, air_temperature, surface_pressure] + obs filters: + - filter: Variable Transforms + Transform: ["RelativeHumidity"] + Formulation: Walko + compareVariables: # test output matches precalculated values + - reference: + name: relative_humidity_Walko@TestReference + test: + name: relative_humidity@DerivedValue + absTol: 1.0e-5 + passedBenchmark: 99 + +# Exercise the method using the Murphy saturation vapor pressure method. +- obs space: + name: test_relative_humidity4 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small.nc + simulated variables: [specific_humidity, air_temperature, surface_pressure] + obs filters: + - filter: Variable Transforms + Transform: ["RelativeHumidity"] + Formulation: Murphy + compareVariables: # test output matches precalculated values + - reference: + name: relative_humidity_Murphy@TestReference + test: + name: relative_humidity@DerivedValue + absTol: 1.0e-5 + passedBenchmark: 99 diff --git a/test/testinput/unit_tests/variabletransforms_shumidity.yaml b/test/testinput/unit_tests/variabletransforms_shumidity.yaml new file mode 100644 index 000000000..bf8a631be --- /dev/null +++ b/test/testinput/unit_tests/variabletransforms_shumidity.yaml @@ -0,0 +1,89 @@ +window begin: 2018-04-14T21:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: test_specific_humidity1 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small2.nc + simulated variables: [relative_humidity, air_temperature, surface_pressure] + obs filters: + - filter: Variable Transforms + Transform: ["SpecificHumidity"] # calculate specific humidity from relative humidity + # using Default method and default Formulation (Roger 1989) + compareVariables: # test output matches precalculated values + - reference: + name: specific_humidity_NOAA@TestReference + test: + name: specific_humidity@DerivedValue + absTol: 1.0e-7 + passedBenchmark: 99 + +- obs space: + name: test_specific_humidity2 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small2.nc + simulated variables: [relative_humidity, air_temperature, surface_pressure] + obs filters: + - filter: Variable Transforms + Transform: ["SpecificHumidity"] # calculate specific humidity from relative humidity + Method: UKMO # Using UKMO method and UKMO default formulation + compareVariables: # test output matches precalculated values + - reference: + name: specific_humidity_UKMO@TestReference + test: + name: specific_humidity@DerivedValue + absTol: 1.0e-7 + passedBenchmark: 99 + +- obs space: + name: test_specific_humidity3 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small2.nc + simulated variables: [relative_humidity, air_temperature, surface_pressure] + obs filters: + - filter: Variable Transforms + Transform: ["SpecificHumidity"] # calculate specific humidity from relative humidity + Method: UKMO # Using UKMO method and UKMO default formulation + Formulation: Sonntag # Using Sonntag formulation (whihc is the default formulation for UKMO method) + compareVariables: # test output matches precalculated values + - reference: + name: specific_humidity_UKMO@TestReference + test: + name: specific_humidity@DerivedValue + absTol: 1.0e-7 + passedBenchmark: 99 + +- obs space: + name: test_specific_humidity4 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small2.nc + simulated variables: [relative_humidity, air_temperature, surface_pressure] + obs filters: + - filter: Variable Transforms + Transform: ["SpecificHumidity"] # calculate specific humidity from relative humidity + Formulation: Walko + compareVariables: # test output matches precalculated values + - reference: + name: specific_humidity_Walko@TestReference + test: + name: specific_humidity@DerivedValue + absTol: 1.0e-7 + passedBenchmark: 99 + +- obs space: + name: test_specific_humidity5 + obsdatain: + obsfile: Data/ufo/testinput_tier_1/sfc_obs_2018041500_metars_small2.nc + simulated variables: [relative_humidity, air_temperature, surface_pressure] + obs filters: + - filter: Variable Transforms + Transform: ["SpecificHumidity"] + Formulation: Murphy + compareVariables: # test output matches precalculated values + - reference: + name: specific_humidity_Murphy@TestReference + test: + name: specific_humidity@DerivedValue + absTol: 1.0e-7 + passedBenchmark: 99 diff --git a/test/testinput/unit_tests/variabletransforms_windcomponents.yaml b/test/testinput/unit_tests/variabletransforms_windcomponents.yaml new file mode 100644 index 000000000..80ebfc15a --- /dev/null +++ b/test/testinput/unit_tests/variabletransforms_windcomponents.yaml @@ -0,0 +1,27 @@ +window begin: 2018-04-14T20:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: Satwind + # Ioda file we want to apply the filter to + obsdatain: + obsfile: Data/ufo/testinput_tier_1/wind_unit_transforms_2018041500.nc4 + obsdataout: + obsfile: Data/windcomponents_2018041500_output.nc4 + simulated variables: [eastward_wind, northward_wind] + obs filters: + - filter: Variable Transforms + Transform: ["WindComponents"] + passedBenchmark: 148 + compareVariables: # test output matches precalculated values + - reference: + name: eastward_wind@TestReference + test: + name: eastward_wind@DerivedValue + absTol: 5.0e-5 + - reference: + name: northward_wind@TestReference + test: + name: northward_wind@DerivedValue + absTol: 5.0e-5 diff --git a/test/testinput/unit_tests/variabletransforms_windspeedanddirection.yaml b/test/testinput/unit_tests/variabletransforms_windspeedanddirection.yaml new file mode 100644 index 000000000..0c3a48a1c --- /dev/null +++ b/test/testinput/unit_tests/variabletransforms_windspeedanddirection.yaml @@ -0,0 +1,28 @@ +window begin: 2018-04-14T20:00:00Z +window end: 2018-04-15T03:00:00Z + +observations: +- obs space: + name: Satwind + obsdatain: + obsfile: Data/ufo/testinput_tier_1/wind_unit_transforms_2018041500.nc4 + obsdataout: + obsfile: Data/windspeedanddirection_2018041500_output.nc4 + simulated variables: [eastward_wind, northward_wind] + obs filters: + - filter: Variable Transforms + Transform: ["WindSpeedAndDirection"] + UseValidDataOnly: false + # how many data points will pass the filter? Should pass for all present obs + passedBenchmark: 148 + compareVariables: # test output matches precalculated values + - reference: + name: wind_speed@TestReference + test: + name: wind_speed@DerivedValue + absTol: 5.0e-6 + - reference: + name: wind_from_direction@TestReference + test: + name: wind_from_direction@DerivedValue + absTol: 5.0e-5 diff --git a/test/testinput/windprof.yaml b/test/testinput/windprof.yaml index 04666f1f4..5ddb4628b 100644 --- a/test/testinput/windprof.yaml +++ b/test/testinput/windprof.yaml @@ -12,7 +12,7 @@ observations: obs space: name: Radiosonde obsdatain: - obsfile: Data/ioda/testinput_tier_1/windprof_obs_2018041500_m.nc4 + obsfile: Data/ufo/testinput_tier_1/windprof_obs_2018041500_m.nc4 obsdataout: obsfile: Data/windprof_obs_2018041500_m_out.nc4 simulated variables: [eastward_wind, northward_wind] diff --git a/test/ufo/GaussianThinning.h b/test/ufo/GaussianThinning.h index 86ad14117..00cac6c36 100644 --- a/test/ufo/GaussianThinning.h +++ b/test/ufo/GaussianThinning.h @@ -48,6 +48,11 @@ void testGaussianThinning(const eckit::LocalConfiguration &conf) { obsspace.put_db("MetaData", "category", categories); } + if (conf.has("string_category")) { + const std::vector categories = conf.getStringVector("string_category"); + obsspace.put_db("MetaData", "string_category", categories); + } + if (conf.has("priority")) { const std::vector priorities = conf.getIntVector("priority"); obsspace.put_db("MetaData", "priority", priorities); @@ -58,8 +63,10 @@ void testGaussianThinning(const eckit::LocalConfiguration &conf) { std::shared_ptr> qcflags(new ioda::ObsDataVector( obsspace, obsspace.obsvariables())); - const eckit::LocalConfiguration filterConf(conf, "GaussianThinning"); - ufo::Gaussian_Thinning filter(obsspace, filterConf, qcflags, obserr); + eckit::LocalConfiguration filterConf(conf, "GaussianThinning"); + ufo::GaussianThinningParameters filterParameters; + filterParameters.validateAndDeserialize(filterConf); + ufo::Gaussian_Thinning filter(obsspace, filterParameters, qcflags, obserr); filter.preProcess(); const std::vector expectedThinnedObsIndices = diff --git a/test/ufo/GeoVaLs.h b/test/ufo/GeoVaLs.h index cdca73150..6a739c166 100644 --- a/test/ufo/GeoVaLs.h +++ b/test/ufo/GeoVaLs.h @@ -56,7 +56,7 @@ void testGeoVaLs() { /// Check that GeoVaLs default constructor works oops::Log::trace() << "GeoVaLs default constructor - does not allocate fields" << std::endl; - GeoVaLs gv_temp(ospace.comm()); + GeoVaLs gv_temp(ospace.distribution(), ingeovars); /// Check that GeoVaLs constructor to create a GeoVaLs with one location works if (gconf.has("one location check")) { @@ -73,7 +73,7 @@ void testGeoVaLs() { GeoVaLs gv_one(gval, ind[i]); std::vector gv_val(1); gv_one.get(gv_val, var, 1); - EXPECT(oops::is_close_absolute(gv_val[0], values[i], oneloctol, verbosity)); + EXPECT(oops::is_close_absolute(gv_val[0], values[i], oneloctol, 0, verbosity)); } } else { oops::Log::trace() << "Test just the constructor for a one location GeoVaLs" << std::endl; @@ -123,7 +123,8 @@ void testGeoVaLs() { oops::Log::debug()<< "gv dot product with itself after merge " << dp_gv_merged << std::endl; EXPECT(abs(dp_gv_merged - 2.0 * dp_gval)/dp_gv_merged < tol); - GeoVaLs gv1(ospace.comm()), gv2(ospace.comm()); + GeoVaLs gv1(ospace.distribution(), gv.getVars()); + GeoVaLs gv2(ospace.distribution(), gv.getVars()); gv.split(gv1, gv2); double dp_gv1_split = gv1.dot_product_with(gv1); @@ -163,12 +164,201 @@ void testGeoVaLs() { "GeoVaLs & operator *= (const std::vector); test succeeded" << std::endl; } } + +/// \brief Tests GeoVaLs::allocate, GeoVaLs::put, GeoVaLs::get, +/// GeoVaLs::putAtLocation and GeoVaLs::getAtLocation. +void testGeoVaLsAllocatePutGet() { + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); + const eckit::LocalConfiguration testconf(conf, "geovals get test"); + + /// Test 2D variables + const std::string var1 = "variable1"; + const std::string var2 = "variable2"; + const oops::Variables testvars({var1, var2}); + + /// Setup GeoVaLs that are not filled in or allocated; test that they are not allocated + const Locations locs(testconf, oops::mpi::world()); + GeoVaLs gval(locs, testvars); + oops::Log::test() << "GeoVals allocate test: created empty GeoVaLs with " << testvars << + " variables and nlocs=" << gval.nlocs() << std::endl; + EXPECT_EQUAL(gval.nlevs(var1), 0); + EXPECT_EQUAL(gval.nlevs(var2), 0); + + /// Allocate only the first variable, and test that it's allocated correctly + const oops::Variables testvar1({var1}); + const int nlevs1 = 10; + gval.allocate(nlevs1, testvar1); + oops::Log::test() << "Allocated " << var1 << "; nlevs(var1) = " << gval.nlevs(var1) << + "; nlevs(var2) = " << gval.nlevs(var2) << std::endl; + EXPECT_EQUAL(gval.nlevs(var1), nlevs1); + EXPECT_EQUAL(gval.nlevs(var2), 0); + + /// Allocate only the second variable (2D variable), test that it's allocated correctly + const oops::Variables testvar2({var2}); + const int nlevs2 = 1; + gval.allocate(nlevs2, testvar2); + oops::Log::test() << "Allocated " << var2 << "; nlevs(var1) = " << gval.nlevs(var1) << + "; nlevs(var2) = " << gval.nlevs(var2) << std::endl; + EXPECT_EQUAL(gval.nlevs(var1), nlevs1); + EXPECT_EQUAL(gval.nlevs(var2), nlevs2); + + /// Set all values for the first level of a 2D variable to an arbitrary number with the + /// put method. Then check that the get method returns the expected values in each case. + /// Do this for several data types. + /// (1) doubles + const double fillvalue_double = 3.01234567890123; + oops::Log::test() << "Put(double) fill value: " << fillvalue_double << std::endl; + const std::vector refvalues_double(gval.nlocs(), fillvalue_double); + gval.put(refvalues_double, var2, 1); + std::vector testvalues_double(gval.nlocs(), 0); + gval.get(testvalues_double, var2); + oops::Log::test() << "Get(double) result: " << testvalues_double << std::endl; + EXPECT_EQUAL(testvalues_double, refvalues_double); + /// (2) floats + const float fillvalue_float = 4.1f; + oops::Log::test() << "Put(float) fill value: " << fillvalue_float << std::endl; + const std::vector refvalues_float(gval.nlocs(), fillvalue_float); + gval.put(refvalues_float, var2, 1); + std::vector testvalues_float(gval.nlocs(), 0); + gval.get(testvalues_float, var2); + oops::Log::test() << "Get(float) result: " << testvalues_float << std::endl; + EXPECT_EQUAL(testvalues_float, refvalues_float); + /// (3) ints + const int fillvalue_int = 5; + oops::Log::test() << "Put(int) fill value: " << fillvalue_int << std::endl; + const std::vector refvalues_int(gval.nlocs(), fillvalue_int); + gval.put(refvalues_int, var2, 1); + std::vector testvalues_int(gval.nlocs(), 0); + gval.get(testvalues_int, var2); + oops::Log::test() << "Get(int) result: " << testvalues_int << std::endl; + EXPECT_EQUAL(testvalues_int, refvalues_int); + + /// Check that the getAtLocation method returns what we put in the GeoVaLs. + /// The reference GeoVaLs at indices (jlev, jloc) are equal to jlev + jloc. + for (size_t jlev = 0; jlev < nlevs1; ++jlev) { + std::vector refvalues_loc_double(gval.nlocs()); + std::iota(refvalues_loc_double.begin(), refvalues_loc_double.end(), jlev); + gval.put(refvalues_loc_double, var1, jlev+1); + } + for (size_t jloc = 0; jloc < gval.nlocs(); ++jloc) { + // Get the test vector at this location. + std::vector testvalues_loc_double(gval.nlevs(var1)); + gval.getAtLocation(testvalues_loc_double, var1, jloc); + // Recreate reference vector for this location. + std::vector refvalues_loc_double(gval.nlevs(var1)); + std::iota(refvalues_loc_double.begin(), refvalues_loc_double.end(), jloc); + // Compare the two vectors. + EXPECT_EQUAL(testvalues_loc_double, refvalues_loc_double); + // Repeat the test for floats. + std::vector testvalues_loc_float(gval.nlevs(var1)); + gval.getAtLocation(testvalues_loc_float, var1, jloc); + std::vector refvalues_loc_float(refvalues_loc_double.begin(), + refvalues_loc_double.end()); + EXPECT_EQUAL(testvalues_loc_float, refvalues_loc_float); + // Repeat the test for ints. + std::vector testvalues_loc_int(gval.nlevs(var1)); + gval.getAtLocation(testvalues_loc_int, var1, jloc); + std::vector refvalues_loc_int(refvalues_loc_double.begin(), + refvalues_loc_double.end()); + EXPECT_EQUAL(testvalues_loc_int, refvalues_loc_int); + } + + /// Check that the putAtLocation method correctly puts values in the GeoVaLs. + /// This is similar to the previous test but the putting and getting routines + /// are transposed. + /// This is done separately for each data type, using different fill values each time. + /// (1) doubles + /// The reference GeoVaLs at indices (jlev, jloc) are equal to jlev + jloc. + oops::Log::test() << "putAtLoction with doubles" << std::endl; + for (size_t jloc = 0; jloc < gval.nlocs(); ++jloc) { + std::vector refvalues_double(gval.nlevs(var1)); + std::iota(refvalues_double.begin(), refvalues_double.end(), jloc); + gval.putAtLocation(refvalues_double, var1, jloc); + } + oops::Log::test() << "testing putAtLoction with doubles" << std::endl; + for (size_t jlev = 0; jlev < gval.nlevs(var1); ++jlev) { + // Get the test vector on this level. + std::vector testvalues_double(gval.nlocs()); + gval.get(testvalues_double, var1, jlev + 1); + // Recreate reference vector for this level. + std::vector refvalues_double(gval.nlocs()); + std::iota(refvalues_double.begin(), refvalues_double.end(), jlev); + // Compare the two vectors. + EXPECT_EQUAL(testvalues_double, refvalues_double); + } + /// (2) floats + /// The reference GeoVaLs at indices (jlev, jloc) are equal to jlev + jloc + 1. + oops::Log::test() << "putAtLoction with floats" << std::endl; + for (size_t jloc = 0; jloc < gval.nlocs(); ++jloc) { + std::vector refvalues_float(gval.nlevs(var1)); + std::iota(refvalues_float.begin(), refvalues_float.end(), jloc + 1); + gval.putAtLocation(refvalues_float, var1, jloc); + } + oops::Log::test() << "testing putAtLoction with floats" << std::endl; + for (size_t jlev = 0; jlev < gval.nlevs(var1); ++jlev) { + // Get the test vector on this level. + std::vector testvalues_float(gval.nlocs()); + gval.get(testvalues_float, var1, jlev + 1); + // Recreate reference vector for this level. + std::vector refvalues_float(gval.nlocs()); + std::iota(refvalues_float.begin(), refvalues_float.end(), jlev + 1); + // Compare the two vectors. + EXPECT_EQUAL(testvalues_float, refvalues_float); + } + /// (3) ints + /// The reference GeoVaLs at indices (jlev, jloc) are equal to jlev + jloc + 2. + oops::Log::test() << "putAtLoction with ints" << std::endl; + for (size_t jloc = 0; jloc < gval.nlocs(); ++jloc) { + std::vector refvalues_int(gval.nlevs(var1)); + std::iota(refvalues_int.begin(), refvalues_int.end(), jloc + 2); + gval.putAtLocation(refvalues_int, var1, jloc); + } + oops::Log::test() << "testing putAtLoction with ints" << std::endl; + for (size_t jlev = 0; jlev < gval.nlevs(var1); ++jlev) { + // Get the test vector on this level. + // The get method has not been implemented for integers, so get a vector of floats + // and convert it. + std::vector testvalues_float(gval.nlocs()); + gval.get(testvalues_float, var1, jlev + 1); + std::vector testvalues_int(testvalues_float.begin(), testvalues_float.end()); + // Recreate reference vector for this level. + std::vector refvalues_int(gval.nlocs()); + std::iota(refvalues_int.begin(), refvalues_int.end(), jlev + 2); + // Compare the two vectors. + EXPECT_EQUAL(testvalues_int, refvalues_int); + } + + /// Check code paths that throw exceptions for the getAtLocation method. + std::vector testvalues_loc_wrongsize(gval.nlevs(var1) + 1, 0.0); + EXPECT_THROWS(gval.getAtLocation(testvalues_loc_wrongsize, var1, 1)); + std::vector testvalues_loc(gval.nlevs(var1), 0.0); + EXPECT_THROWS(gval.getAtLocation(testvalues_loc, var1, -1)); + EXPECT_THROWS(gval.getAtLocation(testvalues_loc, var1, gval.nlocs())); + + /// Check code paths that throw exceptions for the putAtLocation method. + EXPECT_THROWS(gval.putAtLocation(testvalues_loc_wrongsize, var1, 1)); + EXPECT_THROWS(gval.putAtLocation(testvalues_loc, var1, -1)); + EXPECT_THROWS(gval.putAtLocation(testvalues_loc, var1, gval.nlocs())); + + /// test 3D put and get + for (size_t jlev = 0; jlev < nlevs1; ++jlev) { + const float fillvalue = 3.0*(jlev+1); + const std::vector refvalues(gval.nlocs(), fillvalue); + gval.put(refvalues, var1, jlev+1); + oops::Log::test() << jlev << " level: put fill value: " << fillvalue << std::endl; + std::vector testvalues(gval.nlocs(), 0); + gval.get(testvalues, var1, jlev+1); + oops::Log::test() << jlev << " level: get result: " << testvalues << std::endl; + EXPECT_EQUAL(testvalues, refvalues); + } +} + // ----------------------------------------------------------------------------- class GeoVaLs : public oops::Test { public: - GeoVaLs() {} - virtual ~GeoVaLs() {} + GeoVaLs() = default; + virtual ~GeoVaLs() = default; private: std::string testid() const override {return "ufo::test::GeoVaLs";} @@ -177,6 +367,8 @@ class GeoVaLs : public oops::Test { ts.emplace_back(CASE("ufo/GeoVaLs/testGeoVaLs") { testGeoVaLs(); }); + ts.emplace_back(CASE("ufo/GeoVaLs/testGeoVaLsAllocatePutGet") + { testGeoVaLsAllocatePutGet(); }); } void clear() const override {} diff --git a/test/ufo/HistoryCheck.h b/test/ufo/HistoryCheck.h new file mode 100644 index 000000000..0b59ebf48 --- /dev/null +++ b/test/ufo/HistoryCheck.h @@ -0,0 +1,96 @@ +/* + * (C) 2021 Crown Copyright Met Office. All rights reserved. + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ +#ifndef TEST_UFO_HISTORYCHECK_H_ +#define TEST_UFO_HISTORYCHECK_H_ + +#include +#include +#include +#include + +#define ECKIT_TESTING_SELF_REGISTER_CASES 0 + +#include "eckit/config/LocalConfiguration.h" +#include "eckit/testing/Test.h" +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" +#include "oops/mpi/mpi.h" +#include "oops/runs/Test.h" +#include "oops/util/Expect.h" +#include "test/TestEnvironment.h" +#include "ufo/filters/HistoryCheck.h" +#include "ufo/filters/Variables.h" + +namespace ufo { +namespace test { +void testHistoryCheck(const eckit::LocalConfiguration &conf) { + util::DateTime bgn(conf.getString("window begin")); + util::DateTime end(conf.getString("window end")); + + const eckit::LocalConfiguration obsSpaceConf(conf, "obs space"); + ioda::ObsSpace obsspace(obsSpaceConf, oops::mpi::world(), bgn, end, oops::mpi::myself()); + + eckit::LocalConfiguration filterConf(conf, "History Check"); + ufo::HistoryCheckParameters filterParameters; + filterParameters.validateAndDeserialize(filterConf); + + if (conf.has("air_temperatures")) { + const std::vector airTemperatures = conf.getFloatVector("air_temperatures"); + obsspace.put_db("ObsValue", "air_temperature", airTemperatures); + } + + if (conf.has("station_ids")) { + const std::vector stationIds = conf.getIntVector("station_ids"); + obsspace.put_db("MetaData", "station_id", stationIds); + } + + if (conf.has("station_ids_string")) { + const std::vector stationIds = conf.getStringVector("station_ids_string"); + obsspace.put_db("MetaData", "station_id", stationIds); + } + + std::shared_ptr> obserr(new ioda::ObsDataVector( + obsspace, obsspace.obsvariables(), "ObsError")); + std::shared_ptr> qcflags(new ioda::ObsDataVector( + obsspace, obsspace.obsvariables())); + + ufo::HistoryCheck filter(obsspace, filterParameters, qcflags, obserr, conf); + filter.preProcess(); + + const std::vector expectedRejectedObsIndices = + conf.getUnsignedVector("expected rejected obs indices"); + std::vector rejectedObsIndices; + for (size_t i = 0; i < qcflags->nlocs(); ++i) + if ((*qcflags)[0][i] == ufo::QCflags::history) + rejectedObsIndices.push_back(i); + EXPECT_EQUAL(rejectedObsIndices, expectedRejectedObsIndices); +} + +class HistoryCheck : public oops::Test { + private: + std::string testid() const override {return "ufo::test::HistoryCheck";} + + void register_tests() const override { + std::vector& ts = eckit::testing::specification(); + + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); + for (const std::string &testCaseName : conf.keys()) + { + const eckit::LocalConfiguration testCaseConf(::test::TestEnvironment::config(), testCaseName); + ts.emplace_back(CASE("ufo/HistoryCheck/" + testCaseName, testCaseConf) + { + testHistoryCheck(testCaseConf); + }); + } + } + + void clear() const override {} +}; + +} // namespace test +} // namespace ufo + +#endif // TEST_UFO_HISTORYCHECK_H_ diff --git a/test/ufo/LinearObsBiasOperator.h b/test/ufo/LinearObsBiasOperator.h new file mode 100644 index 000000000..f3ccfd7fc --- /dev/null +++ b/test/ufo/LinearObsBiasOperator.h @@ -0,0 +1,114 @@ +/* + * (C) Crown copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef TEST_UFO_LINEAROBSBIASOPERATOR_H_ +#define TEST_UFO_LINEAROBSBIASOPERATOR_H_ + +#include +#include +#include +#include + +#include "eckit/config/LocalConfiguration.h" +#include "eckit/testing/Test.h" +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" +#include "oops/mpi/mpi.h" +#include "oops/runs/Test.h" +#include "test/interface/ObsTestsFixture.h" +#include "test/TestEnvironment.h" +#include "ufo/filters/Variables.h" +#include "ufo/GeoVaLs.h" +#include "ufo/LinearObsBiasOperator.h" +#include "ufo/ObsBias.h" +#include "ufo/ObsBiasIncrement.h" +#include "ufo/ObsDiagnostics.h" +#include "ufo/ObsTraits.h" + +namespace ufo { +namespace test { + +// ----------------------------------------------------------------------------- + +// Tests whether application of the linear obs bias operator to an obs bias increment obtained +// by subtracting two obs biases from each other produces the expected results. +CASE("ufo/LinearObsBiasOperator/testLinearObsBiasOperator") { + typedef ::test::ObsTestsFixture Test_; + + std::vector typeconfs; + ::test::TestEnvironment::config().get("observations", typeconfs); + + for (std::size_t jj = 0; jj < Test_::obspace().size(); ++jj) { + ioda::ObsSpace &odb = Test_::obspace()[jj].obsspace(); + const eckit::Configuration &conf = typeconfs[jj]; + + // initialize obs bias objects + eckit::LocalConfiguration biasConf = conf.getSubConfiguration("obs bias"); + eckit::LocalConfiguration targetBiasConf = conf.getSubConfiguration("target obs bias"); + ObsBiasParameters biasParams; + ObsBiasParameters targetBiasParams; + biasParams.validateAndDeserialize(biasConf); + targetBiasParams.validateAndDeserialize(targetBiasConf); + const ObsBias bias(odb, biasParams); + const ObsBias targetBias(odb, targetBiasParams); + + // read geovals from the file + const eckit::LocalConfiguration gconf(conf, "geovals"); + oops::Variables requiredVars = odb.obsvariables(); + requiredVars += bias.requiredVars(); + requiredVars += targetBias.requiredVars(); + const GeoVaLs geovals(gconf, odb, requiredVars); + + // set up obs diagnostics + oops::Variables requiredHdiagnostics; + requiredHdiagnostics += bias.requiredHdiagnostics(); + std::vector lons(odb.nlocs()); + std::vector lats(odb.nlocs()); + std::vector times(odb.nlocs()); + odb.get_db("MetaData", "latitude", lats); + odb.get_db("MetaData", "longitude", lons); + odb.get_db("MetaData", "datetime", times); + ObsDiagnostics ydiags(odb, Locations(lons, lats, times, odb.distribution()), + requiredHdiagnostics); + + // set TL trajectory to the geovals and the bias coeff. from the files + LinearObsBiasOperator biasOperator(odb); + biasOperator.setTrajectory(geovals, bias, ydiags); + + // set the bias increment to the difference between two obs biases initialised earlier + ObsBiasIncrement biasInc(odb, biasParams); + biasInc.diff(targetBias, bias); + + // apply the linear obs bias operator + ioda::ObsVector dy(odb); + biasOperator.computeObsBiasTL(geovals, biasInc, dy); + + // verify results + const double dy_rms = dy.rms(); + const double expected_dy_rms = conf.getDouble("rms ref"); + const double tol = conf.getDouble("relative tolerance"); + EXPECT(oops::is_close_relative(dy_rms, expected_dy_rms, tol)); + } +} + +// ----------------------------------------------------------------------------- + +class LinearObsBiasOperator : public oops::Test { + private: + std::string testid() const override {return "ufo::test::LinearObsBiasOperator";} + + void register_tests() const override { } + + void clear() const override {} +}; + +// ----------------------------------------------------------------------------- + +} // namespace test +} // namespace ufo + +#endif // TEST_UFO_LINEAROBSBIASOPERATOR_H_ diff --git a/test/ufo/Locations.h b/test/ufo/Locations.h index 32588032f..3f7655bfb 100644 --- a/test/ufo/Locations.h +++ b/test/ufo/Locations.h @@ -20,66 +20,178 @@ #include "oops/mpi/mpi.h" #include "oops/runs/Test.h" #include "oops/util/DateTime.h" -#include "oops/util/Duration.h" +#include "oops/util/FloatCompare.h" #include "oops/util/Logger.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" #include "test/TestEnvironment.h" #include "ufo/Locations.h" namespace ufo { namespace test { -// ----------------------------------------------------------------------------- -void testLocations() { - const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); +/// Parameters describing Locations/TimeMask test +class LocationsTimeTestParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(LocationsTimeTestParameters, Parameters) + public: + /// t1 & t2 for subsetting locations + oops::RequiredParameter t1{"t1", this}; + oops::RequiredParameter t2{"t2", this}; + /// reference mask + oops::RequiredParameter> refTimeMask{"reference mask", this}; +}; + +/// Parameters describing Locations test +class LocationsTestParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(LocationsTestParameters, Parameters) + public: + /// reference longitudes + oops::RequiredParameter> refLons{"reference lons", this}; + /// reference latitudes + oops::RequiredParameter> refLats{"reference lats", this}; + /// set of tests for time masks + oops::RequiredParameter> + timeMaskTests{"time mask tests", this}; +}; - // Setup ObsSpace - util::DateTime bgn(conf.getString("window begin")); - util::DateTime end(conf.getString("window end")); - const eckit::LocalConfiguration obsconf(conf, "obs space"); - ioda::ObsSpace odb(obsconf, oops::mpi::world(), bgn, end, oops::mpi::myself()); - const size_t nlocs = odb.nlocs(); +/// Code shared by all Locations tests +class LocationsTestFixture : private boost::noncopyable { + public: + static const ioda::ObsSpace & obsspace() {return *getInstance().obsspace_;} + static const eckit::LocalConfiguration & testconfig() {return getInstance().testconfig_;} + static const LocationsTestParameters & testparams() {return getInstance().testparams_;} + + private: + static LocationsTestFixture & getInstance() { + static LocationsTestFixture theLocationsTestFixture; + return theLocationsTestFixture; + } - // testConstructor:: Locations(): - Locations locs(oops::mpi::world()); - EXPECT(locs.nobs() == 0); - oops::Log::test() << "Locs(eckit mpi communicator): " << locs << std::endl; + LocationsTestFixture() { + const eckit::Configuration & conf = ::test::TestEnvironment::config(); + util::DateTime bgn(conf.getString("window begin")); + util::DateTime end(conf.getString("window end")); + const eckit::LocalConfiguration obsconf(conf, "obs space"); + obsspace_.reset(new ioda::ObsSpace(obsconf, oops::mpi::world(), bgn, end, oops::mpi::myself())); + testconfig_ = conf.getSubConfiguration("locations test"); + testparams_.validateAndDeserialize(testconfig_); + } + + std::shared_ptr obsspace_; + eckit::LocalConfiguration testconfig_; + LocationsTestParameters testparams_; +}; + +// ----------------------------------------------------------------------------- +/// Tests Locations constructors and method size() +void testLocations() { + typedef LocationsTestFixture Test_; + + const size_t nlocs = Test_::obsspace().nlocs(); // testConstructor:: Locations(const eckit::Configuration &) + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); Locations locs1(conf, oops::mpi::world()); - EXPECT(locs1.nobs() == nlocs); - oops::Log::test() << "Locs(eckit constructor, eckit mpi communicator): " << locs1 << std::endl; - - // testConstructor::Locations(const ioda::ObsSpace &, const util::DateTime &, - // const util::DateTime &); - Locations locs_t(odb, bgn, end); - EXPECT(locs_t.nobs() == nlocs); - oops::Log::test() << "Locs(odb,t1,t2) constructor): " << locs_t << std::endl; - - // test operator+=(const Locations & other) - util::Duration twin = end - bgn; - util::DateTime stateTime = bgn + twin/2; - - Locations locs_b(odb, stateTime, end); - const size_t nlocs_b = locs_b.nobs(); - oops::Log::test() << "Locs(odb,t1+(t2-t1)/2,t2) constructor): " << locs_b << std::endl; - - { - Locations locs_a(odb, bgn, stateTime); - const size_t nlocs_a = locs_a.nobs(); - oops::Log::test() << "Locs(odb,t1,t1+(t2-t1)/2) constructor): " << locs_a << std::endl; - - EXPECT(locs_t.nobs() == nlocs_a + nlocs_b); - - locs_a += locs_b; - EXPECT(locs_t.nobs() == locs_a.nobs()); - oops::Log::test() << "Locs(odb,t1,t1+(t2-t1)/2) + " - << "Locs(odb,t1+(t2-t1)/2,t2) concatenated: " << locs_a << std::endl; + EXPECT(locs1.size() == nlocs); + oops::Log::test() << "Locations(configuration, eckit mpi communicator): " << locs1 << std::endl; + + // testConstructor::Locations(const ioda::ObsSpace &); +/* Locations locs2(Test_::obsspace()); + EXPECT(locs2.size() == nlocs); + oops::Log::test() << "Locations(obsspace) constructor: " << locs2 << std::endl;*/ +} + +// ----------------------------------------------------------------------------- +/// Tests Locations accessors lons() and lats() +void testLonsLats() { + typedef LocationsTestFixture Test_; + + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); + Locations locs(conf, oops::mpi::world()); + + const LocationsTestParameters & params = Test_::testparams(); + const float abstol = 1.0e-8; + EXPECT(oops::are_all_close_absolute(params.refLons.value(), locs.lons(), abstol)); + EXPECT(oops::are_all_close_absolute(params.refLats.value(), locs.lats(), abstol)); +} + +// ----------------------------------------------------------------------------- +extern "C" { + /// similar to LonsLats test, on Fortran level + /// Returns 1 if the test passes, 0 if the test fails + int test_locations_lonslats_f90(const Locations &, const eckit::Configuration &); +} + +/// Tests Locations accessors lons() and lats() from Fortran +void testFortranLonsLats() { + typedef LocationsTestFixture Test_; + + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); + Locations locs(conf, oops::mpi::world()); + + EXPECT(test_locations_lonslats_f90(locs, Test_::testconfig())); +} + +// ----------------------------------------------------------------------------- +/// Tests Locations::isInTimeWindow method +void testTimeMask() { + typedef LocationsTestFixture Test_; + + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); + Locations locs(conf, oops::mpi::world()); + + const LocationsTestParameters & params = Test_::testparams(); + for (const auto & test : params.timeMaskTests.value()) { + EXPECT_EQUAL(test.refTimeMask.value(), locs.isInTimeWindow(test.t1, test.t2)); } - Locations locs_a(odb, bgn, stateTime); - locs_b += locs_a; - EXPECT(locs_t.nobs() == locs_b.nobs()); - oops::Log::test() << "Locs(odb,t1+(t2-t1)/2,t2) + " - << "Locs(odb,t1,t1+(t2-t1)/2) concatenated: " << locs_b << std::endl; +} + +// ----------------------------------------------------------------------------- +extern "C" { + /// similar to TimeMask test, on Fortran level + /// Returns 1 if the test passes, 0 if the test fails + int test_locations_timemask_f90(const Locations &, const eckit::Configuration &); +} + +/// Tests Locations::isInTimeWindow method from Fortran +void testFortranTimeMask() { + typedef LocationsTestFixture Test_; + + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); + Locations locs(conf, oops::mpi::world()); + + EXPECT(test_locations_timemask_f90(locs, Test_::testconfig())); +} + +// ----------------------------------------------------------------------------- +/// Tests operator+= (concatenation of two Locations) +void testConcatenate() { + typedef LocationsTestFixture Test_; + + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); + Locations locs1(conf, oops::mpi::world()); + Locations locs2(conf, oops::mpi::world()); + const size_t nlocs = locs1.size(); + + locs1 += locs2; + + EXPECT_EQUAL(locs1.size(), 2*nlocs); + + const float abstol = 1.0e-8; + + /// test that concatenated longitudes have two copies of original lons + const std::vector & lons = locs1.lons(); + std::vector lons1(lons.begin(), lons.begin() + nlocs); + std::vector lons2(lons.begin() + nlocs, lons.end()); + EXPECT(oops::are_all_close_absolute(lons1, lons2, abstol)); + EXPECT(oops::are_all_close_absolute(lons1, locs2.lons(), abstol)); + + /// test that concatenated latitudes have two copies of original lats + const std::vector & lats = locs1.lats(); + std::vector lats1(lats.begin(), lats.begin() + nlocs); + std::vector lats2(lats.begin() + nlocs, lats.end()); + EXPECT(oops::are_all_close_absolute(lats1, lats2, abstol)); + EXPECT(oops::are_all_close_absolute(lats1, locs2.lats(), abstol)); } // ----------------------------------------------------------------------------- @@ -87,7 +199,8 @@ void testLocations() { class Locations : public oops::Test { public: Locations() {} - virtual ~Locations() {} + virtual ~Locations() = default; + private: std::string testid() const override {return "ufo::test::Locations";} @@ -96,6 +209,16 @@ class Locations : public oops::Test { ts.emplace_back(CASE("ufo/Locations/testLocations") { testLocations(); }); + ts.emplace_back(CASE("ufo/Locations/testLonsLats") + { testLonsLats(); }); + ts.emplace_back(CASE("ufo/Locations/testFortranLonsLats") + { testFortranLonsLats(); }); + ts.emplace_back(CASE("ufo/Locations/testTimeMask") + { testTimeMask(); }); + ts.emplace_back(CASE("ufo/Locations/testFortranTimeMask") + { testFortranTimeMask(); }); + ts.emplace_back(CASE("ufo/Locations/testConcatenation") + { testConcatenate(); }); } void clear() const override {} diff --git a/test/ufo/MetOfficeBuddyCheck.h b/test/ufo/MetOfficeBuddyCheck.h index 9ff45bf3e..cb0dcafc4 100644 --- a/test/ufo/MetOfficeBuddyCheck.h +++ b/test/ufo/MetOfficeBuddyCheck.h @@ -60,13 +60,26 @@ void testMetOfficeBuddyCheck(const eckit::LocalConfiguration &conf) { std::shared_ptr> qcflags(new ioda::ObsDataVector( obsSpace, obsSpace.obsvariables())); - const eckit::LocalConfiguration filterConf(conf, "Met Office Buddy Check"); - ufo::MetOfficeBuddyCheck filter(obsSpace, filterConf, qcflags, obserr); + eckit::LocalConfiguration filterConf(conf, "Met Office Buddy Check"); + ufo::MetOfficeBuddyCheckParameters filterParameters; + filterParameters.validateAndDeserialize(filterConf); + ufo::MetOfficeBuddyCheck filter(obsSpace, filterParameters, qcflags, obserr); filter.preProcess(); ioda::ObsVector hofx(obsSpace, "HofX"); - ufo::Locations locations(obsSpace, bgn, end); - ufo::ObsDiagnostics obsDiags(obsSpace, locations, oops::Variables()); + ufo::Locations locations(conf, obsSpace.comm()); + + const eckit::LocalConfiguration diagConf = conf.getSubConfiguration("obs diagnostics"); + oops::Variables diagVars; + for (const std::string &name : diagConf.keys()) + diagVars.push_back(name); + ufo::ObsDiagnostics obsDiags(obsSpace, locations, diagVars); + obsDiags.allocate(1, diagVars); + for (const std::string &name : diagConf.keys()) { + const std::vector diag = diagConf.getDoubleVector(name); + obsDiags.save(diag, name, 1); + } + filter.postFilter(hofx, obsDiags); const eckit::LocalConfiguration pgeConf(conf, "ExpectedGrossErrorProbabilities"); diff --git a/test/ufo/MetOfficeRadianceErrorMatrices.h b/test/ufo/MetOfficeRadianceErrorMatrices.h new file mode 100644 index 000000000..80796e1c2 --- /dev/null +++ b/test/ufo/MetOfficeRadianceErrorMatrices.h @@ -0,0 +1,114 @@ +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef TEST_UFO_METOFFICERADIANCEERRORMATRICES_H_ +#define TEST_UFO_METOFFICERADIANCEERRORMATRICES_H_ + +#include +#include +#include +#include +#include + +#define ECKIT_TESTING_SELF_REGISTER_CASES 0 + +#include "eckit/config/LocalConfiguration.h" +#include "eckit/testing/Test.h" +#include "oops/runs/Test.h" +#include "oops/util/Expect.h" +#include "oops/util/IntSetParser.h" +#include "oops/util/Logger.h" +#include "test/TestEnvironment.h" +#include "ufo/utils/metoffice/MetOfficeBMatrixStatic.h" +#include "ufo/utils/metoffice/MetOfficeRMatrixRadiance.h" + +namespace ufo { +namespace test { + +void testMetOfficeRadianceErrorMatrices(const eckit::LocalConfiguration &conf) { + // Get test values from configuration + std::string chlist = conf.getString("channels"); + const float latitude = conf.getFloat("latitude"); + const size_t nelements = conf.getUnsigned("nelements"); + const float BHT_value = conf.getFloat("BHT_value"); + const float HBHT_value = conf.getFloat("HBHT_value"); + const float HBHT_R_value = conf.getFloat("HBHT_R_value"); + const float tol = conf.getFloat("tol"); + + // Setup local variables + std::vector channels; + std::set chans = oops::parseIntSet(chlist); + std::copy(chans.begin(), chans.end(), std::back_inserter(channels)); + const size_t nchans = channels.size(); + + // ----------------- + // Bmatrix testing + // ----------------- + + // Construct object + MetOfficeBMatrixStatic bmatrix(conf); + + // Get an eigen matrix for a specific latitude + Eigen::MatrixXf Hmatrix = Eigen::MatrixXf::Constant(nchans, nelements, 2.0); + Eigen::MatrixXf BHT; + bmatrix.multiply(latitude, Hmatrix.transpose(), BHT); + + // Test print function + oops::Log::info() << "bmatrix = " << bmatrix << std::endl; + + // ----------------- + // Rmatrix testing + // ----------------- + + // Construct object + MetOfficeRMatrixRadiance rmatrix(conf); + + // Create a matrix to add to + Eigen::MatrixXf HBHT; + Eigen::MatrixXf HBHT_R; + HBHT = Hmatrix * BHT; + // HBH' + R + rmatrix.add(channels, HBHT, HBHT_R); + + // Test print function + oops::Log::info() << "rmatrix = " << rmatrix << std::endl; + + // ----------------------------- + // Check values during testing + // ---------------------------- + + // Test to check the multiplication appears to be correct + ASSERT(std::abs(BHT(0, 0) - BHT_value) < tol); + ASSERT(std::abs(HBHT(0, 0) - HBHT_value) < tol); + ASSERT(std::abs(HBHT_R(0, 0) - HBHT_R_value) < tol); +} + +class MetOfficeRadianceErrorMatrices : public oops::Test { + private: + std::string testid() const override {return "ufo::test::MetOfficeRadianceErrorMatrices";} + + void register_tests() const override { + std::vector& ts = eckit::testing::specification(); + + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); + for (const std::string & testCaseName : conf.keys()) + { + const eckit::LocalConfiguration testCaseConf(::test::TestEnvironment::config(), testCaseName); + ts.emplace_back(CASE("ufo/MetOfficeRadianceErrorMatrices/" + testCaseName, testCaseConf) + { + testMetOfficeRadianceErrorMatrices(testCaseConf); + }); + } + } + + void clear() const override {} +}; + +} // namespace test +} // namespace ufo + +#endif // TEST_UFO_METOFFICERADIANCEERRORMATRICES_H_ diff --git a/test/ufo/ObsBiasCovarianceDetails.h b/test/ufo/ObsBiasCovarianceDetails.h index 2fca979be..576ab6320 100644 --- a/test/ufo/ObsBiasCovarianceDetails.h +++ b/test/ufo/ObsBiasCovarianceDetails.h @@ -44,28 +44,32 @@ void testObsBiasCovarianceDetails() { bgn, end, oops::mpi::myself()); // Setup ObsBias - ObsBias ybias(odb, oconf); + eckit::LocalConfiguration biasconf = oconf.getSubConfiguration("obs bias"); + ObsBiasParameters biasparams; + biasparams.validateAndDeserialize(biasconf); + ObsBias ybias(odb, biasparams); // Setup ObsBiasIncrements - ObsBiasIncrement ybias_inc(odb, oconf); + eckit::LocalConfiguration biaserrconf = biasconf.getSubConfiguration("covariance"); + ObsBiasIncrement ybias_inc(odb, biasparams); ObsBiasIncrement ybias_inc_2(ybias_inc); ObsBiasIncrement ybias_inc_3(ybias_inc); ybias_inc_2.zero(); ybias_inc_3.zero(); // Setup ObsBiasCovariance (include reading from file) - ObsBiasCovariance ybias_cov(odb, oconf); + ObsBiasCovariance ybias_cov(odb, biasparams); // Randomize increments ybias_cov.randomize(ybias_inc); // linearize for first outer loop - oconf.set("iteration", 0); - ybias_cov.linearize(ybias, oconf); + biaserrconf.set("iteration", 0); + ybias_cov.linearize(ybias, biaserrconf); // linearize for second outer loop - oconf.set("iteration", 1); - EXPECT_THROWS(ybias_cov.linearize(ybias, oconf)); + biaserrconf.set("iteration", 1); + EXPECT_THROWS(ybias_cov.linearize(ybias, biaserrconf)); // mimic QC flags from first outer loop const std::vector qc_flags(odb.nlocs(), 50); @@ -90,7 +94,7 @@ void testObsBiasCovarianceDetails() { ybias_cov.randomize(ybias_inc); // linearize for second outer loop - ybias_cov.linearize(ybias, oconf); + ybias_cov.linearize(ybias, biaserrconf); // delta_bias * B ybias_cov.multiply(ybias_inc, ybias_inc_2); diff --git a/test/ufo/ObsDiagnostics.h b/test/ufo/ObsDiagnostics.h index f18427718..b3c7a206a 100644 --- a/test/ufo/ObsDiagnostics.h +++ b/test/ufo/ObsDiagnostics.h @@ -28,6 +28,14 @@ #include "ufo/ObsDiagnostics.h" #include "ufo/ObsOperator.h" +namespace eckit +{ + // Don't use the contracted output for this type: the current implementation works only + // with integer types. + // TODO(wsmigaj) Report this as a bug in eckit. + template <> struct VectorPrintSelector { typedef VectorPrintSimple selector; }; +} // namespace eckit + namespace ufo { namespace test { @@ -53,7 +61,10 @@ void testObsDiagnostics() { const GeoVaLs gval(gconf, ospace, hop.requiredVars()); // initialize bias correction - const ObsBias ybias(ospace, conf); + eckit::LocalConfiguration biasconf = conf.getSubConfiguration("obs bias"); + ObsBiasParameters biasparams; + biasparams.validateAndDeserialize(biasconf); + const ObsBias ybias(ospace, biasparams); // create obsvector to hold H(x) ioda::ObsVector hofx(ospace); @@ -62,7 +73,7 @@ void testObsDiagnostics() { eckit::LocalConfiguration diagconf(conf, "obs diagnostics"); oops::Variables diagvars(diagconf, "variables"); EXPECT(diagvars.size() > 0); - std::unique_ptr locs(hop.locations(bgn, end)); + std::unique_ptr locs(hop.locations()); ObsDiagnostics diags(ospace, *(locs.get()), diagvars); // call H(x) to compute diagnostics diff --git a/test/ufo/ObsErrorAssign.h b/test/ufo/ObsErrorAssign.h new file mode 100644 index 000000000..6319c21e7 --- /dev/null +++ b/test/ufo/ObsErrorAssign.h @@ -0,0 +1,113 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef TEST_UFO_OBSERRORASSIGN_H_ +#define TEST_UFO_OBSERRORASSIGN_H_ + +#include +#include +#include +#include + +#define ECKIT_TESTING_SELF_REGISTER_CASES 0 + +#include "eckit/config/LocalConfiguration.h" +#include "eckit/testing/Test.h" +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" +#include "oops/mpi/mpi.h" +#include "oops/runs/Test.h" +#include "oops/util/Expect.h" +#include "test/TestEnvironment.h" +#include "ufo/filters/BlackList.h" +#include "ufo/filters/Variables.h" + +namespace ufo { +namespace test { + +void testObsErrorAssign(const eckit::LocalConfiguration &conf) { + util::DateTime bgn(conf.getString("window begin")); + util::DateTime end(conf.getString("window end")); + + const eckit::LocalConfiguration obsSpaceConf(conf, "obs space"); + ioda::ObsSpace obsspace(obsSpaceConf, oops::mpi::world(), bgn, end, oops::mpi::myself()); + + std::vector varnames {"air_pressure", "station_id", "observation_type", + "latitude_band", "channel_number", "processing_center", + "satellite_id"}; + for (std::string varname : varnames) { + if (conf.has(varname)) { + const std::vector dat = conf.getIntVector(varname); + obsspace.put_db("MetaData", varname, dat); + } + } + if (conf.has("StringVar")) { + const std::vector dat = conf.getStringVector("StringVar"); + obsspace.put_db("MetaData", "StringVar", dat); + } + + std::shared_ptr> obserr(new ioda::ObsDataVector( + obsspace, obsspace.obsvariables(), "ObsError")); + std::shared_ptr> qcflags(new ioda::ObsDataVector( + obsspace, obsspace.obsvariables())); + + const eckit::LocalConfiguration filterConf(conf, "ObsError assign"); + ufo::BlackListParameters bfparam; + bfparam.deserialize(filterConf); + + ufo::BlackList filter(obsspace, bfparam, qcflags, obserr); + if (conf.has("expectExceptionWithMessage")) { + const std::string msg = conf.getString("expectExceptionWithMessage"); + EXPECT_THROWS_MSG(filter.preProcess(), msg.c_str()); + return; + } else { + filter.preProcess(); + } + + std::vector expectedObsError; + if (conf.has("expected_obserror")) { + expectedObsError = conf.getFloatVector("expected_obserror"); + } else { + expectedObsError = conf.getFloatVector("expected_obserror_variance"); + for (size_t ind=0; ind < expectedObsError.size(); ind++) { + expectedObsError[ind] = std::sqrt(expectedObsError[ind]); + } + } + int ind = 0; + for (size_t varn = 0; varn < obserr->nvars(); ++varn) { + for (size_t locn = 0; locn < obserr->nlocs(); ++locn) { + EXPECT(std::abs((*obserr)[varn][locn] - expectedObsError[ind]) < 1e-4); + ind++; + } + } +} + +class ObsErrorAssign : public oops::Test { + private: + std::string testid() const override {return "ufo::test::ObsErrorAssign";} + + void register_tests() const override { + std::vector& ts = eckit::testing::specification(); + + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); + for (const std::string & testCaseName : conf.keys()) + { + const eckit::LocalConfiguration testCaseConf(::test::TestEnvironment::config(), testCaseName); + ts.emplace_back(CASE("ufo/ObsErrorAssign/" + testCaseName, testCaseConf) + { + testObsErrorAssign(testCaseConf); + }); + } + } + + void clear() const override {} +}; + +} // namespace test +} // namespace ufo + +#endif // TEST_UFO_OBSERRORASSIGN_H_ diff --git a/test/ufo/ObsFilterData.h b/test/ufo/ObsFilterData.h index f295560a8..b4b3b2508 100644 --- a/test/ufo/ObsFilterData.h +++ b/test/ufo/ObsFilterData.h @@ -67,8 +67,9 @@ void testObsFilterData() { EXPECT(data.nlocs() == ospace.nlocs()); /// Check that has(), get() and dtype() works on ObsSpace: + const eckit::LocalConfiguration dataconf(confs[jconf], "test data"); varconfs.clear(); - obsconf.get("float variables", varconfs); + dataconf.get("float variables", varconfs); ufo::Variables obsvars(varconfs); for (size_t jvar = 0; jvar < obsvars.nvars(); ++jvar) { EXPECT(data.has(obsvars.variable(jvar))); @@ -83,7 +84,7 @@ void testObsFilterData() { } /// Check that has(), get() and dtype() work on integer variables in ObsSpace: varconfs.clear(); - obsconf.get("integer variables", varconfs); + dataconf.get("integer variables", varconfs); ufo::Variables intvars(varconfs); for (size_t jvar = 0; jvar < intvars.nvars(); ++jvar) { EXPECT(data.has(intvars.variable(jvar))); @@ -99,7 +100,7 @@ void testObsFilterData() { /// Check that get() works on string variables in ObsSpace: varconfs.clear(); - obsconf.get("string variables", varconfs); + dataconf.get("string variables", varconfs); ufo::Variables strvars(varconfs); for (size_t jvar = 0; jvar < strvars.nvars(); ++jvar) { std::vector vec; @@ -111,7 +112,7 @@ void testObsFilterData() { /// Check that get() works on datetime variables in ObsSpace: varconfs.clear(); - obsconf.get("datetime variables", varconfs); + dataconf.get("datetime variables", varconfs); ufo::Variables dtvars(varconfs); for (size_t jvar = 0; jvar < dtvars.nvars(); ++jvar) { std::vector vec; diff --git a/test/ufo/ObsFilters.h b/test/ufo/ObsFilters.h index 54555c519..6ef5a99e1 100644 --- a/test/ufo/ObsFilters.h +++ b/test/ufo/ObsFilters.h @@ -29,11 +29,17 @@ #include "oops/util/Duration.h" #include "oops/util/Expect.h" #include "oops/util/Logger.h" +#include "oops/util/parameters/OptionalParameter.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" #include "test/interface/ObsTestsFixture.h" #include "test/TestEnvironment.h" #include "ufo/filters/QCflags.h" #include "ufo/filters/Variable.h" +#include "ufo/ObsBiasParameters.h" #include "ufo/ObsTraits.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" namespace eckit { @@ -50,61 +56,163 @@ namespace test { // ----------------------------------------------------------------------------- +/// \brief Options used to configure comparison of a variable generated by a filter +/// against a reference variable loaded from the input IODA file. +class CompareVariablesParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(CompareVariablesParameters, Parameters) + + public: + /// Variable that should be contained against reference values. + oops::RequiredParameter test{"test", this}; + /// Variable containing the reference values. + oops::RequiredParameter reference{"reference", this}; + + /// If set, the comparison will fail if any corresponding elements of the test and reference + /// variables differ by more than `absTol`. If neither `absTol` nor `relTol` is set, the + /// comparison will fail if any corresponding elements do not match exactly. + oops::OptionalParameter absTol{"absTol", this}; + + /// If set, the comparison will fail if the relative difference of any pair of corresponding + /// elements of the test and reference variables exceeds `relTol`. If neither `absTol` nor + /// `relTol` is set, the comparison will fail if any corresponding elements do not match exactly. + oops::OptionalParameter relTol{"relTol", this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Options used to configure a test running a sequence of filters on observations +/// from a single obs space. +/// +/// Note: at least one of the options whose names end in `Benchmark` or the `compareVariables` +/// option needs to be set (otherwise the test won't test much). +class ObsTypeParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsTypeParameters, Parameters) + + public: + /// Options used to configure the observation space. + oops::Parameter obsSpace{ + "obs space", eckit::LocalConfiguration(), this}; + + /// Options used to configure observation filters. + oops::Parameter>> obsFilters{ + "obs filters", {}, this}; + + /// Options passed to the observation operator that will be applied during the test. If not set, + /// no observation operator will be applied. To speed up tests of filters that depend on the + /// values produced by the observation operator (model equivalents), these values can be + /// precalculated and stored in the IODA file used to initialize the ObsSpace. In that case the + /// `obs operator` keyword should be omitted and instead the `HofX` option should be set to the + /// name of the group of ObsSpace variables containing the precalculated model equivalents. + oops::OptionalParameter obsOperator{"obs operator", this}; + + /// Group of variables storing precalculated model equivalents of observations. See the + /// description of the `obs operator` option for more information. + oops::OptionalParameter hofx{"HofX", this}; + + /// Options used to load GeoVaLs from a file. Required if any observation filters depend on + /// GeoVaLs or of the `obs operator` option is set. + oops::OptionalParameter geovals{"geovals", this}; + + /// Options used to load observation diagnostics from a file. Required if any observation filters + /// depend on observation diagnostics. + oops::OptionalParameter obsDiagnostics{"obs diagnostics", this}; + + /// Options used to configure the observation bias. + oops::Parameter obsBias{"obs bias", {}, this}; + + /// Indices of observations expected to pass quality control. + /// + /// The observations are numbered as in the input IODA file. + oops::OptionalParameter> passedObservationsBenchmark{ + "passedObservationsBenchmark", this}; + /// Number of observations expected to pass quality control. + oops::OptionalParameter passedBenchmark{"passedBenchmark", this}; + + /// Indices of observations expected to fail quality control. + /// + /// The observations are numbered as in the input IODA file. + oops::OptionalParameter> failedObservationsBenchmark{ + "failedObservationsBenchmark", this}; + /// Number of observations expected to fail quality control. + oops::OptionalParameter failedBenchmark{"failedBenchmark", this}; + + /// An integer corresponding to one of the constants in the QCflags namespace. + oops::OptionalParameter benchmarkFlag{"benchmarkFlag", this}; + + /// Indices of observations expected to receive the `benchmarkFlag` flag. + /// + /// The observations are numbered as in the input IODA file. + oops::OptionalParameter> flaggedObservationsBenchmark{ + "flaggedObservationsBenchmark", this}; + /// Number of observations expected to receive the `benchmarkFlag` flag. + oops::OptionalParameter flaggedBenchmark{"flaggedBenchmark", this}; + + /// A list of options indicating variables whose final values should be compared against reference + /// values loaded from the input IODA file. + oops::OptionalParameter> compareVariables{ + "compareVariables", this}; + + /// If set to a string, the test will pass only if the filters produce an exception whose message + /// contains that string. + oops::OptionalParameter expectExceptionWithMessage{ + "expectExceptionWithMessage", this}; +}; + +// ----------------------------------------------------------------------------- + +/// \brief Top-level options taken by the ObsFilters test. +class ObsFiltersParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(ObsFiltersParameters, Parameters) + + public: + /// Only observations taken at times lying in the (`window begin`, `window end`] interval + /// will be included in observation spaces. + oops::RequiredParameter windowBegin{"window begin", this}; + oops::RequiredParameter windowEnd{"window end", this}; + /// A list whose elements are used to configure tests running sequences of filters on + /// observations from individual observation spaces. + oops::Parameter> observations{"observations", {}, this}; +}; + +// ----------------------------------------------------------------------------- + //! //! \brief Convert indices of observations held by this process to global observation indices. //! -//! It is assumed that observations are distributed to processes in a round-robin fashion. -//! For example, 8 observations are mapped to 3 processes in the following way: +//! \param[inout] indices +//! On input: local indices of observations held by this process. On output: corresponding +//! global observation indices. +//! \param globalIdxFromLocalIdx +//! A vector whose ith element is the global index of ith observation held by this process. //! -//! Global obs. index | Process index | Local obs. index -//! ----------------- | ------------- | ---------------- -//! 0 | 0 | 0 -//! 1 | 1 | 0 -//! 2 | 2 | 0 -//! 3 | 0 | 1 -//! 4 | 1 | 1 -//! 5 | 2 | 1 -//! 6 | 0 | 2 -//! 7 | 1 | 2 -//! -void convertLocalObsIndicesToGlobal(const eckit::mpi::Comm &comm, std::vector &indices) { - const size_t rank = comm.rank(); - const size_t size = comm.size(); +void convertLocalObsIndicesToGlobal(std::vector &indices, + const std::vector &globalIdxFromLocalIdx) { for (size_t &index : indices) - index = index * size + rank; + index = globalIdxFromLocalIdx[index]; } // ----------------------------------------------------------------------------- -/// -/// \brief Gather data from all tasks and deliver the combined data to all tasks. -/// -/// \returns A vector that contains the elements of \p v from process 0 followed by the elements -/// of \p v from process 1 etc. -/// -template -std::vector allGatherv(const eckit::mpi::Comm &comm, const std::vector &v) { - eckit::mpi::Buffer buffer(comm.size()); - comm.allGatherv(v.begin(), v.end(), buffer); - return buffer.buffer; -} - //! //! Return the indices of observations whose quality control flags satisfy the //! predicate in at least one variable. //! //! \param qcFlags //! Vector of quality control flags for all observations. +//! \param obsDisttribution +//! The MPI distribution used by the ObsSpace. +//! \param globalIdxFromLocalIdx +//! A vector whose ith element is the global index of ith observation held by this process. //! \param predicate //! A function object taking an argument of type int and returning bool. -//! \param comm -//! The MPI communicator used by the ObsSpace. //! template std::vector getObservationIndicesWhere( - const eckit::mpi::Comm &comm, const ObsTraits::ObsDataVector &qcFlags, + const eckit::mpi::Comm &comm, + const std::vector &globalIdxFromLocalIdx, const Predicate &predicate) { + // Among the locations held on the calling process, identify those that satisfy the predicate. std::vector indices; for (size_t locIndex = 0; locIndex < qcFlags.nlocs(); ++locIndex) { bool satisfied = false; @@ -119,9 +227,13 @@ std::vector getObservationIndicesWhere( } } - convertLocalObsIndicesToGlobal(comm, indices); - indices = allGatherv(comm, indices); + // Convert the local location indices to global ones. + convertLocalObsIndicesToGlobal(indices, globalIdxFromLocalIdx); + // Concatenate the lists of global indices produced on all processes. + oops::mpi::allGatherv(comm, indices); + // Remove any duplicates (which can occur if some locations are held on more than one process). std::sort(indices.begin(), indices.end()); + indices.erase(std::unique(indices.begin(), indices.end()), indices.end()); return indices; } @@ -131,9 +243,11 @@ std::vector getObservationIndicesWhere( //! Return the indices of observations that have passed quality control in //! at least one variable. //! -std::vector getPassedObservationIndices(const eckit::mpi::Comm &comm, - const ObsTraits::ObsDataVector &qcFlags) { - return getObservationIndicesWhere(comm, qcFlags, [](int qcFlag) { return qcFlag == 0; }); +std::vector getPassedObservationIndices(const ObsTraits::ObsDataVector &qcFlags, + const eckit::mpi::Comm &comm, + const std::vector &globalIdxFromLocalIdx) { + return getObservationIndicesWhere(qcFlags, comm, globalIdxFromLocalIdx, + [](int qcFlag) { return qcFlag == 0; }); } // ----------------------------------------------------------------------------- @@ -142,9 +256,11 @@ std::vector getPassedObservationIndices(const eckit::mpi::Comm &comm, //! Return the indices of observations that have failed quality control in //! at least one variable. //! -std::vector getFailedObservationIndices(const eckit::mpi::Comm &comm, - const ObsTraits::ObsDataVector &qcFlags) { - return getObservationIndicesWhere(comm, qcFlags, [](int qcFlag) { return qcFlag != 0; }); +std::vector getFailedObservationIndices(const ObsTraits::ObsDataVector &qcFlags, + const eckit::mpi::Comm &comm, + const std::vector &globalIdxFromLocalIdx) { + return getObservationIndicesWhere(qcFlags, comm, globalIdxFromLocalIdx, + [](int qcFlag) { return qcFlag != 0; }); } // ----------------------------------------------------------------------------- @@ -153,42 +269,55 @@ std::vector getFailedObservationIndices(const eckit::mpi::Comm &comm, //! Return the indices of observations whose quality control flag is set to \p flag in //! at least one variable. //! -std::vector getFlaggedObservationIndices(const eckit::mpi::Comm &comm, - const ObsTraits::ObsDataVector &qcFlags, +std::vector getFlaggedObservationIndices(const ObsTraits::ObsDataVector &qcFlags, + const eckit::mpi::Comm &comm, + const std::vector &globalIdxFromLocalIdx, int flag) { - return getObservationIndicesWhere(comm, qcFlags, [flag](int qcFlag) { return qcFlag == flag; }); + return getObservationIndicesWhere(qcFlags, comm, globalIdxFromLocalIdx, + [flag](int qcFlag) { return qcFlag == flag; }); } // ----------------------------------------------------------------------------- //! -//! Return the number of elements of \p data with at least one nonzero component. +//! Return the number of nonzero elements of \p data (on all MPI ranks, but counting each location +//! only once even if it is held on multiple ranks). //! -size_t numNonzero(const ObsTraits::ObsDataVector & data) { - size_t result = 0; +size_t numNonzero(const ObsTraits::ObsDataVector & data, + const ioda::Distribution &dist) { + auto accumulator = dist.createAccumulator(); + // Local reduction for (size_t locIndex = 0; locIndex < data.nlocs(); ++locIndex) { + size_t numNonzerosAtLocation = 0; for (size_t varIndex = 0; varIndex < data.nvars(); ++varIndex) { if (data[varIndex][locIndex] != 0) - ++result; + ++numNonzerosAtLocation; } + accumulator->addTerm(locIndex, numNonzerosAtLocation); } - return result; + // Global reduction + return accumulator->computeResult(); } // ----------------------------------------------------------------------------- - //! -//! Return the number of elements of \p data with at least one component equal to \p value. +//! Return the number of elements of \p data equal to \p value (on all MPI ranks, but counting each +//! location only once even if it is held on multiple ranks). //! -size_t numEqualTo(const ObsTraits::ObsDataVector & data, int value) { - size_t result = 0; +size_t numEqualTo(const ObsTraits::ObsDataVector & data, int value, + const ioda::Distribution &dist) { + auto accumulator = dist.createAccumulator(); + // Local reduction for (size_t locIndex = 0; locIndex < data.nlocs(); ++locIndex) { + size_t numHitsAtLocation = 0; for (size_t varIndex = 0; varIndex < data.nvars(); ++varIndex) { if (data[varIndex][locIndex] == value) - ++result; + ++numHitsAtLocation; } + accumulator->addTerm(locIndex, numHitsAtLocation); } - return result; + // Global reduction + return accumulator->computeResult(); } // ----------------------------------------------------------------------------- @@ -221,8 +350,22 @@ void expectVariablesApproximatelyEqual(const ObsTraits::ObsSpace &obsspace, // ----------------------------------------------------------------------------- -void testFilters() { - typedef ::test::ObsTestsFixture Test_; +void expectVariablesRelativelyEqual(const ObsTraits::ObsSpace &obsspace, + const ufo::Variable &referenceVariable, + const ufo::Variable &testVariable, + float relTol) +{ + std::vector reference(obsspace.nlocs()); + obsspace.get_db(referenceVariable.group(), referenceVariable.variable(), reference); + std::vector test(obsspace.nlocs()); + obsspace.get_db(testVariable.group(), testVariable.variable(), test); + EXPECT(oops::are_all_close_relative(reference, test, relTol)); +} + +// ----------------------------------------------------------------------------- + +void testFilters(size_t obsSpaceIndex, oops::ObsSpace &obspace, + const ObsTypeParameters ¶ms) { typedef oops::GeoVaLs GeoVaLs_; typedef oops::ObsDiagnostics ObsDiags_; typedef oops::ObsAuxControl ObsAuxCtrl_; @@ -231,181 +374,212 @@ void testFilters() { typedef oops::ObsVector ObsVector_; typedef oops::ObsSpace ObsSpace_; - std::vector typeconfs; - ::test::TestEnvironment::config().get("observations", typeconfs); - for (eckit::LocalConfiguration & conf : typeconfs) conf.set("iteration", 0); - - for (std::size_t jj = 0; jj < Test_::obspace().size(); ++jj) { /// init QC and error - std::shared_ptr > obserr - (new oops::ObsDataVector(Test_::obspace()[jj], - Test_::obspace()[jj].obsvariables(), "ObsError")); - std::shared_ptr > - qcflags(new oops::ObsDataVector (Test_::obspace()[jj], - Test_::obspace()[jj].obsvariables())); + ObsVector_ obserr(obspace, "ObsError"); + std::shared_ptr > + qcflags(new oops::ObsDataVector (obspace, obspace.obsvariables())); // Create filters and run preProcess - ObsFilters_ filters(Test_::obspace()[jj], typeconfs[jj], qcflags, obserr); - filters.preProcess(); + ObsFilters_ filters(obspace, params.obsFilters, qcflags, obserr); + filters.preProcess(); /// call priorFilter and postFilter if hofx is available - oops::Variables geovars = filters.requiredVars(); - oops::Variables diagvars = filters.requiredHdiagnostics(); - if (typeconfs[jj].has("HofX")) { + oops::Variables geovars = filters.requiredVars(); + oops::Variables diagvars = filters.requiredHdiagnostics(); + if (params.hofx.value() != boost::none) { /// read GeoVaLs from file if required - std::unique_ptr gval; - if (geovars.size() > 0) { - const eckit::LocalConfiguration gconf(typeconfs[jj], "geovals"); - gval.reset(new GeoVaLs_(gconf, Test_::obspace()[jj], geovars)); - filters.priorFilter(*gval); - } else { - oops::Log::info() << "Filters don't require geovals, priorFilter not called" << std::endl; - } + std::unique_ptr gval; + if (geovars.size() > 0) { + if (params.geovals.value() == boost::none) + throw eckit::UserError("Element #" + std::to_string(obsSpaceIndex) + + " of the 'observations' list requires a 'geovals' section", Here()); + gval.reset(new GeoVaLs_(*params.geovals.value(), obspace, geovars)); + filters.priorFilter(*gval); + } else { + oops::Log::info() << "Filters don't require geovals, priorFilter not called" << std::endl; + } /// read H(x) and ObsDiags from file - oops::Log::info() << "HofX section specified, reading HofX from file" << std::endl; - const std::string hofxgroup = typeconfs[jj].getString("HofX"); - ObsVector_ hofx(Test_::obspace()[jj], hofxgroup); - eckit::LocalConfiguration obsdiagconf; - if (diagvars.size() > 0) { - obsdiagconf = eckit::LocalConfiguration(typeconfs[jj], "obs diagnostics"); - oops::Log::info() << "Obs diagnostics section specified, reading obs diagnostics from file" - << std::endl; - } - const ObsDiags_ diags(obsdiagconf, Test_::obspace()[jj], diagvars); - filters.postFilter(hofx, diags); - } else if (typeconfs[jj].has("obs operator")) { + oops::Log::info() << "HofX section specified, reading HofX from file" << std::endl; + const std::string &hofxgroup = *params.hofx.value(); + ObsVector_ hofx(obspace, hofxgroup); + eckit::LocalConfiguration obsdiagconf; + if (diagvars.size() > 0) { + if (params.obsDiagnostics.value() == boost::none) + throw eckit::UserError("Element #" + std::to_string(obsSpaceIndex) + + " of the 'observations' list requires an 'obs diagnostics' section", + Here()); + obsdiagconf = *params.obsDiagnostics.value(); + oops::Log::info() << "Obs diagnostics section specified, reading obs diagnostics from file" + << std::endl; + } + const ObsDiags_ diags(obsdiagconf, obspace, diagvars); + filters.postFilter(hofx, diags); + } else if (params.obsOperator.value() != boost::none) { /// read GeoVaLs, compute H(x) and ObsDiags - oops::Log::info() << "ObsOperator section specified, computing HofX" << std::endl; - const eckit::LocalConfiguration obsopconf(typeconfs[jj], "obs operator"); - ObsOperator_ hop(Test_::obspace()[jj], obsopconf); - const ObsAuxCtrl_ ybias(Test_::obspace()[jj], typeconfs[jj]); - ObsVector_ hofx(Test_::obspace()[jj]); - oops::Variables vars; - vars += hop.requiredVars(); - vars += filters.requiredVars(); - if (typeconfs[jj].has("obs bias")) vars += ybias.requiredVars(); - const eckit::LocalConfiguration gconf(typeconfs[jj], "geovals"); - const GeoVaLs_ gval(gconf, Test_::obspace()[jj], vars); - oops::Variables diagvars; - diagvars += filters.requiredHdiagnostics(); - if (typeconfs[jj].has("obs bias")) diagvars += ybias.requiredHdiagnostics(); - ObsDiags_ diags(Test_::obspace()[jj], - hop.locations(Test_::obspace()[jj].windowStart(), - Test_::obspace()[jj].windowEnd()), - diagvars); - filters.priorFilter(gval); - hop.simulateObs(gval, hofx, ybias, diags); - hofx.save("hofx"); - filters.postFilter(hofx, diags); - } else if (geovars.size() > 0) { + oops::Log::info() << "ObsOperator section specified, computing HofX" << std::endl; + ObsOperator_ hop(obspace, *params.obsOperator.value()); + const ObsAuxCtrl_ ybias(obspace, params.obsBias.value()); + ObsVector_ hofx(obspace); + oops::Variables vars; + vars += hop.requiredVars(); + vars += filters.requiredVars(); + vars += ybias.requiredVars(); + if (params.geovals.value() == boost::none) + throw eckit::UserError("Element #" + std::to_string(obsSpaceIndex) + + " of the 'observations' list requires a 'geovals' section", Here()); + const GeoVaLs_ gval(*params.geovals.value(), obspace, vars); + oops::Variables diagvars; + diagvars += filters.requiredHdiagnostics(); + diagvars += ybias.requiredHdiagnostics(); + ObsDiags_ diags(obspace, hop.locations(), diagvars); + filters.priorFilter(gval); + hop.simulateObs(gval, hofx, ybias, diags); + hofx.save("hofx"); + filters.postFilter(hofx, diags); + } else if (geovars.size() > 0) { /// Only call priorFilter - const eckit::LocalConfiguration gconf(typeconfs[jj], "geovals"); - const GeoVaLs_ gval(gconf, Test_::obspace()[jj], geovars); - filters.priorFilter(gval); - oops::Log::info() << "HofX or ObsOperator sections not provided for filters, " << - "postFilter not called" << std::endl; - } else { + if (params.geovals.value() == boost::none) + throw eckit::UserError("Element #" + std::to_string(obsSpaceIndex) + + " of the 'observations' list requires a 'geovals' section", Here()); + const GeoVaLs_ gval(*params.geovals.value(), obspace, geovars); + filters.priorFilter(gval); + oops::Log::info() << "HofX or ObsOperator sections not provided for filters, " << + "postFilter not called" << std::endl; + } else { /// no need to run priorFilter or postFilter - oops::Log::info() << "GeoVaLs not required, HofX or ObsOperator sections not " << - "provided for filters, only preProcess was called" << std::endl; - } + oops::Log::info() << "GeoVaLs not required, HofX or ObsOperator sections not " << + "provided for filters, only preProcess was called" << std::endl; + } - qcflags->save("EffectiveQC"); - const std::string errname = "EffectiveError"; - obserr->save(errname); + qcflags->save("EffectiveQC"); + const std::string errname = "EffectiveError"; + obserr.save(errname); // Compare with known results - bool atLeastOneBenchmarkFound = false; - const ObsTraits::ObsSpace &obsspace = Test_::obspace()[jj].obsspace(); + bool atLeastOneBenchmarkFound = false; + const ObsTraits::ObsSpace &ufoObsSpace = obspace.obsspace(); + + if (params.passedObservationsBenchmark.value() != boost::none) { + atLeastOneBenchmarkFound = true; + const std::vector &passedObsBenchmark = + *params.passedObservationsBenchmark.value(); + const std::vector passedObs = getPassedObservationIndices( + qcflags->obsdatavector(), ufoObsSpace.comm(), ufoObsSpace.index()); + EXPECT_EQUAL(passedObs, passedObsBenchmark); + } - if (typeconfs[jj].has("passedObservationsBenchmark")) { - atLeastOneBenchmarkFound = true; - const std::vector passedObsBenchmark = - typeconfs[jj].getUnsignedVector("passedObservationsBenchmark"); - const std::vector passedObs = getPassedObservationIndices( - obsspace.comm(), qcflags->obsdatavector()); - EXPECT_EQUAL(passedObs, passedObsBenchmark); - } + if (params.passedBenchmark.value() != boost::none) { + atLeastOneBenchmarkFound = true; + const size_t passedBenchmark = *params.passedBenchmark.value(); + size_t passed = numEqualTo(qcflags->obsdatavector(), ufo::QCflags::pass, + *ufoObsSpace.distribution()); + EXPECT_EQUAL(passed, passedBenchmark); + } - if (typeconfs[jj].has("passedBenchmark")) { - atLeastOneBenchmarkFound = true; - const int passedBenchmark = typeconfs[jj].getInt("passedBenchmark"); - int passed = numEqualTo(qcflags->obsdatavector(), ufo::QCflags::pass); - obsspace.comm().allReduceInPlace(passed, eckit::mpi::Operation::SUM); - EXPECT_EQUAL(passed, passedBenchmark); - } + if (params.failedObservationsBenchmark.value() != boost::none) { + atLeastOneBenchmarkFound = true; + const std::vector &failedObsBenchmark = + *params.failedObservationsBenchmark.value(); + const std::vector failedObs = getFailedObservationIndices( + qcflags->obsdatavector(), ufoObsSpace.comm(), ufoObsSpace.index()); + EXPECT_EQUAL(failedObs, failedObsBenchmark); + } + + if (params.failedBenchmark.value() != boost::none) { + atLeastOneBenchmarkFound = true; + const size_t failedBenchmark = *params.failedBenchmark.value(); + size_t failed = numNonzero(qcflags->obsdatavector(), *ufoObsSpace.distribution()); + EXPECT_EQUAL(failed, failedBenchmark); + } - if (typeconfs[jj].has("failedObservationsBenchmark")) { + if (params.benchmarkFlag.value() != boost::none) { + const int flag = *params.benchmarkFlag.value(); + + if (params.flaggedObservationsBenchmark.value() != boost::none) { atLeastOneBenchmarkFound = true; - const std::vector failedObsBenchmark = - typeconfs[jj].getUnsignedVector("failedObservationsBenchmark"); - const std::vector failedObs = getFailedObservationIndices( - obsspace.comm(), qcflags->obsdatavector()); - EXPECT_EQUAL(failedObs, failedObsBenchmark); + const std::vector &flaggedObsBenchmark = + *params.flaggedObservationsBenchmark.value(); + const std::vector flaggedObs = + getFlaggedObservationIndices(qcflags->obsdatavector(), ufoObsSpace.comm(), + ufoObsSpace.index(), flag); + EXPECT_EQUAL(flaggedObs, flaggedObsBenchmark); } - if (typeconfs[jj].has("failedBenchmark")) { + if (params.flaggedBenchmark.value() != boost::none) { atLeastOneBenchmarkFound = true; - const int failedBenchmark = typeconfs[jj].getInt("failedBenchmark"); - int failed = numNonzero(qcflags->obsdatavector()); - obsspace.comm().allReduceInPlace(failed, eckit::mpi::Operation::SUM); - EXPECT_EQUAL(failed, failedBenchmark); + const size_t flaggedBenchmark = *params.flaggedBenchmark.value(); + size_t flagged = numEqualTo(qcflags->obsdatavector(), flag, *ufoObsSpace.distribution()); + EXPECT_EQUAL(flagged, flaggedBenchmark); } + } - if (typeconfs[jj].has("benchmarkFlag")) { - const int flag = typeconfs[jj].getInt("benchmarkFlag"); + if (params.compareVariables.value() != boost::none) { + for (const CompareVariablesParameters &compareVariablesParams : + *params.compareVariables.value()) { + atLeastOneBenchmarkFound = true; - if (typeconfs[jj].has("flaggedObservationsBenchmark")) { - atLeastOneBenchmarkFound = true; - const std::vector flaggedObsBenchmark = - typeconfs[jj].getUnsignedVector("flaggedObservationsBenchmark"); - const std::vector flaggedObs = - getFlaggedObservationIndices(obsspace.comm(), qcflags->obsdatavector(), flag); - EXPECT_EQUAL(flaggedObsBenchmark, flaggedObsBenchmark); - } + const ufo::Variable &referenceVariable = compareVariablesParams.reference; + const ufo::Variable &testVariable = compareVariablesParams.test; - if (typeconfs[jj].has("flaggedBenchmark")) { - atLeastOneBenchmarkFound = true; - const int flaggedBenchmark = typeconfs[jj].getInt("flaggedBenchmark"); - int flagged = numEqualTo(qcflags->obsdatavector(), flag); - obsspace.comm().allReduceInPlace(flagged, eckit::mpi::Operation::SUM); - EXPECT_EQUAL(flagged, flaggedBenchmark); - } - } - - if (typeconfs[jj].has("compareVariables")) { - for (const eckit::LocalConfiguration &compareVariablesConf : - typeconfs[jj].getSubConfigurations("compareVariables")) { - atLeastOneBenchmarkFound = true; - - ufo::Variable referenceVariable(compareVariablesConf.getSubConfiguration("reference")); - ufo::Variable testVariable(compareVariablesConf.getSubConfiguration("test")); - - switch (obsspace.dtype(referenceVariable.group(), referenceVariable.variable())) { - case ioda::ObsDtype::Integer: - expectVariablesEqual(obsspace, referenceVariable, testVariable); - break; - case ioda::ObsDtype::String: - expectVariablesEqual(obsspace, referenceVariable, testVariable); - break; - case ioda::ObsDtype::DateTime: - expectVariablesEqual(obsspace, referenceVariable, testVariable); - break; - case ioda::ObsDtype::Float: - if (!compareVariablesConf.has("absTol")) { - expectVariablesEqual(obsspace, referenceVariable, testVariable); - } else { - const float tol = compareVariablesConf.getFloat("absTol"); - expectVariablesApproximatelyEqual(obsspace, referenceVariable, testVariable, tol); + switch (ufoObsSpace.dtype(referenceVariable.group(), referenceVariable.variable())) { + case ioda::ObsDtype::Integer: + expectVariablesEqual(ufoObsSpace, referenceVariable, testVariable); + break; + case ioda::ObsDtype::String: + expectVariablesEqual(ufoObsSpace, referenceVariable, testVariable); + break; + case ioda::ObsDtype::DateTime: + expectVariablesEqual(ufoObsSpace, referenceVariable, testVariable); + break; + case ioda::ObsDtype::Float: + if (compareVariablesParams.absTol.value() == boost::none && + compareVariablesParams.relTol.value() == boost::none) { + expectVariablesEqual(ufoObsSpace, referenceVariable, testVariable); + } else { + if (compareVariablesParams.absTol.value() != boost::none) { + const float tol = *compareVariablesParams.absTol.value(); + expectVariablesApproximatelyEqual(ufoObsSpace, referenceVariable, testVariable, tol); + } else if (compareVariablesParams.relTol.value() != boost::none) { + const float tol = *compareVariablesParams.relTol.value(); + expectVariablesRelativelyEqual(ufoObsSpace, referenceVariable, testVariable, tol); } - break; - case ioda::ObsDtype::None: - ASSERT_MSG(false, "Reference variable not found in observation space"); } + break; + case ioda::ObsDtype::None: + ASSERT_MSG(false, "Reference variable not found in observation space"); } } + } + + if (params.expectExceptionWithMessage.value() != boost::none) { + // We shouldn't get here, but if we do, we want the test to fail with a message that an + // expected exception hasn't been thrown rather than that no benchmarks have been found. + atLeastOneBenchmarkFound = true; + } + + EXPECT(atLeastOneBenchmarkFound); +} + +// ----------------------------------------------------------------------------- + +void runTest() { + typedef ::test::ObsTestsFixture Test_; + typedef oops::ObsSpace ObsSpace_; - EXPECT(atLeastOneBenchmarkFound); + ObsFiltersParameters params; + params.validateAndDeserialize(::test::TestEnvironment::config()); + + for (std::size_t jj = 0; jj < Test_::obspace().size(); ++jj) { +/// identify parameters used for this group of observations + const ObsTypeParameters &typeParams = params.observations.value()[jj]; + + if (typeParams.expectExceptionWithMessage.value() != boost::none) { + const char *expectedMessage = typeParams.expectExceptionWithMessage.value()->c_str(); + EXPECT_THROWS_MSG(testFilters(jj, Test_::obspace()[jj], typeParams), + expectedMessage); + } else { + testFilters(jj, Test_::obspace()[jj], typeParams); + } } } @@ -423,7 +597,7 @@ class ObsFilters : public oops::Test { std::vector& ts = eckit::testing::specification(); ts.emplace_back(CASE("ufo/ObsFilters/testFilters") - { testFilters(); }); + { runTest(); }); } void clear() const override { diff --git a/test/ufo/ObsFunction.h b/test/ufo/ObsFunction.h index 65f1dd1da..a14d86dd0 100644 --- a/test/ufo/ObsFunction.h +++ b/test/ufo/ObsFunction.h @@ -17,16 +17,20 @@ #include "eckit/config/LocalConfiguration.h" #include "eckit/testing/Test.h" +#include "ioda/distribution/DistributionUtils.h" #include "ioda/ObsSpace.h" #include "ioda/ObsVector.h" #include "oops/mpi/mpi.h" #include "oops/runs/Test.h" +#include "oops/util/missingValues.h" +#include "test/interface/ObsTestsFixture.h" #include "test/TestEnvironment.h" #include "ufo/filters/ObsFilterData.h" #include "ufo/filters/obsfunctions/ObsFunction.h" #include "ufo/filters/Variables.h" #include "ufo/GeoVaLs.h" #include "ufo/ObsDiagnostics.h" +#include "ufo/ObsTraits.h" namespace ufo { namespace test { @@ -35,17 +39,18 @@ namespace test { void dataVectorDiff(const ioda::ObsSpace & ospace, ioda::ObsDataVector & vals, const ioda::ObsDataVector & ref, std::vector & rms_out) { + float missing = util::missingValue(missing); /// Loop through variables and calculate rms for each variable for (size_t ivar = 0; ivar < vals.nvars() ; ++ivar) { - float rms = 0.0; - int nobs = 0; for (size_t jj = 0; jj < ref.nlocs() ; ++jj) { - vals[ivar][jj] -= ref[ivar][jj]; - rms += vals[ivar][jj] * vals[ivar][jj]; - nobs++; + if (vals[ivar][jj] != missing && ref[ivar][jj] != missing) { + vals[ivar][jj] -= ref[ivar][jj]; + } else { + vals[ivar][jj] = missing; + } } - ospace.comm().allReduceInPlace(rms, eckit::mpi::sum()); - ospace.comm().allReduceInPlace(nobs, eckit::mpi::sum()); + int nobs = globalNumNonMissingObs(*ospace.distribution(), 1, vals[ivar]); + float rms = dotProduct(*ospace.distribution(), 1, vals[ivar], vals[ivar]); if (nobs > 0) rms = sqrt(rms / static_cast(nobs)); rms_out[ivar] = rms; } @@ -54,76 +59,79 @@ void dataVectorDiff(const ioda::ObsSpace & ospace, ioda::ObsDataVector & // ----------------------------------------------------------------------------- void testFunction() { - const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); -/// Setup ObsSpace - util::DateTime bgn(conf.getString("window begin")); - util::DateTime end(conf.getString("window end")); - const eckit::LocalConfiguration obsconf(conf, "obs space"); - ioda::ObsSpace ospace(obsconf, oops::mpi::world(), bgn, end, oops::mpi::myself()); + typedef ::test::ObsTestsFixture Test_; + + std::vector typeconfs; + ::test::TestEnvironment::config().get("observations", typeconfs); + + for (std::size_t jj = 0; jj < Test_::obspace().size(); ++jj) { + ioda::ObsSpace &ospace = Test_::obspace()[jj].obsspace(); + const eckit::Configuration &conf = typeconfs[jj]; /// Setup ObsFilterData - ObsFilterData inputs(ospace); + ObsFilterData inputs(ospace); /// Get function name and which group to use for H(x) - const eckit::LocalConfiguration obsfuncconf(conf, "obs function"); - Variable funcname(obsfuncconf); + const eckit::LocalConfiguration obsfuncconf(conf, "obs function"); + Variable funcname(obsfuncconf); /// Setup function - ObsFunction obsfunc(funcname); - ufo::Variables allfuncvars = obsfunc.requiredVariables(); + ObsFunction obsfunc(funcname); + ufo::Variables allfuncvars = obsfunc.requiredVariables(); /// Setup GeoVaLs - const oops::Variables geovars = allfuncvars.allFromGroup("GeoVaLs").toOopsVariables(); - std::unique_ptr gval; - if (geovars.size() > 0) { - const eckit::LocalConfiguration gconf(conf, "geovals"); - gval.reset(new GeoVaLs(gconf, ospace, geovars)); - inputs.associate(*gval); - } + const oops::Variables geovars = allfuncvars.allFromGroup("GeoVaLs").toOopsVariables(); + std::unique_ptr gval; + if (geovars.size() > 0) { + const eckit::LocalConfiguration gconf(conf, "geovals"); + gval.reset(new GeoVaLs(gconf, ospace, geovars)); + inputs.associate(*gval); + } /// Setup ObsDiags - const oops::Variables diagvars = allfuncvars.allFromGroup("ObsDiag").toOopsVariables(); - std::unique_ptr diags; - if (diagvars.size() > 0) { - const eckit::LocalConfiguration diagconf(conf, "obs diagnostics"); - diags.reset(new ObsDiagnostics(diagconf, ospace, diagvars)); - inputs.associate(*diags); - } + const oops::Variables diagvars = allfuncvars.allFromGroup("ObsDiag").toOopsVariables(); + std::unique_ptr diags; + if (diagvars.size() > 0) { + const eckit::LocalConfiguration diagconf(conf, "obs diagnostics"); + diags.reset(new ObsDiagnostics(diagconf, ospace, diagvars)); + inputs.associate(*diags); + } /// Get output variable names - const oops::Variables outputvars(obsfuncconf, "variables"); + const oops::Variables outputvars(obsfuncconf, "variables"); /// Compute function result - ioda::ObsDataVector vals(ospace, outputvars, "ObsFunction", false); - obsfunc.compute(inputs, vals); - vals.save("TestResult"); - int nvars = vals.nvars(); + ioda::ObsDataVector vals(ospace, outputvars, "ObsFunction", false); + obsfunc.compute(inputs, vals); + vals.save("TestResult"); + int nvars = vals.nvars(); /// Compute function result through ObsFilterData - ioda::ObsDataVector vals_ofd(ospace, outputvars, "ObsFunction", false); - inputs.get(funcname, vals_ofd); + ioda::ObsDataVector vals_ofd(ospace, outputvars, "ObsFunction", false); + inputs.get(funcname, vals_ofd); /// Read reference values from ObsSpace - ioda::ObsDataVector ref(ospace, outputvars, "TestReference"); + ioda::ObsDataVector ref(ospace, outputvars, "TestReference"); - const double tol = obsfuncconf.getDouble("tolerance"); + const double tol = obsfuncconf.getDouble("tolerance"); /// Calculate rms(f(x) - ref) and compare to tolerance - std::vector rms_out(nvars); - dataVectorDiff(ospace, vals, ref, rms_out); + std::vector rms_out(nvars); + dataVectorDiff(ospace, vals, ref, rms_out); - oops::Log::info() << "Vector difference between reference and computed: " << std::endl; - oops::Log::info() << vals << std::endl; - for (size_t ivar = 0; ivar < nvars; ivar++) { - EXPECT(rms_out[ivar] < 100*tol); // change tol from percent to actual value. - } + oops::Log::info() << "Vector difference between reference and computed: " << std::endl; + oops::Log::info() << vals << std::endl; + for (size_t ivar = 0; ivar < nvars; ivar++) { + EXPECT(rms_out[ivar] < 100*tol); // change tol from percent to actual value. + } - dataVectorDiff(ospace, vals_ofd, ref, rms_out); - oops::Log::info() << "Vector difference between reference and computed via ObsFilterData: " - << std::endl; - oops::Log::info() << vals_ofd << std::endl; - for (size_t ivar = 0; ivar < nvars; ivar++) { - EXPECT(rms_out[ivar] < 100*tol); // change tol from percent to actual value. + dataVectorDiff(ospace, vals_ofd, ref, rms_out); + oops::Log::info() << "Vector difference between reference and computed via ObsFilterData: " + << std::endl; + oops::Log::info() << vals_ofd << std::endl; + for (size_t ivar = 0; ivar < nvars; ivar++) { + EXPECT(rms_out[ivar] < 100*tol); // change tol from percent to actual value. + } } } diff --git a/test/ufo/OperatorUtils.h b/test/ufo/OperatorUtils.h new file mode 100644 index 000000000..e429b6b7d --- /dev/null +++ b/test/ufo/OperatorUtils.h @@ -0,0 +1,90 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef TEST_UFO_OPERATORUTILS_H_ +#define TEST_UFO_OPERATORUTILS_H_ + +#include +#include +#include +#include + +#include "eckit/config/LocalConfiguration.h" +#include "eckit/testing/Test.h" +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" +#include "oops/mpi/mpi.h" +#include "oops/runs/Test.h" +#include "oops/util/Expect.h" +#include "test/TestEnvironment.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/OperatorUtils.h" + +namespace ufo { +namespace test { + +/// Code shared by all tests +class TestFixture : private boost::noncopyable { + public: + static const ioda::ObsSpace & obsspace() {return *getInstance().obsspace_;} + static const eckit::LocalConfiguration & config() {return getInstance().config_;} + + private: + static TestFixture & getInstance() { + static TestFixture theTestFixture; + return theTestFixture; + } + + TestFixture() { + const eckit::Configuration & conf = ::test::TestEnvironment::config(); + util::DateTime bgn(conf.getString("window begin")); + util::DateTime end(conf.getString("window end")); + const eckit::LocalConfiguration obsconf(conf, "obs space"); + obsspace_.reset(new ioda::ObsSpace(obsconf, oops::mpi::world(), bgn, end, oops::mpi::myself())); + config_ = conf.getSubConfiguration("cases"); + } + + std::shared_ptr obsspace_; + eckit::LocalConfiguration config_; +}; + +void testOperatorUtils(const eckit::LocalConfiguration &conf) { + oops::Variables expectedOperatorVariables = + ufo::Variables(conf.getSubConfigurations("expected operator variables")).toOopsVariables(); + std::vector expectedOperatorVariableIndices = + conf.getIntVector("expected indices"); + + oops::Variables operatorVariables; + std::vector operatorVariableIndices; + ufo::getOperatorVariables(conf, TestFixture::obsspace().obsvariables(), + operatorVariables, operatorVariableIndices); + + EXPECT_EQUAL(operatorVariables, expectedOperatorVariables); + EXPECT_EQUAL(operatorVariableIndices, expectedOperatorVariableIndices); +} + +CASE("ufo/OperatorUtils/Without 'variables' option") { + testOperatorUtils(TestFixture::config().getSubConfiguration("without variables")); +} + +CASE("ufo/OperatorUtils/With 'variables' option") { + testOperatorUtils(TestFixture::config().getSubConfiguration("with variables")); +} + +class OperatorUtils : public oops::Test { + private: + std::string testid() const override {return "ufo::test::OperatorUtils";} + + void register_tests() const override {} + + void clear() const override {} +}; + +} // namespace test +} // namespace ufo + +#endif // TEST_UFO_OPERATORUTILS_H_ diff --git a/test/ufo/ParallelObsDistribution.h b/test/ufo/ParallelObsDistribution.h deleted file mode 100644 index 69539ac73..000000000 --- a/test/ufo/ParallelObsDistribution.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * (C) Copyright 2020 Met Office UK - * - * This software is licensed under the terms of the Apache Licence Version 2.0 - * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - */ - -#ifndef TEST_UFO_PARALLELOBSDISTRIBUTION_H_ -#define TEST_UFO_PARALLELOBSDISTRIBUTION_H_ - -#include -#include -#include -#include - -#include "eckit/config/LocalConfiguration.h" -#include "eckit/testing/Test.h" -#include "ioda/ObsSpace.h" -#include "ioda/ObsVector.h" -#include "oops/mpi/mpi.h" -#include "oops/runs/Test.h" -#include "oops/util/Expect.h" -#include "oops/util/parameters/Parameters.h" -#include "oops/util/parameters/RequiredParameter.h" -#include "test/TestEnvironment.h" -#include "ufo/filters/Variable.h" -#include "ufo/utils/ParallelObsDistribution.h" -#include "ufo/utils/parameters/ParameterTraitsVariable.h" - -namespace eckit -{ - // Don't use the contracted output for these types: the current implementation works only - // with integer types. - // TODO(wsmigaj) Report this (especially for floats) as a bug in eckit? - template <> struct VectorPrintSelector { typedef VectorPrintSimple selector; }; - template <> struct VectorPrintSelector { typedef VectorPrintSimple selector; }; - template <> struct VectorPrintSelector { typedef VectorPrintSimple selector; }; -} // namespace eckit - -namespace ufo { -namespace test { - -template -class TestParameters : public oops::Parameters { - OOPS_CONCRETE_PARAMETERS(TestParameters, Parameters) - - public: - oops::RequiredParameter variable{"variable", this}; - oops::RequiredParameter> expectedValues{"expectedValues", this}; -}; - -template -void ifTIsDoubleCastDoublesToFloats(std::vector &v) -{} - -template <> -void ifTIsDoubleCastDoublesToFloats(std::vector &v) { - for (double &x : v) - x = static_cast(x); -} - -template -void testVariable(const std::string §ion) { - const eckit::Configuration &topConf = ::test::TestEnvironment::config(); - - util::DateTime bgn(topConf.getString("window begin")); - util::DateTime end(topConf.getString("window end")); - - const eckit::LocalConfiguration obsSpaceConf(topConf, "obs space"); - ioda::ObsSpace obsSpace(obsSpaceConf, oops::mpi::world(), bgn, end, oops::mpi::myself()); - - TestParameters parameters; - parameters.deserialize(topConf.getSubConfiguration(section)); - std::vector expectedValues = parameters.expectedValues; - // IODA stores all floating-point variables in single precision, so if the T template - // parameter is double, we need to cast the expected values, which have been loaded in double - // precision from the YAML file, to single precision before comparing. - ifTIsDoubleCastDoublesToFloats(expectedValues); - - ParallelObsDistribution obsDistribution(obsSpace); - std::vector globalValues = getGlobalVariableValues( - obsSpace, obsDistribution, - parameters.variable.value().variable(), parameters.variable.value().group()); - - EXPECT_EQUAL(globalValues, expectedValues); -} - -CASE("ufo/ParallelObsDistribution/getGlobalIntVariableValues") { - testVariable("intTest"); -} - -CASE("ufo/ParallelObsDistribution/getGlobalFloatVariableValues") { - testVariable("floatOrDoubleTest"); -} - -CASE("ufo/ParallelObsDistribution/getGlobalDoubleVariableValues") { - testVariable("floatOrDoubleTest"); -} - -CASE("ufo/ParallelObsDistribution/getGlobalDateTimeVariableValues") { - testVariable("dateTimeTest"); -} - -CASE("ufo/ParallelObsDistribution/members") { - const eckit::Configuration &topConf = ::test::TestEnvironment::config(); - - util::DateTime bgn(topConf.getString("window begin")); - util::DateTime end(topConf.getString("window end")); - - const eckit::LocalConfiguration obsSpaceConf(topConf, "obs space"); - ioda::ObsSpace obsSpace(obsSpaceConf, oops::mpi::world(), bgn, end, oops::mpi::myself()); - - ParallelObsDistribution obsDistribution(obsSpace); - const size_t gnlocs = obsSpace.gnlocs(); - EXPECT_EQUAL(obsDistribution.globalObsCount(), gnlocs); - - const size_t rank = obsSpace.comm().rank(); - const size_t nlocs = obsSpace.nlocs(); - EXPECT_EQUAL(obsDistribution.localObsCounts()[rank], nlocs); -} - -class ParallelObsDistribution : public oops::Test { - private: - std::string testid() const override {return "ufo::test::ParallelObsDistribution";} - - void register_tests() const override {} - - void clear() const override {} -}; - -} // namespace test -} // namespace ufo - -#endif // TEST_UFO_PARALLELOBSDISTRIBUTION_H_ diff --git a/test/ufo/ParallelPoissonDiskThinning.h b/test/ufo/ParallelPoissonDiskThinning.h new file mode 100644 index 000000000..c7a468ce9 --- /dev/null +++ b/test/ufo/ParallelPoissonDiskThinning.h @@ -0,0 +1,163 @@ +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef TEST_UFO_PARALLELPOISSONDISKTHINNING_H_ +#define TEST_UFO_PARALLELPOISSONDISKTHINNING_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define ECKIT_TESTING_SELF_REGISTER_CASES 0 + +#include "eckit/config/LocalConfiguration.h" +#include "eckit/testing/Test.h" +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" +#include "oops/mpi/mpi.h" +#include "oops/runs/Test.h" +#include "oops/util/AssociativeContainers.h" +#include "oops/util/Expect.h" +#include "test/TestEnvironment.h" +#include "ufo/filters/PoissonDiskThinning.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/StringUtils.h" // for splitVarGroup + +namespace eckit +{ + // Don't use the contracted output for these types: the current implementation works only + // with integer types. + template <> struct VectorPrintSelector { typedef VectorPrintSimple selector; }; +} // namespace eckit + +namespace ufo { +namespace test { + +void testPoissonDiskThinning(const eckit::LocalConfiguration &conf, + bool expectValidationError = false) { + util::DateTime bgn(conf.getString("window begin")); + util::DateTime end(conf.getString("window end")); + + const eckit::LocalConfiguration obsSpaceConf(conf, "obs space"); + ioda::ObsSpace obsspace(obsSpaceConf, oops::mpi::world(), bgn, end, oops::mpi::myself()); + + std::shared_ptr> obserr(new ioda::ObsDataVector( + obsspace, obsspace.obsvariables(), "ObsError")); + std::shared_ptr> qcflags(new ioda::ObsDataVector( + obsspace, obsspace.obsvariables())); + + eckit::LocalConfiguration filterConf(conf, "Poisson Disk Thinning"); + ufo::PoissonDiskThinningParameters filterParameters; + filterParameters.validateAndDeserialize(filterConf); + ufo::PoissonDiskThinning filter(obsspace, filterParameters, qcflags, obserr); + + // Stagger the start of the thinning process on different ranks to detect problems related to + // initialisation of the random generator seed from system time. + std::this_thread::sleep_for(obsspace.comm().rank() * std::chrono::milliseconds(1100)); + + filter.preProcess(); + + std::vector isObsRetained(qcflags->nlocs(), 0); + for (size_t i = 0; i < qcflags->nlocs(); ++i) + isObsRetained[i] = ((*qcflags)[0][i] == ufo::QCflags::pass); + + if (obsSpaceConf.getString("distribution", "RoundRobin") == "InefficientDistribution") { + // PART 1: Verify that all ranks have retained the same observations. + const size_t rootRank = 0; + size_t isObsRetainedSizeOnRoot = isObsRetained.size(); + obsspace.comm().broadcast(isObsRetainedSizeOnRoot, rootRank); + + std::vector isObsRetainedOnRoot(isObsRetainedSizeOnRoot); + if (obsspace.comm().rank() == rootRank) { + isObsRetainedOnRoot = isObsRetained; + } + obsspace.comm().broadcast(isObsRetainedOnRoot, rootRank); + + EXPECT_EQUAL(isObsRetained, isObsRetainedOnRoot); + } + + // PART 2: Verify that all retained observations are sufficiently far from each other + // and that all rejected observations so close to a retained observation that they can't + // themselves be retained. + + // Collect status of observations on all processes + obsspace.distribution()->allGatherv(isObsRetained); + std::set retainedGlobalObsIndices; + for (size_t i = 0; i < isObsRetained.size(); ++i) + if (isObsRetained[i]) + retainedGlobalObsIndices.insert(i); + + // Collect pressures from all processes + std::vector pressures(obsspace.nlocs()); + obsspace.get_db("MetaData", "air_pressure", pressures); + obsspace.distribution()->allGatherv(pressures); + + // Collect categories from all processes + std::vector categories(obsspace.nlocs(), 0); + if (filterConf.has("category_variable")) + { + std::string name, group; + splitVarGroup(filterConf.getString("category_variable.name"), name, group); + obsspace.get_db(group, name, categories); + } + obsspace.distribution()->allGatherv(categories); + + // Check distances between observations + const float minAllowedDistance = filterConf.getFloat("min_vertical_spacing"); + std::vector minDistanceToRetainedObs(pressures.size(), + std::numeric_limits::max()); + for (size_t i : retainedGlobalObsIndices) { + for (size_t j = 0; j < pressures.size(); ++j) { + if (j == i || categories[i] != categories[j]) + continue; + const float distance = std::abs(pressures[i] - pressures[j]); + if (oops::contains(retainedGlobalObsIndices, j)) { + // Retained observations should be far from other retained observation + EXPECT(distance >= minAllowedDistance); + } else { + minDistanceToRetainedObs[j] = std::min(minDistanceToRetainedObs[j], distance); + } + } + } + + for (size_t j = 0; j < pressures.size(); ++j) { + if (!oops::contains(retainedGlobalObsIndices, j)) { + // Rejected observations should be close to some retained observation + EXPECT(minDistanceToRetainedObs[j] < minAllowedDistance); + } + } +} + +class ParallelPoissonDiskThinning : public oops::Test { + private: + std::string testid() const override {return "ufo::test::ParallelPoissonDiskThinning";} + + void register_tests() const override { + std::vector& ts = eckit::testing::specification(); + + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); + for (const std::string & testCaseName : conf.keys()) + { + const eckit::LocalConfiguration testCaseConf(::test::TestEnvironment::config(), testCaseName); + ts.emplace_back(CASE("ufo/ParallelPoissonDiskThinning/" + testCaseName, testCaseConf) + { + testPoissonDiskThinning(testCaseConf); + }); + } + } + + void clear() const override {} +}; + +} // namespace test +} // namespace ufo + +#endif // TEST_UFO_PARALLELPOISSONDISKTHINNING_H_ diff --git a/test/ufo/Parameters.h b/test/ufo/Parameters.h index e9e559090..d8ef2b3f1 100644 --- a/test/ufo/Parameters.h +++ b/test/ufo/Parameters.h @@ -87,6 +87,17 @@ void testCorrectValue() { EXPECT_EQUAL(params.optVariableParameter.value().get().channels(), (std::vector{1, 5})); } +void testSimpleString() { + MyParameters params; + const eckit::LocalConfiguration fullConf(::test::TestEnvironment::config(), "simple_string"); + EXPECT_NO_THROW(params.validate(fullConf)); + params.deserialize(fullConf); + + EXPECT(params.optVariableParameter.value() != boost::none); + EXPECT_EQUAL(params.optVariableParameter.value().get().group(), "MetaData"); + EXPECT_EQUAL(params.optVariableParameter.value().get().variable(), "latitude"); +} + void testNoChannels() { MyParameters params; const eckit::LocalConfiguration fullConf(::test::TestEnvironment::config(), "no_channels"); @@ -117,7 +128,7 @@ void testMissingName() { const eckit::LocalConfiguration conf(::test::TestEnvironment::config(), "missing_name"); if (validationSupported) EXPECT_THROWS(params.validate(conf)); - EXPECT_THROWS_AS(params.deserialize(conf), eckit::BadParameter); + EXPECT_THROWS_MSG(params.deserialize(conf), "ConfigurationNotFound: [name]"); } void testMisspelledProperty() { @@ -152,6 +163,9 @@ class Parameters : public oops::Test { ts.emplace_back(CASE("ufo/Parameters/correctValue") { testCorrectValue(); }); + ts.emplace_back(CASE("ufo/Parameters/testSimpleString") { + testSimpleString(); + }); ts.emplace_back(CASE("ufo/Parameters/noChannels") { testNoChannels(); }); diff --git a/test/ufo/PoissonDiskThinning.h b/test/ufo/PoissonDiskThinning.h index e837b8141..1f30fd967 100644 --- a/test/ufo/PoissonDiskThinning.h +++ b/test/ufo/PoissonDiskThinning.h @@ -52,6 +52,11 @@ void testPoissonDiskThinning(const eckit::LocalConfiguration &conf, obsspace.put_db("MetaData", "category", categories); } + if (conf.has("string_category")) { + const std::vector categories = conf.getStringVector("string_category"); + obsspace.put_db("MetaData", "string_category", categories); + } + if (conf.has("priority")) { const std::vector priorities = conf.getIntVector("priority"); obsspace.put_db("MetaData", "priority", priorities); @@ -62,8 +67,10 @@ void testPoissonDiskThinning(const eckit::LocalConfiguration &conf, std::shared_ptr> qcflags(new ioda::ObsDataVector( obsspace, obsspace.obsvariables())); - const eckit::LocalConfiguration filterConf(conf, "Poisson Disk Thinning"); - ufo::PoissonDiskThinning filter(obsspace, filterConf, qcflags, obserr); + eckit::LocalConfiguration filterConf(conf, "Poisson Disk Thinning"); + ufo::PoissonDiskThinningParameters filterParameters; + filterParameters.validateAndDeserialize(filterConf); + ufo::PoissonDiskThinning filter(obsspace, filterParameters, qcflags, obserr); if (expectValidationError) { EXPECT_THROWS(filter.preProcess()); return; @@ -165,9 +172,14 @@ CASE("ufo/PoissonDiskThinning/Priorities") { "Priorities")); } -CASE("ufo/PoissonDiskThinning/Categories") { +CASE("ufo/PoissonDiskThinning/Int-valued categories") { + testPoissonDiskThinning(eckit::LocalConfiguration(::test::TestEnvironment::config(), + "Int-valued categories")); +} + +CASE("ufo/PoissonDiskThinning/String-valued categories") { testPoissonDiskThinning(eckit::LocalConfiguration(::test::TestEnvironment::config(), - "Categories")); + "String-valued categories")); } CASE("ufo/PoissonDiskThinning/Variable min spacings") { diff --git a/test/ufo/Predictor.h b/test/ufo/Predictor.h new file mode 100644 index 000000000..b37c28045 --- /dev/null +++ b/test/ufo/Predictor.h @@ -0,0 +1,175 @@ +/* + * (C) Crown copyright 2021, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef TEST_UFO_PREDICTOR_H_ +#define TEST_UFO_PREDICTOR_H_ + +#include +#include +#include +#include + + +#define ECKIT_TESTING_SELF_REGISTER_CASES 0 + + +#include "eckit/config/LocalConfiguration.h" +#include "eckit/mpi/Comm.h" +#include "eckit/testing/Test.h" +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" +#include "oops/interface/ObsDataVector.h" +#include "oops/interface/ObsOperator.h" +#include "oops/mpi/mpi.h" +#include "oops/runs/Test.h" +#include "oops/util/Expect.h" +#include "oops/util/IntSetParser.h" +#include "test/interface/ObsTestsFixture.h" +#include "ufo/ObsTraits.h" + + +namespace ufo { +namespace test { + +// ----------------------------------------------------------------------------- +void testPredictor() { + typedef ::test::ObsTestsFixture Test_; + typedef oops::ObsDiagnostics ObsDiags_; + + std::vector typeconfs; + ::test::TestEnvironment::config().get("observations", typeconfs); + + for (std::size_t jj = 0; jj < Test_::obspace().size(); ++jj) { + ioda::ObsSpace &ospace = Test_::obspace()[jj].obsspace(); + const eckit::Configuration &conf = typeconfs[jj]; + + // initialize bias correction + eckit::LocalConfiguration bconf(conf, "obs bias"); + ObsBiasParameters bparams; + bparams.validateAndDeserialize(bconf); + const ObsBias ybias(ospace, bparams); + // get predictor names + std::vector predictor_names = ybias.requiredPredictors(); + + // Initialize GeoVaLs + oops::Variables gvars; + gvars += ybias.requiredVars(); + std::unique_ptr gval; + if (gvars.size() > 0) { + // Read GeoVaLs from a file + eckit::LocalConfiguration gconf(conf, "geovals"); + gval.reset(new GeoVaLs(gconf, ospace, gvars)); + } else { + // Create an empty GeoVaLs object + gval.reset(new GeoVaLs(ospace.distribution(), gvars)); + } + + // initialize Obs diagnostics + oops::Variables diagvars; + diagvars += ybias.requiredHdiagnostics(); + std::vector lons(ospace.nlocs()); + std::vector lats(ospace.nlocs()); + std::vector times(ospace.nlocs()); + ospace.get_db("MetaData", "latitude", lats); + ospace.get_db("MetaData", "longitude", lons); + ospace.get_db("MetaData", "datetime", times); + Locations locs(lons, lats, times, ospace.distribution()); + ObsDiagnostics ydiags(ospace, locs, diagvars); + + bool expect_error_message = false; + + // Calculate predictor values + const std::size_t npreds = predictor_names.size(); + EXPECT(npreds > 0); + std::vector predData(npreds, ioda::ObsVector(ospace)); + + const Predictors & predictors = ybias.predictors(); + for (std::size_t p = 0; p < npreds; ++p) { + if (conf.has("expectExceptionWithMessage")) { + const std::string msg = conf.getString("expectExceptionWithMessage"); + EXPECT_THROWS_MSG(predictors[p]->compute(ospace, *gval, ydiags, predData[p]), msg.c_str()); + expect_error_message = true; + break; + } + predictors[p]->compute(ospace, *gval, ydiags, predData[p]); + predData[p].save(predictors[p]->name() + "Predictor"); + } + + if (expect_error_message) { + continue; + } + + // Read in tolerance from yaml + const double tol = conf.getDouble("tolerance"); + + // Get output variable names and read test data + // Note prepend predictor_ to predictor names to distingush + // predictor reference from obsbias reference + std::vector vars; + for (std::size_t jp = 0; jp < npreds; ++jp) { + vars.push_back("predictor_" + predictor_names[jp]); + } + const oops::Variables testvars = ospace.obsvariables(); + const std::size_t nvars = testvars.size(); + if (!testvars.channels().empty()) { + // At present we can label predictors with either the channel number or the variable + // name, but not both. So make sure there's only one multi-channel variable. + ASSERT(nvars == testvars.channels().size()); + } + + /// For each predictor for each variable compare computed predictor values to reference + std::vector testData(ospace.nlocs()); + for (std::size_t jp = 0; jp < npreds; ++jp) { + const std::size_t nlocs = predData[jp].nlocs(); + for (std::size_t jv = 0; jv < nvars; ++jv) { + std::string refVarName = "predictor_" + predictor_names[jp] + '_'; + if (testvars.channels().empty()) + refVarName += testvars[jv]; + else + refVarName += std::to_string(testvars.channels()[jv]); + + ospace.get_db("TestReference", refVarName, testData); + + // compare test and reference vectors + double rms = 0.0; + for (size_t jl = 0; jl < nlocs; jl++) { + testData[jl] -= predData[jp][jl*nvars + jv]; + rms += testData[jl] * testData[jl]; + } + rms = std::sqrt(rms / nlocs); + + oops::Log::debug() << "Vector difference between reference and computed for " + << refVarName << ": " << testData << std::endl; + EXPECT(rms <= tol); + } + } + } +} + +class Predictor : public oops::Test { + public: + Predictor() {} + virtual ~Predictor() {} + private: + std::string testid() const override {return "ufo::test::Predictor";} + + void register_tests() const override { + std::vector& ts = eckit::testing::specification(); + + ts.emplace_back(CASE("ufo/Predictor/testPredictor") + { testPredictor(); }); + } + + void clear() const override {} +}; + +// ----------------------------------------------------------------------------- + +} // namespace test +} // namespace ufo + +#endif // TEST_UFO_PREDICTOR_H_ diff --git a/test/ufo/PrimitiveVariables.h b/test/ufo/PrimitiveVariables.h new file mode 100644 index 000000000..4a1868b99 --- /dev/null +++ b/test/ufo/PrimitiveVariables.h @@ -0,0 +1,169 @@ +/* + * (C) Copyright 2021 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef TEST_UFO_PRIMITIVEVARIABLES_H_ +#define TEST_UFO_PRIMITIVEVARIABLES_H_ + +#include +#include +#include +#include + +#define ECKIT_TESTING_SELF_REGISTER_CASES 0 + +#include "eckit/config/LocalConfiguration.h" +#include "eckit/testing/Test.h" +#include "ioda/ObsSpace.h" +#include "oops/mpi/mpi.h" +#include "oops/runs/Test.h" +#include "oops/util/Expect.h" +#include "oops/util/FloatCompare.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" +#include "test/TestEnvironment.h" +#include "ufo/filters/ObsFilterData.h" +#include "ufo/filters/Variables.h" +#include "ufo/utils/parameters/ParameterTraitsVariable.h" +#include "ufo/utils/PrimitiveVariables.h" + +namespace eckit +{ + // Don't use the contracted output for floats: the current implementation works only + // with integer types. + template <> struct VectorPrintSelector { typedef VectorPrintSimple selector; }; +} // namespace eckit + +namespace ufo { +namespace test { + +class TestCaseParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(TestCaseParameters, Parameters); + public: + oops::RequiredParameter> variables{"variables", this}; + oops::RequiredParameter> expectedNames{"expected names", this}; + oops::RequiredParameter> expectedGroups{"expected groups", this}; + oops::RequiredParameter>> expectedValues{"expected values", this}; +}; + +/// Code shared by all tests +class TestFixture : private boost::noncopyable { + public: + static const ioda::ObsSpace & obsspace() { return *getInstance().obsspace_; } + static const ObsFilterData & data() { return *getInstance().data_; } + + private: + static TestFixture & getInstance() { + static TestFixture theTestFixture; + return theTestFixture; + } + + TestFixture() { + const eckit::Configuration & conf = ::test::TestEnvironment::config(); + util::DateTime bgn(conf.getString("window begin")); + util::DateTime end(conf.getString("window end")); + const eckit::LocalConfiguration obsconf(conf, "obs space"); + obsspace_.reset(new ioda::ObsSpace(obsconf, oops::mpi::world(), bgn, end, oops::mpi::myself())); + data_.reset(new ObsFilterData(*obsspace_)); + } + + std::shared_ptr obsspace_; + std::unique_ptr data_; +}; + +/// Test a range-based for loop over the PrimitiveVariables range. +void testPrimitiveVariables(const eckit::LocalConfiguration &conf) { + TestCaseParameters parameters; + parameters.validateAndDeserialize(conf); + + Variables variables; + for (const Variable &variable : parameters.variables.value()) + variables += variable; + + std::vector names; + std::vector groups; + std::vector primitiveVariables; + std::vector> values; + + for (PrimitiveVariable pv : PrimitiveVariables(variables, TestFixture::data())) { + names.push_back(pv.name()); + groups.push_back(pv.group()); + primitiveVariables.push_back(pv.variable()); + values.push_back(pv.values()); + } + + EXPECT_EQUAL(names, parameters.expectedNames.value()); + EXPECT_EQUAL(groups, parameters.expectedGroups.value()); + EXPECT_EQUAL(values.size(), parameters.expectedValues.value().size()); + for (size_t i = 0; i < values.size(); ++i) + EXPECT(oops::are_all_close_relative(values[i], parameters.expectedValues.value()[i], 1e-6f)); + + EXPECT(std::equal(primitiveVariables.begin(), primitiveVariables.end(), names.begin(), + [](const Variable &primitiveVariable, const std::string &name) + {return primitiveVariable.variable() == name; })); + EXPECT(std::equal(primitiveVariables.begin(), primitiveVariables.end(), groups.begin(), + [](const Variable &primitiveVariable, const std::string &group) + {return primitiveVariable.group() == group; })); +} + +/// Test explicitly that the * and -> operator overloads in PrimitiveVariablesIterator work +/// correctly. +void testPrimitiveVariablesIterator(const eckit::LocalConfiguration &conf) { + TestCaseParameters parameters; + parameters.validateAndDeserialize(conf); + + Variables variables; + for (const Variable &variable : parameters.variables.value()) + variables += variable; + + std::vector names; + std::vector> values; + + { + PrimitiveVariables pvars(variables, TestFixture::data()); + for (PrimitiveVariablesIterator it = pvars.begin(); it != pvars.end(); ++it) { + // Use the * operator to get access to the variable name and the -> operator to get access + // to its values. + names.push_back((*it).name()); + values.push_back(it->values()); + } + } + + EXPECT_EQUAL(names, parameters.expectedNames.value()); + EXPECT_EQUAL(values.size(), parameters.expectedValues.value().size()); + for (size_t i = 0; i < values.size(); ++i) + EXPECT(oops::are_all_close_relative(values[i], parameters.expectedValues.value()[i], 1e-6f)); +} + +class PrimitiveVariables : public oops::Test { + private: + std::string testid() const override {return "ufo::test::PrimitiveVariables";} + + void register_tests() const override { + std::vector& ts = eckit::testing::specification(); + + const eckit::LocalConfiguration conf(::test::TestEnvironment::config(), "cases"); + for (const std::string & testCaseName : conf.keys()) + { + const eckit::LocalConfiguration testCaseConf(conf, testCaseName); + ts.emplace_back(CASE("ufo/PrimitiveVariables/range/" + testCaseName, testCaseConf) + { + testPrimitiveVariables(testCaseConf); + }); + ts.emplace_back(CASE("ufo/PrimitiveVariables/iterator/" + testCaseName, testCaseConf) + { + testPrimitiveVariablesIterator(testCaseConf); + }); + } + } + + void clear() const override {} +}; + +} // namespace test +} // namespace ufo + +#endif // TEST_UFO_PRIMITIVEVARIABLES_H_ diff --git a/test/ufo/ProcessWhere.h b/test/ufo/ProcessWhere.h index f53dec706..4cfb90778 100644 --- a/test/ufo/ProcessWhere.h +++ b/test/ufo/ProcessWhere.h @@ -24,9 +24,19 @@ #include "ufo/filters/processWhere.h" #include "ufo/filters/Variables.h" +#include "oops/util/parameters/Parameter.h" +#include "oops/util/parameters/Parameters.h" +#include "oops/util/parameters/RequiredParameter.h" + namespace ufo { namespace test { +class TestParameters : public oops::Parameters { + OOPS_CONCRETE_PARAMETERS(TestParameters, Parameters); + public: + oops::Parameter> where{"where", {}, this}; + oops::RequiredParameter sizeWhereTrue{"size where true", this}; +}; // ----------------------------------------------------------------------------- @@ -40,18 +50,20 @@ void testProcessWhere(const eckit::LocalConfiguration &conf, ioda::ObsSpace ospace(obsconf, oops::mpi::world(), bgn, end, oops::mpi::myself()); ObsFilterData data(ospace); - const int nlocs = obsconf.getInt("nlocs"); + const int nlocs = conf.getInt("nlocs"); EXPECT(data.nlocs() == nlocs); std::vector confs; conf.get("ProcessWhere", confs); for (size_t jconf = 0; jconf < confs.size(); ++jconf) { eckit::LocalConfiguration config = confs[jconf]; + TestParameters params; + params.validateAndDeserialize(config); if (is_in_usererror) { - EXPECT_THROWS(processWhere(config, data)); + EXPECT_THROWS(processWhere(params.where, data)); } else { - std::vector result = processWhere(config, data); - const int size_ref = config.getInt("size where true"); + std::vector result = processWhere(params.where, data); + const int size_ref = params.sizeWhereTrue; const int size = std::count(result.begin(), result.end(), true); oops::Log::info() << "reference: " << size_ref << ", compare with " << size << std::endl; EXPECT(size == size_ref); diff --git a/test/ufo/ProfileConsistencyChecks.h b/test/ufo/ProfileConsistencyChecks.h index 550a4d5a6..4b3c67c7b 100644 --- a/test/ufo/ProfileConsistencyChecks.h +++ b/test/ufo/ProfileConsistencyChecks.h @@ -10,7 +10,9 @@ #include #include +#include #include +#include #include #define ECKIT_TESTING_SELF_REGISTER_CASES 0 @@ -22,13 +24,17 @@ #include "oops/mpi/mpi.h" #include "oops/runs/Test.h" #include "oops/util/Expect.h" +#include "oops/util/FloatCompare.h" #include "test/TestEnvironment.h" +#include "ufo/filters/ObsFilterData.h" #include "ufo/filters/ProfileConsistencyCheckParameters.h" #include "ufo/filters/ProfileConsistencyChecks.h" #include "ufo/filters/Variables.h" +#include "ufo/GeoVaLs.h" #include "ufo/ObsDiagnostics.h" #include "ufo/profile/EntireSampleDataHandler.h" +#include "ufo/profile/ModelHeightCalculator.h" #include "ufo/profile/ProfileCheckBackgroundGeopotentialHeight.h" #include "ufo/profile/ProfileCheckBackgroundRelativeHumidity.h" #include "ufo/profile/ProfileCheckBackgroundTemperature.h" @@ -36,7 +42,12 @@ #include "ufo/profile/ProfileCheckBase.h" #include "ufo/profile/ProfileCheckTime.h" #include "ufo/profile/ProfileCheckUInterp.h" + +#include "ufo/profile/ProfileCheckValidator.h" #include "ufo/profile/ProfileDataHandler.h" +#include "ufo/profile/ProfileDataHolder.h" +#include "ufo/profile/ProfileVerticalAveraging.h" +#include "ufo/profile/ProfileVerticalInterpolation.h" #include "ufo/profile/VariableNames.h" #include "ufo/utils/metoffice/MetOfficeQCFlags.h" @@ -51,6 +62,10 @@ void testProfileConsistencyChecks(const eckit::LocalConfiguration &conf) { const eckit::LocalConfiguration obsSpaceConf(conf, "obs space"); ioda::ObsSpace obsspace(obsSpaceConf, oops::mpi::world(), bgn, end, oops::mpi::myself()); + const Variables filtervars = Variables(obsspace.obsvariables()); + + ObsFilterData filterdata(obsspace); + ioda::ObsVector hofx(obsspace); const eckit::LocalConfiguration obsdiagconf(conf, "obs diagnostics"); @@ -66,22 +81,39 @@ void testProfileConsistencyChecks(const eckit::LocalConfiguration &conf) { obsspace, obsspace.obsvariables())); const eckit::LocalConfiguration filterConf(conf, "ProfileConsistencyChecks"); + ufo::ProfileConsistencyCheckParameters filterParameters; + filterParameters.validateAndDeserialize(filterConf); // Determine whether an exception is expected to be thrown. - // Exceptions can be thrown in two places: on instantiation of the filter, - // and during the operation of the filter. + // Exceptions can be thrown in the following places: + // - on instantiation of the filter, + // - during the operation of the filter, bool expectThrowOnInstantiation = conf.getBool("ExpectThrowOnInstantiation", false); bool expectThrowDuringOperation = conf.getBool("ExpectThrowDuringOperation", false); if (expectThrowOnInstantiation) { - EXPECT_THROWS(ufo::ProfileConsistencyChecks filterThrow(obsspace, filterConf, qcflags, obserr)); + EXPECT_THROWS(ufo::ProfileConsistencyChecks filterThrow(obsspace, filterParameters, + qcflags, obserr)); // Do not proceed further in this case. return; } - ufo::ProfileConsistencyChecks filter(obsspace, filterConf, qcflags, obserr); - filter.preProcess(); + // Instantiate filter. + ufo::ProfileConsistencyChecks filter(obsspace, filterParameters, qcflags, obserr); + + // Obtain GeoVaLs. + const bool ignoreGeoVaLs = conf.getBool("IgnoreGeoVaLs", false); + const auto geovars = filter.requiredVars(); + std::unique_ptr geovals; + if (!ignoreGeoVaLs && geovars.size() > 0) { + const eckit::LocalConfiguration geovalsConf(conf, "geovals"); + geovals.reset(new GeoVaLs(geovalsConf, obsspace, geovars)); + } else { + geovals.reset(new GeoVaLs(obsspace.distribution(), oops::Variables())); + } + filter.preProcess(); + filter.priorFilter(*geovals); if (expectThrowDuringOperation) EXPECT_THROWS(filter.postFilter(hofx, obsdiags)); else @@ -90,7 +122,7 @@ void testProfileConsistencyChecks(const eckit::LocalConfiguration &conf) { // Determine whether the mismatch check should be bypassed or not. // It might be necessary to disable the mismatch check in tests which are // designed to reach code paths that would normally result in failure. - bool bypassMismatchComparison = conf.getBool("BypassMismatchComparison", false); + const bool bypassMismatchComparison = conf.getBool("BypassMismatchComparison", false); // Check there are no mismatches between the values produced by this code and the OPS equivalents if (!bypassMismatchComparison) { @@ -101,32 +133,30 @@ void testProfileConsistencyChecks(const eckit::LocalConfiguration &conf) { // === Additional tests of exceptions === // // Test whether adding the same check twice throws an exception. - bool addDuplicateCheck = conf.getBool("AddDuplicateCheck", false); + const bool addDuplicateCheck = conf.getBool("AddDuplicateCheck", false); if (addDuplicateCheck) { static ProfileCheckMaker makerDuplicate1_("duplicate"); EXPECT_THROWS(static ProfileCheckMaker makerDuplicate2_("duplicate")); } // Test whether using the get function with the wrong type throws an exception. - bool getWrongType = conf.getBool("GetWrongType", false); + const bool getWrongType = conf.getBool("GetWrongType", false); if (getWrongType) { - std::unique_ptr options_; - options_.reset(new ProfileConsistencyCheckParameters()); - options_->deserialize(conf); + ProfileConsistencyCheckParameters options; + options.deserialize(conf); EntireSampleDataHandler entireSampleDataHandler(obsspace, - *options_); + options.DHParameters); // Load data from obsspace entireSampleDataHandler.get(ufo::VariableNames::obs_air_pressure); // Attempt to access data with incorrect type EXPECT_THROWS(entireSampleDataHandler.get(ufo::VariableNames::obs_air_pressure)); std::vector apply(obsspace.nlocs(), true); - ProfileIndices profileIndices(obsspace, - *options_, - apply); - ProfileDataHandler profileDataHandler(obsspace, - *options_, - entireSampleDataHandler, - profileIndices); + std::vector> flagged; + ProfileDataHandler profileDataHandler(filterdata, + options.DHParameters, + apply, + filtervars, + flagged); // Obtain profile data profileDataHandler.get(ufo::VariableNames::obs_air_pressure); // Attempt to access data with incorrect type @@ -134,29 +164,25 @@ void testProfileConsistencyChecks(const eckit::LocalConfiguration &conf) { } // Manually modify QC flags in order to cover rare code paths. - bool ManualFlagModification = conf.getBool("ManualFlagModification", false); + const bool ManualFlagModification = conf.getBool("ManualFlagModification", false); if (ManualFlagModification) { - std::unique_ptr options_; - options_.reset(new ProfileConsistencyCheckParameters()); - options_->deserialize(conf); + ProfileConsistencyCheckParameters options; + options.deserialize(conf); EntireSampleDataHandler entireSampleDataHandler(obsspace, - *options_); + options.DHParameters); // Load data from obsspace entireSampleDataHandler.get(ufo::VariableNames::obs_air_pressure); entireSampleDataHandler.get(ufo::VariableNames::qcflags_air_temperature); std::vector apply(obsspace.nlocs(), true); - ProfileIndices profileIndices(obsspace, - *options_, - apply); - ProfileDataHandler profileDataHandler(obsspace, - *options_, - entireSampleDataHandler, - profileIndices); - ProfileCheckValidator profileCheckValidator(*options_, - profileDataHandler); - - profileIndices.determineProfileIndices(); - profileDataHandler.reset(); + std::vector> flagged; + ProfileDataHandler profileDataHandler(filterdata, + options.DHParameters, + apply, + filtervars, + flagged); + ProfileCheckValidator profileCheckValidator(options); + + profileDataHandler.initialiseNextProfile(); // Obtain profile data profileDataHandler.get(ufo::VariableNames::obs_air_pressure); @@ -185,38 +211,229 @@ void testProfileConsistencyChecks(const eckit::LocalConfiguration &conf) { zFlags[0] |= ufo::MetOfficeQCFlags::Profile::HydrostaticFlag; // Create checks - ProfileCheckTime profileCheckTime(*options_, - profileIndices, - profileDataHandler, - profileCheckValidator); - ProfileCheckBackgroundTemperature profileCheckBackgroundT(*options_, - profileIndices, - profileDataHandler, - profileCheckValidator); - ProfileCheckBackgroundRelativeHumidity profileCheckBackgroundRH(*options_, - profileIndices, - profileDataHandler, - profileCheckValidator); - ProfileCheckBackgroundWindSpeed profileCheckBackgroundUV(*options_, - profileIndices, - profileDataHandler, - profileCheckValidator); - ProfileCheckBackgroundGeopotentialHeight profileCheckBackgroundZ(*options_, - profileIndices, - profileDataHandler, - profileCheckValidator); + ProfileCheckTime profileCheckTime(options); + ProfileCheckBackgroundTemperature profileCheckBackgroundT(options); + ProfileCheckBackgroundRelativeHumidity profileCheckBackgroundRH(options); + ProfileCheckBackgroundWindSpeed profileCheckBackgroundUV(options); + ProfileCheckBackgroundGeopotentialHeight profileCheckBackgroundZ(options); // Run time check - profileCheckTime.runCheck(); + profileCheckTime.runCheck(profileDataHandler); // Modify time flag timeFlags[0] = true; // Run remaining checks - profileCheckBackgroundT.runCheck(); - profileCheckBackgroundRH.runCheck(); - profileCheckBackgroundUV.runCheck(); - profileCheckBackgroundZ.runCheck(); + profileCheckBackgroundT.runCheck(profileDataHandler); + profileCheckBackgroundRH.runCheck(profileDataHandler); + profileCheckBackgroundUV.runCheck(profileDataHandler); + profileCheckBackgroundZ.runCheck(profileDataHandler); + } + + // Test the profile vertical interpolation + const bool testProfileVerticalInterpolation = + conf.getBool("testProfileVerticalInterpolation", false); + if (testProfileVerticalInterpolation) { + ProfileConsistencyCheckParameters options; + options.deserialize(conf); + + std::vector apply(obsspace.nlocs(), true); + std::vector> flagged; + ProfileDataHandler profileDataHandler(filterdata, + options.DHParameters, + apply, + filtervars, + flagged); + + // Get interpolation options for each profile. + const auto interpMethodNames = conf.getStringVector("interpMethodNames"); + const auto coordOrderNames = conf.getStringVector("coordOrderNames"); + const auto outOfBoundsNames = conf.getStringVector("outOfBoundsNames"); + + for (size_t jprof = 0; jprof < obsspace.nrecs(); ++jprof) { + profileDataHandler.initialiseNextProfile(); + + const std::string interpMethodName = interpMethodNames[jprof]; + const std::string coordOrderName = coordOrderNames[jprof]; + const std::string outOfBoundsName = outOfBoundsNames[jprof]; + + // Calculate level heights for GeoVaLs. + std::vector orogGeoVaLs(obsspace.nlocs(), 0.0); + geovals->get(orogGeoVaLs, ufo::VariableNames::geovals_orog, 1); + std::vector zRhoGeoVaLs; + std::vector zThetaGeoVaLs; + ufo::CalculateModelHeight(options.DHParameters.ModParameters, + orogGeoVaLs[0], + zRhoGeoVaLs, + zThetaGeoVaLs); + + // Reverse coordinate order if required. + if (coordOrderName == "Descending") + std::reverse(zRhoGeoVaLs.begin(), zRhoGeoVaLs.end()); + + // Create column of pressure GeoVaLs. + std::vector pressureGeoVaLs(obsspace.nlocs(), 0.0); + const size_t gvnlevs = geovals->nlevs(ufo::VariableNames::geovals_pressure); + std::vector pressureGeoVaLs_column; + for (int jlev = 1; jlev < gvnlevs + 1; ++jlev) { + geovals->get(pressureGeoVaLs, ufo::VariableNames::geovals_pressure, jlev); + pressureGeoVaLs_column.push_back(pressureGeoVaLs[0]); + } + + // Get observed geopotential height and (empty) pressure vector. + const auto &zObs = profileDataHandler.get(ufo::VariableNames::obs_geopotential_height); + auto &pressures = profileDataHandler.get(ufo::VariableNames::obs_air_pressure); + + auto interpMethod = ProfileInterpolation::InterpolationMethod::Linear; + if (interpMethodName == "LogLinear") + interpMethod = ProfileInterpolation::InterpolationMethod::LogLinear; + auto coordOrder = ProfileInterpolation::CoordinateOrder::Ascending; + if (coordOrderName == "Descending") + coordOrder = ProfileInterpolation::CoordinateOrder::Descending; + auto outOfBounds = ProfileInterpolation::OutOfBoundsTreatment::SetToBound; + if (outOfBoundsName == "SetMissing") + outOfBounds = ProfileInterpolation::OutOfBoundsTreatment::SetMissing; + if (outOfBoundsName == "Extrapolate") + outOfBounds = ProfileInterpolation::OutOfBoundsTreatment::Extrapolate; + + // Interpolate to determine pressure. + profileVerticalInterpolation(zRhoGeoVaLs, + pressureGeoVaLs_column, + zObs, + pressures, + interpMethod, + coordOrder, + outOfBounds); + + // Compare each value of pressure. + const std::string expectedPressureName = "OPS_" + + static_cast(ufo::VariableNames::obs_air_pressure); + const auto &expected_pressures = profileDataHandler.get(expectedPressureName); + for (size_t jlev = 0; jlev < pressures.size(); ++jlev) + EXPECT(oops::is_close_relative(pressures[jlev], expected_pressures[jlev], 1e-5f)); + } + } + + // Test the profile vertical averaging. + const bool testProfileVerticalAveraging = + conf.getBool("testProfileVerticalAveraging", false); + if (testProfileVerticalAveraging) { + ProfileConsistencyCheckParameters options; + options.deserialize(conf); + + std::vector apply(obsspace.nlocs(), true); + std::vector> flagged; + ProfileDataHandler profileDataHandler(filterdata, + options.DHParameters, + apply, + filtervars, + flagged); + + for (size_t jprof = 0; jprof < obsspace.nrecs(); ++jprof) { + profileDataHandler.initialiseNextProfile(); + + const auto &flagsIn = profileDataHandler.get(ufo::VariableNames::qcflags_eastward_wind); + const auto &valuesIn = profileDataHandler.get(ufo::VariableNames::obs_eastward_wind); + const auto &coordIn = profileDataHandler.get(ufo::VariableNames::LogP_derived); + const auto &bigGap = profileDataHandler.get(ufo::VariableNames::bigPgaps_derived); + const auto &coordOut = profileDataHandler.get + (ufo::VariableNames::modellevels_logPWB_rho_derived); + const float DZFrac = 0.5; + const ProfileAveraging::Method method = + ProfileAveraging::Method::Averaging; + + std::vector flagsOut; + std::vector valuesOut; + int numGaps = 0; + std::vector ZMax; + std::vector ZMin; + ufo::calculateVerticalAverage(flagsIn, + valuesIn, + coordIn, + bigGap, + coordOut, + DZFrac, + method, + flagsOut, + valuesOut, + numGaps, + &ZMax, + &ZMin); + + // Compare output values with OPS equivalents. + // The name of the OPS variables are hardcoded because they are purely used for testing. + // todo(ctgh): check whether any hardcoded names can be substituted. + const auto &expected_flagsOut = + profileDataHandler.get("OPS_eastward_wind@ModelLevelsQCFlags"); + for (size_t jlev = 0; jlev < flagsOut.size(); ++jlev) + EXPECT(flagsOut[jlev] == expected_flagsOut[jlev]); + const auto &expected_valuesOut = + profileDataHandler.get("OPS_eastward_wind@ModelLevelsDerivedValue"); + for (size_t jlev = 0; jlev < flagsOut.size(); ++jlev) + EXPECT(oops::is_close_relative(valuesOut[jlev], expected_valuesOut[jlev], 1e-4f)); + const auto &expected_ZMin = + profileDataHandler.get("OPS_LogP_u_Min@ModelLevelsDerivedValue"); + for (size_t jlev = 0; jlev < ZMin.size(); ++jlev) + EXPECT(oops::is_close_relative(ZMin[jlev], expected_ZMin[jlev], 1e-14f)); + const auto &expected_ZMax = + profileDataHandler.get("OPS_LogP_u_Max@ModelLevelsDerivedValue"); + for (size_t jlev = 0; jlev < ZMax.size(); ++jlev) + EXPECT(oops::is_close_relative(ZMax[jlev], expected_ZMax[jlev], 1e-14f)); + } + } + + // Test the profile data holder. + const bool testProfileDataHolder = + conf.getBool("testProfileDataHolder", false); + if (testProfileDataHolder) { + ProfileConsistencyCheckParameters options; + options.deserialize(conf); + + std::vector apply(obsspace.nlocs(), true); + std::vector> flagged; + ProfileDataHandler profileDataHandler(filterdata, + options.DHParameters, + apply, + filtervars, + flagged); + + profileDataHandler.initialiseNextProfile(); + + // Create a ProfileDataHolder and request some variables. + ProfileDataHolder profileDataHolder(profileDataHandler); + profileDataHolder.fill({ufo::VariableNames::extended_obs_space}, + {ufo::VariableNames::obs_air_pressure}, + {ufo::VariableNames::station_ID}, + {ufo::VariableNames::geovals_pressure}, + {ufo::VariableNames::bkgerr_air_temperature}); + + // Get GeoVaLs + profileDataHolder.getGeoVaLVector(ufo::VariableNames::geovals_pressure); + + // Attempt to access data with incorrect type. + EXPECT_THROWS(profileDataHolder.get(ufo::VariableNames::obs_air_pressure)); + + // Attempt to access nonexistent data. + EXPECT_THROWS(profileDataHolder.get("wrong@MetaData")); + EXPECT_THROWS(profileDataHolder.getGeoVaLVector("wrong@MetaData")); + EXPECT_THROWS(profileDataHolder.getObsDiagVector("wrong@MetaData")); + + // Check this profile has been marked as being in the correct section of the ObsSpace. + profileDataHolder.checkObsSpaceSection(ufo::ObsSpaceSection::Original); + EXPECT_THROWS(profileDataHolder.checkObsSpaceSection(ufo::ObsSpaceSection::Extended)); + + // Advance to the profile in the extended section and perform the same check. + profileDataHandler.initialiseNextProfile(); + ProfileDataHolder profileDataHolderExt(profileDataHandler); + profileDataHolderExt.fill({ufo::VariableNames::extended_obs_space}, {}, {}, {}, {}); + EXPECT_THROWS(profileDataHolderExt.checkObsSpaceSection(ufo::ObsSpaceSection::Original)); + profileDataHolderExt.checkObsSpaceSection(ufo::ObsSpaceSection::Extended); + + // Exercise the set routine + std::vector int1 {1}; + profileDataHolder.set("test", std::move(int1)); + std::vector int2 {1}; + profileDataHolder.set("test", std::move(int2)); } } diff --git a/test/ufo/RecursiveSplitter.h b/test/ufo/RecursiveSplitter.h index 87720b4cc..8e9f08f04 100644 --- a/test/ufo/RecursiveSplitter.h +++ b/test/ufo/RecursiveSplitter.h @@ -8,6 +8,8 @@ #ifndef TEST_UFO_RECURSIVESPLITTER_H_ #define TEST_UFO_RECURSIVESPLITTER_H_ +#include "ufo/utils/RecursiveSplitter.h" + #include #include #include @@ -17,7 +19,6 @@ #include "eckit/testing/Test.h" #include "oops/runs/Test.h" #include "oops/util/Expect.h" -#include "ufo/utils/RecursiveSplitter.h" namespace ufo { namespace test { @@ -47,6 +48,24 @@ Groups getMultiElementGroups(const RecursiveSplitter &splitter) { return groups; } + +void orderedComparison(const RecursiveSplitter &splitter, + const std::vector> &expected) { + int groupInd = -1; + int iterInd = -1; + for (const auto &g : splitter.groups()) { + groupInd++; + iterInd = -1; + for (auto index : g) { + iterInd++; + oops::Log::debug() << "group: " << groupInd << " index: " << index << " expected index: " << + expected[groupInd][iterInd] << std::endl; + EXPECT_EQUAL(index, expected[groupInd][iterInd]); + } + } +} + + CASE("ufo/RecursiveSplitter/ZeroIds") { RecursiveSplitter splitter(0); { @@ -190,6 +209,33 @@ CASE("ufo/RecursiveSplitter/StringCategories") { } } +CASE("ufo/RecursiveSplitter/Recurse") { + std::vector orig{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + RecursiveSplitter splitter(10); + + // First grouping + std::vector categories{1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; + splitter.groupBy(categories); + std::vector> expected{{5, 6, 7, 8, 9}, {0, 1, 2, 3, 4}}; + orderedComparison(splitter, expected); + + // Second grouping + categories = {1, 1, 0, 2, 2, 2, 2, 0, 1, 1}; + splitter.groupBy(categories); + expected = {{7}, {8, 9}, {5, 6}, {2}, {0, 1}, {3, 4}}; + orderedComparison(splitter, expected); + + // Final sorting of groups + categories = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; // effectively reverse ordering of final groups + splitter.sortGroupsBy( + [&categories](auto indexA, auto indexB) { + return categories[indexA] < categories[indexB]; + }); + expected = {{7}, {9, 8}, {6, 5}, {2}, {1, 0}, {4, 3}}; + orderedComparison(splitter, expected); +} + + class RecursiveSplitter : public oops::Test { public: RecursiveSplitter() {} diff --git a/test/ufo/StuckCheck.h b/test/ufo/StuckCheck.h new file mode 100644 index 000000000..33edcaeab --- /dev/null +++ b/test/ufo/StuckCheck.h @@ -0,0 +1,93 @@ +/* + * (C) Copyright 2020 Met Office UK + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#ifndef TEST_UFO_STUCKCHECK_H_ +#define TEST_UFO_STUCKCHECK_H_ + +#include +#include +#include +#include + +#define ECKIT_TESTING_SELF_REGISTER_CASES 0 + +#include "eckit/config/LocalConfiguration.h" +#include "eckit/testing/Test.h" +#include "ioda/ObsSpace.h" +#include "ioda/ObsVector.h" +#include "oops/mpi/mpi.h" +#include "oops/runs/Test.h" +#include "oops/util/Expect.h" +#include "test/TestEnvironment.h" +#include "ufo/filters/StuckCheck.h" +#include "ufo/filters/Variables.h" + +namespace ufo { +namespace test { + +void testStuckCheck(const eckit::LocalConfiguration &conf) { + util::DateTime bgn(conf.getString("window begin")); + util::DateTime end(conf.getString("window end")); + + const eckit::LocalConfiguration obsSpaceConf(conf, "obs space"); + ioda::ObsSpace obsspace(obsSpaceConf, oops::mpi::world(), bgn, end, oops::mpi::myself()); + + if (conf.has("air_temperatures")) { + const std::vector airTemperatures = conf.getFloatVector("air_temperatures"); + obsspace.put_db("ObsValue", "air_temperature", airTemperatures); + } + + if (conf.has("air_pressures")) { + const std::vector airPressures = conf.getFloatVector("air_pressures"); + obsspace.put_db("ObsValue", "air_pressure", airPressures); + } + + std::shared_ptr> obserr(new ioda::ObsDataVector( + obsspace, obsspace.obsvariables(), "ObsError")); + std::shared_ptr> qcflags(new ioda::ObsDataVector( + obsspace, obsspace.obsvariables())); + + eckit::LocalConfiguration filterConf(conf, "Stuck Check"); + ufo::StuckCheckParameters filterParameters; + filterParameters.validateAndDeserialize(filterConf); + ufo::StuckCheck filter(obsspace, filterParameters, qcflags, obserr); + filter.preProcess(); + + const std::vector expectedRejectedObsIndices = + conf.getUnsignedVector("expected_rejected_obs_indices"); + std::vector rejectedObsIndices; + for (size_t i = 0; i < qcflags->nlocs(); ++i) + if ((*qcflags)[0][i] == ufo::QCflags::track) + rejectedObsIndices.push_back(i); + EXPECT_EQUAL(rejectedObsIndices, expectedRejectedObsIndices); +} + +class StuckCheck : public oops::Test { + private: + std::string testid() const override {return "ufo::test::StuckCheck";} + + void register_tests() const override { + std::vector& ts = eckit::testing::specification(); + + const eckit::LocalConfiguration conf(::test::TestEnvironment::config()); + for (const std::string & testCaseName : conf.keys()) + { + const eckit::LocalConfiguration testCaseConf(::test::TestEnvironment::config(), testCaseName); + ts.emplace_back(CASE("ufo/StuckCheck/" + testCaseName, testCaseConf) + { + testStuckCheck(testCaseConf); + }); + } + } + + void clear() const override {} +}; + +} // namespace test +} // namespace ufo + +#endif // TEST_UFO_STUCKCHECK_H_ diff --git a/test/ufo/TemporalThinning.h b/test/ufo/TemporalThinning.h index d3c86ee28..1a0a2d149 100644 --- a/test/ufo/TemporalThinning.h +++ b/test/ufo/TemporalThinning.h @@ -41,6 +41,11 @@ void testTemporalThinning(const eckit::LocalConfiguration &conf) { obsspace.put_db("MetaData", "category", categories); } + if (conf.has("string_category")) { + const std::vector categories = conf.getStringVector("string_category"); + obsspace.put_db("MetaData", "string_category", categories); + } + if (conf.has("priority")) { const std::vector priorities = conf.getIntVector("priority"); obsspace.put_db("MetaData", "priority", priorities); @@ -51,8 +56,10 @@ void testTemporalThinning(const eckit::LocalConfiguration &conf) { std::shared_ptr> qcflags(new ioda::ObsDataVector( obsspace, obsspace.obsvariables())); - const eckit::LocalConfiguration filterConf(conf, "TemporalThinning"); - ufo::TemporalThinning filter(obsspace, filterConf, qcflags, obserr); + eckit::LocalConfiguration filterConf(conf, "TemporalThinning"); + ufo::TemporalThinningParameters filterParameters; + filterParameters.validateAndDeserialize(filterConf); + ufo::TemporalThinning filter(obsspace, filterParameters, qcflags, obserr); filter.preProcess(); const std::vector expectedThinnedObsIndices = diff --git a/test/ufo/TrackCheck.h b/test/ufo/TrackCheck.h index 63280dd01..d23889151 100644 --- a/test/ufo/TrackCheck.h +++ b/test/ufo/TrackCheck.h @@ -53,8 +53,10 @@ void testTrackCheck(const eckit::LocalConfiguration &conf) { std::shared_ptr> qcflags(new ioda::ObsDataVector( obsspace, obsspace.obsvariables())); - const eckit::LocalConfiguration filterConf(conf, "Track Check"); - ufo::TrackCheck filter(obsspace, filterConf, qcflags, obserr); + eckit::LocalConfiguration filterConf(conf, "Track Check"); + ufo::TrackCheckParameters filterParameters; + filterParameters.validateAndDeserialize(filterConf); + ufo::TrackCheck filter(obsspace, filterParameters, qcflags, obserr); filter.preProcess(); const std::vector expectedRejectedObsIndices = diff --git a/test/ufo/TrackCheckShip.h b/test/ufo/TrackCheckShip.h index c5965ddda..4ce397b05 100644 --- a/test/ufo/TrackCheckShip.h +++ b/test/ufo/TrackCheckShip.h @@ -13,6 +13,9 @@ #include #include +#include +#include + #define ECKIT_TESTING_SELF_REGISTER_CASES 0 #include "eckit/config/LocalConfiguration.h" @@ -31,7 +34,8 @@ namespace ufo { namespace test { -const ufo::TrackCheckShipDiagnostics setupRunFilter(const eckit::LocalConfiguration &conf) { +const boost::optional setupRunFilter( + const eckit::LocalConfiguration &conf, std::vector *rejectedObsIndices = nullptr) { util::DateTime bgn(conf.getString("window begin")); util::DateTime end(conf.getString("window end")); @@ -48,14 +52,26 @@ const ufo::TrackCheckShipDiagnostics setupRunFilter(const eckit::LocalConfigurat std::shared_ptr> qcflags(new ioda::ObsDataVector( obsspace, obsspace.obsvariables())); - const eckit::LocalConfiguration filterConf(conf, "Ship Track Check"); - ufo::TrackCheckShip filter(obsspace, filterConf, qcflags, obserr); + eckit::LocalConfiguration filterConf(conf, "Ship Track Check"); + ufo::TrackCheckShipParameters filterParameters; + filterParameters.validateAndDeserialize(filterConf); + ufo::TrackCheckShip filter(obsspace, filterParameters, qcflags, obserr); filter.preProcess(); - return *filter.diagnostics(); + if (filterConf.getBool("comparison test", false) && rejectedObsIndices) { + for (size_t i = 0; i < qcflags->nlocs(); ++i) + if ((*qcflags)[0][i] == ufo::QCflags::track) + rejectedObsIndices->push_back(i); + return boost::none; + } else if (filterConf.getBool("unit testing mode", false)) { + return *filter.diagnostics(); + } + return boost::none; } void testTrackCheckShipInitialCalculations(const eckit::LocalConfiguration &conf) { auto diagnostics = setupRunFilter(conf); + if (!diagnostics) + return; const std::vector expectedDistances = conf.getDoubleVector("expected distance"); const std::vector expectedSpeeds = conf.getDoubleVector("expected speed"); const std::vector expectedDistancesAveraged = @@ -66,16 +82,13 @@ void testTrackCheckShipInitialCalculations(const eckit::LocalConfiguration &conf const std::vector expectedShort = conf.getIntVector("expected short"); const std::vector expectedFast = conf.getIntVector("expected fast"); const std::vector expectedBends = conf.getIntVector("expected bends"); - const std::vector expectedDist0 = conf.getIntVector("expected dist0"); - const std::vector expectedSimultaneous = conf.getIntVector("expected simultaneous"); const std::vector expectedSumSpeeds = conf.getDoubleVector("expected sum speed"); const std::vector expectedMeanSpeeds = conf.getDoubleVector("expected mean speed"); std::vector calculatedDistances, calculatedSpeeds, calculatedDistancesAveraged, calculatedSpeedsAveraged, calculatedSumSpeeds, calculatedMeanSpeeds; std::vector calculatedAngles; - std::vector calculatedShort, calculatedFast, calculatedBends, - calculatedDist0, calculatedSimultaneous; - for (auto const& tracks : diagnostics.getInitialCalculationResults()) { + std::vector calculatedShort, calculatedFast, calculatedBends; + for (auto const& tracks : diagnostics->getInitialCalculationResults()) { for (auto const& obsStats : tracks.first) { calculatedDistances.push_back(obsStats.distance); calculatedSpeeds.push_back(obsStats.speed); @@ -86,15 +99,13 @@ void testTrackCheckShipInitialCalculations(const eckit::LocalConfiguration &conf calculatedShort.push_back(tracks.second.numShort_); calculatedFast.push_back(tracks.second.numFast_); calculatedBends.push_back(tracks.second.numBends_); - calculatedDist0.push_back(tracks.second.numDist0_); - calculatedSimultaneous.push_back(tracks.second.numSimultaneous_); calculatedSumSpeeds.push_back(tracks.second.sumSpeed_); calculatedMeanSpeeds.push_back(tracks.second.meanSpeed_); } EXPECT(oops::are_all_close_relative(calculatedDistances, expectedDistances, - .05)); + .05)); EXPECT(oops::are_all_close_relative(calculatedSpeeds, expectedSpeeds, - .05)); + .05)); EXPECT(oops::are_all_close_relative( calculatedDistancesAveraged, expectedDistancesAveraged, .05)); @@ -107,8 +118,6 @@ void testTrackCheckShipInitialCalculations(const eckit::LocalConfiguration &conf EXPECT_EQUAL(calculatedShort, expectedShort); EXPECT_EQUAL(calculatedFast, expectedFast); EXPECT_EQUAL(calculatedBends, expectedBends); - EXPECT_EQUAL(calculatedDist0, expectedDist0); - EXPECT_EQUAL(calculatedSimultaneous, expectedSimultaneous); EXPECT(oops::are_all_close_relative( calculatedSumSpeeds, expectedSumSpeeds, .05)); @@ -124,59 +133,26 @@ void testEarlyBreakCondition(const eckit::LocalConfiguration &conf) { } auto diagnostics = setupRunFilter(conf); + if (!diagnostics) + return; std::vector expectedEarlyBreaks(conf.getIntVector("expected early breaks")); std::vector calculatedEarlyBreaks; - for (auto const& earlyBreakResults : diagnostics.getEarlyBreaks()) { + for (auto const& earlyBreakResults : diagnostics->getEarlyBreaks()) { calculatedEarlyBreaks.push_back(earlyBreakResults); } EXPECT_EQUAL(expectedEarlyBreaks, calculatedEarlyBreaks); } -void testDeferSimultaneous(const eckit::LocalConfiguration &conf) { - const eckit::LocalConfiguration filterConf(conf, "Ship Track Check"); - if (!filterConf.getBool("deferred check simultaneous", false)) { +void testRejectedObservations(const eckit::LocalConfiguration &conf) { + std::vector rejectedObsIndices; + if (setupRunFilter(conf, &rejectedObsIndices)) return; - } - - auto diagnostics = setupRunFilter(conf); - const std::vector expectedDistancesDeferred = conf.getDoubleVector( - "expected deferred distance"); - const std::vector expectedSpeedsDeferred = conf.getDoubleVector( - "expected deferred speed"); - const std::vector expectedDistancesAveragedDeferred = - conf.getDoubleVector("expected deferred distance averaged"); - const std::vector expectedSpeedsAveragedDeferred = - conf.getDoubleVector("expected deferred speed averaged"); - const std::vector expectedAnglesDeferred = conf.getFloatVector("expected deferred angle"); - std::vector calculatedDistancesDeferred, calculatedSpeedsDeferred, - calculatedDistancesAveragedDeferred, - calculatedSpeedsAveragedDeferred; - std::vector calculatedAnglesDeferred; - for (auto const& recalcIteration : (diagnostics.getCalculatedResultsSimultaneousDeferred())) { - for (auto const& obs : recalcIteration) { - calculatedAnglesDeferred.push_back(obs.angle); - calculatedDistancesDeferred.push_back(obs.distance); - calculatedSpeedsDeferred.push_back(obs.speed); - calculatedDistancesAveragedDeferred.push_back(obs.distanceAveraged); - calculatedSpeedsAveragedDeferred.push_back(obs.speedAveraged); - } - } - EXPECT(oops::are_all_close_relative(calculatedDistancesDeferred, expectedDistancesDeferred, - .05)); - EXPECT(oops::are_all_close_relative(calculatedSpeedsDeferred, expectedSpeedsDeferred, - .05)); - EXPECT(oops::are_all_close_relative( - calculatedDistancesAveragedDeferred, expectedDistancesAveragedDeferred, - .05)); - EXPECT(oops::are_all_close_relative( - calculatedSpeedsAveragedDeferred, expectedSpeedsAveragedDeferred, - .05)); - EXPECT(oops::are_all_close_absolute( - calculatedAnglesDeferred, expectedAnglesDeferred, - 5.0f)); + const std::vector expectedRejectedObsIndices = + conf.getUnsignedVector("expected rejected obs indices"); + EXPECT_EQUAL(rejectedObsIndices, expectedRejectedObsIndices); } class TrackCheckShip : public oops::Test { @@ -194,7 +170,7 @@ class TrackCheckShip : public oops::Test { { testTrackCheckShipInitialCalculations(testCaseConf); testEarlyBreakCondition(testCaseConf); - testDeferSimultaneous(testCaseConf); + testRejectedObservations(testCaseConf); }); } } diff --git a/test/ufo/TrackCheckShipMainLoop.h b/test/ufo/TrackCheckShipMainLoop.h index b56a811fc..1fc1a2bbb 100644 --- a/test/ufo/TrackCheckShipMainLoop.h +++ b/test/ufo/TrackCheckShipMainLoop.h @@ -31,8 +31,6 @@ namespace ufo { namespace test { void testFirstRejectionSimultaneousIncluded(const eckit::LocalConfiguration &conf) { - const eckit::LocalConfiguration filterConf(conf, "Ship Track Check"); - util::DateTime bgn(conf.getString("window begin")); util::DateTime end(conf.getString("window end")); @@ -49,7 +47,10 @@ void testFirstRejectionSimultaneousIncluded(const eckit::LocalConfiguration &con std::shared_ptr> qcflags(new ioda::ObsDataVector( obsspace, obsspace.obsvariables())); - ufo::TrackCheckShip filter(obsspace, filterConf, qcflags, obserr); + eckit::LocalConfiguration filterConf(conf, "Ship Track Check"); + ufo::TrackCheckShipParameters filterParameters; + filterParameters.validateAndDeserialize(filterConf); + ufo::TrackCheckShip filter(obsspace, filterParameters, qcflags, obserr); filter.preProcess(); const ufo::TrackCheckShipDiagnostics diagnostics = *filter.diagnostics(); diff --git a/test/ufo/locations_test.F90 b/test/ufo/locations_test.F90 new file mode 100644 index 000000000..bf0604af3 --- /dev/null +++ b/test/ufo/locations_test.F90 @@ -0,0 +1,125 @@ +! +! (C) Copyright 2020 UCAR +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +! +module test_locations + +use iso_c_binding + +implicit none +private + +contains + +! ------------------------------------------------------------------------------ +!> Tests whether latitudes and longitudes from ufo::Locations object +!! are matching references in yaml file +integer function test_locations_lonslats_c(c_locs, c_conf) bind(c,name='test_locations_lonslats_f90') +use fckit_configuration_module, only: fckit_configuration +use fckit_log_module, only: fckit_log +use ufo_locations_mod +implicit none +type(c_ptr), value, intent(in) :: c_locs !< ufo::Locations object +type(c_ptr), value, intent(in) :: c_conf !< test configuration (described in LocationsTestParameters + +!> local variables +type(ufo_locations) :: locs +type(fckit_configuration) :: f_conf +integer :: nlocs, iloc +real :: tolerance = 1.e-8 +real(c_double), dimension(:), allocatable :: lons_ref, lats_ref, lons, lats +character(len=200) :: logmessage + +!> default value: test passed +test_locations_lonslats_c = 1 + +!> read reference longitudes and latitudes +f_conf = fckit_configuration(c_conf) +nlocs = f_conf%get_size("reference lons") +call f_conf%get_or_die("reference lons", lons_ref) +call f_conf%get_or_die("reference lats", lats_ref) + +!> get longitudes and latitudes from locations object +locs = ufo_locations(c_locs) +allocate(lons(nlocs), lats(nlocs)) +call locs%get_lons(lons) +call locs%get_lats(lats) + +do iloc = 1, nlocs + write(logmessage, *) "lons: loc and ref: ", lons(iloc), ", ", lons_ref(iloc) + call fckit_log%debug(logmessage) + write(logmessage, *) "lats: loc and ref: ", lats(iloc), ", ", lats_ref(iloc) + call fckit_log%debug(logmessage) +enddo + +!> compare to reference +if(any(abs(lons-lons_ref) > tolerance)) test_locations_lonslats_c = 0 +if(any(abs(lats-lats_ref) > tolerance)) test_locations_lonslats_c = 0 + +deallocate(lons, lats) + +end function test_locations_lonslats_c + +! ------------------------------------------------------------------------------ + +! ------------------------------------------------------------------------------ +!> Tests whether ufo::Locations time mask method results +!! are matching references in yaml file +integer function test_locations_timemask_c(c_locs, c_conf) bind(c,name='test_locations_timemask_f90') +use fckit_configuration_module, only: fckit_configuration +use fckit_log_module, only: fckit_log +use datetime_mod +use ufo_locations_mod +implicit none +type(c_ptr), value, intent(in) :: c_locs !< ufo::Locations object +type(c_ptr), value, intent(in) :: c_conf !< test configuration (described in LocationsTestParameters + +!> local variables +type(ufo_locations) :: locs +type(fckit_configuration) :: f_conf +type(fckit_configuration), allocatable :: testconfigs(:) +character(kind=c_char,len=:), allocatable :: t1str, t2str +logical(kind=c_bool), allocatable :: mask(:) +logical, allocatable :: mask_ref(:) +type(datetime) :: t1, t2 +integer :: nlocs, iloc, itest +character(len=200) :: logmessage + +!> default value: test passed +test_locations_timemask_c = 1 + +locs = ufo_locations(c_locs) +f_conf = fckit_configuration(c_conf) +call f_conf%get_or_die("time mask tests", testconfigs) +!> loop over all the tests for time masks +do itest = 1, size(testconfigs) + call testconfigs(itest)%get_or_die("t1", t1str) + call testconfigs(itest)%get_or_die("t2", t2str) + call datetime_create(t1str, t1) + call datetime_create(t2str, t2) + call testconfigs(itest)%get_or_die("reference mask", mask_ref) + + !> call ufo::Locations method to compute time mask + nlocs = locs%nlocs() + allocate(mask(nlocs)) + call locs%get_timemask(t1, t2, mask) + + write(logmessage, *) "Test ", itest + call fckit_log%debug(logmessage) + do iloc = 1, nlocs + write(logmessage, *) "mask: loc and ref: ", mask(iloc), ", ", mask_ref(iloc) + call fckit_log%debug(logmessage) + enddo + + !> compare to reference + if(any(mask .neqv. mask_ref)) test_locations_timemask_c = 0 + + deallocate(mask) + +enddo + +end function test_locations_timemask_c + +end module test_locations diff --git a/test/ufo_data_checker.py.in b/test/ufo_data_checker.py.in new file mode 100755 index 000000000..894556957 --- /dev/null +++ b/test/ufo_data_checker.py.in @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 + +import os +import sys + +test_dir = sys.argv[1] +if (not os.path.isdir(test_dir)): + print(test_dir + " does not exist") + exit(1) +else: + print("test data is available") diff --git a/test/ufo_data_downloader.py.in b/test/ufo_data_downloader.py.in new file mode 100755 index 000000000..fdc53961a --- /dev/null +++ b/test/ufo_data_downloader.py.in @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +import os +import sys +import stat +import tarfile +import urllib.request + +bucket_name = "jedi-test-files" + +download_base_url = sys.argv[1] +testfiles_path = sys.argv[2] +testfiles_name = sys.argv[3] +md5check = sys.argv[4] + +def DownloadUntar(download_base_url, testfiles_path, testfiles_name, md5check): + if (md5check == "1"): + urllib.request.urlretrieve( download_base_url+"/"+testfiles_name+".md5", testfiles_path+"/"+testfiles_name+".md5") + + urllib.request.urlretrieve( download_base_url+"/"+testfiles_name, testfiles_path+"/"+testfiles_name) + tar_file = tarfile.open(testfiles_path+"/"+testfiles_name) + tar_file.extractall(testfiles_path) + tar_file.close() + +if (md5check == "1") : + # if .tar.gz and .tar.gz.md5 exist + # then download s3 md5 + # and compare with local md5 + if os.path.isfile(testfiles_path+"/"+testfiles_name) and os.path.isfile(testfiles_path+"/"+testfiles_name+".md5") : + print("local files found") + + # dl md5 save it as *.md5.dl + urllib.request.urlretrieve( download_base_url+"/"+testfiles_name+".md5", testfiles_path+"/"+testfiles_name+".md5.dl") + + # compare *md5.dl with md5 local + with open(testfiles_path+"/"+testfiles_name+".md5", 'r') as f: + md5_local = f.read() + with open(testfiles_path+"/"+testfiles_name+".md5.dl", 'r') as f: + md5_dl = f.read() + if md5_local == md5_dl : + print("no update in dataset") + else: + print("update found; download new dataset") + DownloadUntar(download_base_url, testfiles_path, testfiles_name, md5check) + else: + print("local file not found; download from host") + print("downloading "+ download_base_url+"/"+testfiles_name) + DownloadUntar(download_base_url, testfiles_path, testfiles_name, md5check) +else: + # downloading release data from DASH + if os.path.isfile(testfiles_path+"/"+testfiles_name): + print("local RELEASE file found") + else: + print ("downloading RELEASE data from "+download_base_url+"/"+testfiles_name) + DownloadUntar(download_base_url, testfiles_path, testfiles_name, md5check) diff --git a/test/ufo_ioda_data_downloader.py b/test/ufo_ioda_data_downloader.py deleted file mode 100755 index 7898fefd7..000000000 --- a/test/ufo_ioda_data_downloader.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys -import stat -import tarfile -import urllib.request - -bucket_name = "jedi-test-files" - -download_file_name = sys.argv[1] -testfiles_name = sys.argv[2] -testfiles_path = sys.argv[3] -download_base_url = sys.argv[4] -md5check = sys.argv[5] - -def DownloadUntar(download_base_url, download_file_name, testfiles_path, testfiles_name): - urllib.request.urlretrieve( download_base_url+"/"+download_file_name, testfiles_path+"/"+testfiles_name) - tar_file = tarfile.open(testfiles_path+"/"+testfiles_name) - tar_file.extractall(testfiles_path) - tar_file.close() - -if md5check == "1" : - # if .tar.gz and .tar.gz.md5 exist - # then download s3 md5 - # and compare with local md5 - print ("checking md5sum") - if os.path.isfile(testfiles_path+"/"+testfiles_name) and os.path.isfile(testfiles_path+"/"+testfiles_name+".md5") : - print("local files found") - - # dl md5 save it as *.md5.dl - urllib.request.urlretrieve( download_base_url+"/"+download_file_name+".md5", testfiles_path+"/"+testfiles_name+".md5.dl") - - # compare *md5.dl with md5 local - with open(testfiles_path+"/"+testfiles_name+".md5", 'r') as f: - md5_local = f.read() - with open(testfiles_path+"/"+testfiles_name+".md5.dl", 'r') as f: - md5_dl = f.read() - if md5_local == md5_dl : - print("no update in dataset") - else: - print("update found; download new dataset") - DownloadUntar(download_base_url, download_file_name, testfiles_path, testfiles_name) - urllib.request.urlretrieve( download_base_url+"/"+download_file_name+".md5", testfiles_path+"/"+testfiles_name+".md5") - else: - print("local file not found; download from S3") - print("downloading "+ download_base_url+"/"+download_file_name) - DownloadUntar(download_base_url, download_file_name, testfiles_path, testfiles_name) - urllib.request.urlretrieve( download_base_url+"/"+download_file_name+".md5", testfiles_path+"/"+testfiles_name+".md5") - -else: - # downloading release data from DASH - if os.path.isfile(testfiles_path+"/"+testfiles_name): - print("local RELEASE file found") - else: - print ("dowloading RELEASE data from "+download_base_url+"/"+download_file_name) - DownloadUntar(download_base_url, download_file_name, testfiles_path, testfiles_name) diff --git a/tools/cpplint.py b/tools/cpplint.py index 3add6637a..a7f6dfaa9 100755 --- a/tools/cpplint.py +++ b/tools/cpplint.py @@ -6004,10 +6004,8 @@ def FlagCxx11Features(filename, clean_lines, linenum, error): 'fenv', 'future', 'mutex', - 'thread', 'chrono', 'ratio', - 'regex', 'system_error', ): error(filename, linenum, 'build/c++11', 5, diff --git a/tools/new_obsop/example/ObsExampleTLAD.cc b/tools/new_obsop/example/ObsExampleTLAD.cc index 1b713edc5..ac2239bc8 100644 --- a/tools/new_obsop/example/ObsExampleTLAD.cc +++ b/tools/new_obsop/example/ObsExampleTLAD.cc @@ -25,7 +25,7 @@ static LinearObsOperatorMaker makerExampleTL_("Example"); ObsExampleTLAD::ObsExampleTLAD(const ioda::ObsSpace & odb, const eckit::Configuration & config) - : keyOper_(0), odb_(odb), varin_() + : LinearObsOperatorBase(odb), keyOper_(0), varin_() { ufo_example_tlad_setup_f90(keyOper_, config, odb.obsvariables(), varin_); oops::Log::trace() << "ObsExampleTLAD created" << std::endl; @@ -42,14 +42,14 @@ ObsExampleTLAD::~ObsExampleTLAD() { void ObsExampleTLAD::setTrajectory(const GeoVaLs & geovals, const ObsBias & bias, ObsDiagnostics & ydiags) { - ufo_example_tlad_settraj_f90(keyOper_, geovals.toFortran(), odb_, ydiags.toFortran()); + ufo_example_tlad_settraj_f90(keyOper_, geovals.toFortran(), obsspace(), ydiags.toFortran()); oops::Log::trace() << "ObsExampleTLAD: trajectory set" << std::endl; } // ----------------------------------------------------------------------------- void ObsExampleTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ovec) const { - ufo_example_simobs_tl_f90(keyOper_, geovals.toFortran(), odb_, + ufo_example_simobs_tl_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsExampleTLAD: TL observation operator run" << std::endl; } @@ -57,7 +57,7 @@ void ObsExampleTLAD::simulateObsTL(const GeoVaLs & geovals, ioda::ObsVector & ov // ----------------------------------------------------------------------------- void ObsExampleTLAD::simulateObsAD(GeoVaLs & geovals, const ioda::ObsVector & ovec) const { - ufo_example_simobs_ad_f90(keyOper_, geovals.toFortran(), odb_, + ufo_example_simobs_ad_f90(keyOper_, geovals.toFortran(), obsspace(), ovec.size(), ovec.toFortran()); oops::Log::trace() << "ObsExampleTLAD: adjoint observation operator run" << std::endl; } diff --git a/tools/new_obsop/example/ObsExampleTLAD.h b/tools/new_obsop/example/ObsExampleTLAD.h index 30fd6534b..2ed1ef9dd 100644 --- a/tools/new_obsop/example/ObsExampleTLAD.h +++ b/tools/new_obsop/example/ObsExampleTLAD.h @@ -55,7 +55,6 @@ class ObsExampleTLAD : public LinearObsOperatorBase, private: void print(std::ostream &) const override; F90hop keyOper_; - const ioda::ObsSpace& odb_; oops::Variables varin_; }; diff --git a/tools/new_obsop/test/CMakeLists.txt b/tools/new_obsop/test/CMakeLists.txt index bfe8de694..ed9e62467 100644 --- a/tools/new_obsop/test/CMakeLists.txt +++ b/tools/new_obsop/test/CMakeLists.txt @@ -3,11 +3,11 @@ execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_SOURCE_DIR}/testinput/autogenerated.yaml" "${CMAKE_CURRENT_BINARY_DIR}/testinput/autogenerated.yaml") -file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Data") # Note: can't use filters_testdata.nc4 since it doesn't contain latitudes or longitudes. +# link the local test Data directory into the main ufo test Data directory execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink - "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/testinput/filters/poisson_disk_thinning_3x3x3x3_regular_grid.nc4" - "${CMAKE_CURRENT_BINARY_DIR}/Data/poisson_disk_thinning_3x3x3x3_regular_grid.nc4") + "${CMAKE_CURRENT_BINARY_DIR}/../../../test/Data" + "${CMAKE_CURRENT_BINARY_DIR}/Data") # Test the generated operator. ecbuild_add_test(TARGET test_ufo_opr_autogenerated @@ -15,7 +15,8 @@ ecbuild_add_test(TARGET test_ufo_opr_autogenerated $ ARGS "testinput/autogenerated.yaml" ENVIRONMENT OOPS_TRAPFPE=1 - LIBS ufo) + LIBS ufo + TEST_DEPENDS ufo_get_ufo_test_data) # (We can't test the linear operator as long as its implementation is empty, since the test # expects its output to vary as a function of input data.) diff --git a/tools/new_obsop/test/testinput/autogenerated.yaml b/tools/new_obsop/test/testinput/autogenerated.yaml index 77b934ecd..5b99bd4c4 100644 --- a/tools/new_obsop/test/testinput/autogenerated.yaml +++ b/tools/new_obsop/test/testinput/autogenerated.yaml @@ -7,7 +7,7 @@ observations: obs space: name: test data obsdatain: - obsfile: Data/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 + obsfile: Data/ufo/testinput_tier_1/poisson_disk_thinning_3x3x3x3_regular_grid.nc4 simulated variables: [air_temperature] geovals: filename: NONE diff --git a/tools/new_qc/test/CMakeLists.txt b/tools/new_qc/test/CMakeLists.txt index 108335bd2..3c6484c22 100644 --- a/tools/new_qc/test/CMakeLists.txt +++ b/tools/new_qc/test/CMakeLists.txt @@ -3,10 +3,10 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_SOURCE_DIR}/testinput/qc_autogenerated.yaml" "${CMAKE_CURRENT_BINARY_DIR}/testinput/qc_autogenerated.yaml") -file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Data") +# link the local test Data directory into the main ufo test Data directory execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink - "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/testinput/filters/filters_testdata.nc4" - "${CMAKE_CURRENT_BINARY_DIR}/Data/filters_testdata.nc4") + "${CMAKE_CURRENT_BINARY_DIR}/../../../test/Data" + "${CMAKE_CURRENT_BINARY_DIR}/Data") # Test that the generated filter can be run. ecbuild_add_test(TARGET test_autogeneratedfilter @@ -16,4 +16,5 @@ ecbuild_add_test(TARGET test_autogeneratedfilter LIBS ufo #[[Make it possible to include files from the top-level test directory and the directory with the generated sources]] INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/../../.." - "${CMAKE_CURRENT_BINARY_DIR}/..") + "${CMAKE_CURRENT_BINARY_DIR}/.." + TEST_DEPENDS ufo_get_ufo_test_data) diff --git a/tools/new_qc/test/testinput/qc_autogenerated.yaml b/tools/new_qc/test/testinput/qc_autogenerated.yaml index 520aaf7a1..8bbce9ee6 100644 --- a/tools/new_qc/test/testinput/qc_autogenerated.yaml +++ b/tools/new_qc/test/testinput/qc_autogenerated.yaml @@ -5,7 +5,7 @@ observations: - obs space: name: test data obsdatain: - obsfile: Data/filters_testdata.nc4 + obsfile: Data/ufo/testinput_tier_1/filters_testdata.nc4 simulated variables: [variable1, variable2, variable3] obs filters: - filter: Autogenerated diff --git a/ufo-import.cmake.in b/ufo-import.cmake.in new file mode 100644 index 000000000..b708165d9 --- /dev/null +++ b/ufo-import.cmake.in @@ -0,0 +1,75 @@ +# ufo-import.cmake + +include(CMakeFindDependencyMacro) + +if( @jedicmake_FOUND@ AND NOT jedicmake_FOUND ) + find_dependency(jedicmake REQUIRED) +endif() + +if(NOT (MPI_C_FOUND AND MPI_CXX_FOUND AND MPI_Fortran_FOUND)) + find_dependency(MPI REQUIRED COMPONENTS C CXX Fortran) +endif() + +if(NOT Boost_FOUND) + find_dependency(Boost REQUIRED) +endif() + +if(NOT (NetCDF_C_FOUND AND NetCDF_Fortran_FOUND)) + find_dependency(NetCDF REQUIRED COMPONENTS C Fortran) +endif() + +if(NOT (Eigen3_FOUND OR EIGEN3_FOUND)) + find_dependency( Eigen3 REQUIRED NO_MODULE HINTS @EIGEN3_ROOT_DIR@ ) +endif() + +if(NOT gsl-lite_FOUND) + find_dependency(gsl-lite REQUIRED) +endif() + +if(NOT eckit_FOUND) + find_dependency(eckit REQUIRED) +endif() + +if(NOT fckit_FOUND) + find_dependency(fckit REQUIRED) +endif() + +if(NOT ioda_FOUND) + find_dependency(ioda REQUIRED) +endif() + +if(NOT oops_FOUND) + find_dependency(oops REQUIRED) +endif() + +# Optional dependencies + +if(@crtm_FOUND@ AND NOT crtm_FOUND) + find_dependency(crtm REQUIRED) +endif() + +if(@rttov_FOUND@ AND NOT rttov_FOUND) + find_dependency(rttov REQUIRED) +endif() + +if(@gsw_FOUND@ AND NOT gsw_FOUND) + find_dependency(gsw REQUIRED) +endif() + +if(@ropp-ufo_FOUND@ AND NOT ropp-ufo_FOUND) + find_dependency(ropp-ufo REQUIRED) +endif() + +if(@geos-aero_FOUND@ AND NOT geos-aero_FOUND) + find_dependency(geos-aero REQUIRED) +endif() + +# Export Fortran compiler version for checking module compatibility +set(@PROJECT_NAME@_MODULES_Fortran_COMPILER_ID @CMAKE_Fortran_COMPILER_ID@) +set(@PROJECT_NAME@_MODULES_Fortran_COMPILER_VERSION @CMAKE_Fortran_COMPILER_VERSION@) +if(NOT @PROJECT_NAME@_MODULES_Fortran_COMPILER_ID STREQUAL CMAKE_Fortran_COMPILER_ID + OR NOT @PROJECT_NAME@_MODULES_Fortran_COMPILER_VERSION VERSION_EQUAL CMAKE_Fortran_COMPILER_VERSION) + message(SEND_ERROR "Package @PROJECT_NAME@ provides Fortran modules built with " + "${@PROJECT_NAME@_MODULES_Fortran_COMPILER_ID}-${@PROJECT_NAME@_MODULES_Fortran_COMPILER_VERSION} " + "but this build for ${PROJECT_NAME} uses incompatible compiler ${CMAKE_Fortran_COMPILER_ID}-${CMAKE_Fortran_COMPILER_VERSION}") +endif()