From 510d51f8f3c79f7a69953a24bb88ccda70e2e175 Mon Sep 17 00:00:00 2001
From: "Benjamin T. Schwertfeger"
<51495182+btschwertfeger@users.noreply.github.com>
Date: Sun, 16 Apr 2023 14:57:53 +0200
Subject: [PATCH] Add Unit Tests; Simplify the Installation; Create the CI (#9)
---
.github/workflows/_build.yml | 39 +++
.../{docker_build.yaml => _build_docker.yml} | 29 +-
.github/workflows/_pre_commit.yml | 19 ++
.github/workflows/_test.yml | 38 +++
.github/workflows/cicd.yml | 59 ++++
.gitignore | 2 +-
.pre-commit-config.yaml | 19 ++
.vscode/extensions.json | 3 -
.vscode/settings.json | 115 -------
CMakeLists.txt | 33 +-
Dockerfile | 64 +++-
Makefile | 73 ++++
README.md | 122 ++++---
cmake/modules/FindNetCDFCxx.cmake | 14 +-
cmake/modules/uninstall.cmake | 29 ++
examples/example_all_methods.run.sh | 13 +-
include/CMethods.hxx | 9 +-
include/Manager.hxx | 7 +-
include/MathUtils.hxx | 9 +-
include/NcFileHandler.hxx | 9 +-
include/Utils.hxx | 14 +-
include/colors.h | 26 +-
src/CMakeLists.txt | 47 +++
src/CMethods.cxx | 31 +-
src/Manager.cxx | 90 +----
src/MathUtils.cxx | 7 +-
src/NcFileHandler.cxx | 7 +-
src/Utils.cxx | 120 +++++--
src/main.cxx | 7 +-
tests/CMakeLists.txt | 62 ++++
tests/README.md | 9 +
tests/src/TestCMethods.cxx | 317 ++++++++++++++++++
tests/src/TestManager.cxx | 51 +++
tests/src/TestMathUtils.cxx | 218 ++++++++++++
tests/src/TestNcFileHandler.cxx | 50 +++
tests/src/TestUtils.cxx | 154 +++++++++
tests/src/main.cxx | 31 ++
{test => validation}/CMakeLists.txt | 0
{test => validation}/README.md | 2 +
{test => validation}/src/ComputeIndicator.cxx | 9 +-
40 files changed, 1577 insertions(+), 380 deletions(-)
create mode 100644 .github/workflows/_build.yml
rename .github/workflows/{docker_build.yaml => _build_docker.yml} (56%)
create mode 100644 .github/workflows/_pre_commit.yml
create mode 100644 .github/workflows/_test.yml
create mode 100644 .github/workflows/cicd.yml
create mode 100644 .pre-commit-config.yaml
delete mode 100644 .vscode/extensions.json
delete mode 100644 .vscode/settings.json
create mode 100644 Makefile
create mode 100644 cmake/modules/uninstall.cmake
create mode 100644 src/CMakeLists.txt
create mode 100644 tests/CMakeLists.txt
create mode 100644 tests/README.md
create mode 100644 tests/src/TestCMethods.cxx
create mode 100644 tests/src/TestManager.cxx
create mode 100644 tests/src/TestMathUtils.cxx
create mode 100644 tests/src/TestNcFileHandler.cxx
create mode 100644 tests/src/TestUtils.cxx
create mode 100644 tests/src/main.cxx
rename {test => validation}/CMakeLists.txt (100%)
rename {test => validation}/README.md (95%)
rename {test => validation}/src/ComputeIndicator.cxx (97%)
diff --git a/.github/workflows/_build.yml b/.github/workflows/_build.yml
new file mode 100644
index 0000000..c65e12f
--- /dev/null
+++ b/.github/workflows/_build.yml
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2023 Benjamin Thomas Schwertfeger
+# Github: https://github.com/btschwertfeger
+#
+# Template workflow to build and install BiasAdjustCXX
+#
+
+name: Build and install BiasAdjustCXX
+
+on:
+ workflow_call:
+
+jobs:
+ Build:
+ runs-on: ubuntu-latest
+ container:
+ image: alpine:3.17
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Install dependencies
+ run: |
+ apk add --update linux-headers libc-dev g++ build-base git cmake libaec-dev netcdf-dev hdf5-dev curl-dev
+ git clone https://github.com/Unidata/netcdf-cxx4.git
+ cd netcdf-cxx4
+ cmake -S . -B build
+ cmake --build build
+ cd build
+ ctest
+ make install
+ cd ../..
+ rm -rf netcdf-cxx4
+
+ - name: Build and install BiasAdjustCXX
+ run: |
+ make build
+ make install
+ make uninstall
diff --git a/.github/workflows/docker_build.yaml b/.github/workflows/_build_docker.yml
similarity index 56%
rename from .github/workflows/docker_build.yaml
rename to .github/workflows/_build_docker.yml
index 77762b7..e0e8b5d 100644
--- a/.github/workflows/docker_build.yaml
+++ b/.github/workflows/_build_docker.yml
@@ -1,27 +1,44 @@
-name: Docker build
+## Checks the code logic, style and more
+# -*- coding: utf-8 -*-
+# Copyright (C) 2023 Benjamin Thomas Schwertfeger
+# Github: https://github.com/btschwertfeger
+#
+# Workflow to build the docker image.
+
+name: Build Docker Image
on:
- push:
- branches:
- - 'master'
+ workflow_call:
+ inputs:
+ TAG:
+ type: string
+ required: true
+ secrets:
+ DOCKERHUB_USERNAME:
+ required: true
+ DOCKERHUB_TOKEN:
+ required: true
jobs:
- build:
+ Deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
+
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
+
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
push: true
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/biasadjustcxx:latest
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/biasadjustcxx:${{ inputs.TAG }}
diff --git a/.github/workflows/_pre_commit.yml b/.github/workflows/_pre_commit.yml
new file mode 100644
index 0000000..0fca6f7
--- /dev/null
+++ b/.github/workflows/_pre_commit.yml
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2023 Benjamin Thomas Schwertfeger
+# Github: https://github.com/btschwertfeger
+#
+# Template workflow to run pre-commit.
+#
+
+name: Pre-Commit
+
+on:
+ workflow_call:
+
+jobs:
+ Pre-Commit:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - uses: pre-commit/action@v3.0.0
diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml
new file mode 100644
index 0000000..d149d16
--- /dev/null
+++ b/.github/workflows/_test.yml
@@ -0,0 +1,38 @@
+## Checks the code logic, style and more
+# -*- coding: utf-8 -*-
+# Copyright (C) 2023 Benjamin Thomas Schwertfeger
+# Github: https://github.com/btschwertfeger
+#
+# Workflow to build the test suite and run the unit tests.
+
+name: Unit Tests
+
+on:
+ workflow_call:
+
+jobs:
+ Test:
+ runs-on: ubuntu-latest
+ container:
+ image: alpine:3.17
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Install dependencies
+ run: |
+ apk add --update linux-headers libc-dev g++ build-base git cmake libaec-dev netcdf-dev hdf5-dev curl-dev
+ git clone https://github.com/Unidata/netcdf-cxx4.git
+ cd netcdf-cxx4
+ cmake -S . -B build
+ cmake --build build
+ cd build
+ ctest
+ make install
+ cd ../..
+ rm -rf netcdf-cxx4
+
+ - name: Test BiasAdjustCXX
+ run: |
+ make dev
+ make test
diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml
new file mode 100644
index 0000000..8740537
--- /dev/null
+++ b/.github/workflows/cicd.yml
@@ -0,0 +1,59 @@
+## Checks the code logic, style and more
+# -*- coding: utf-8 -*-
+# Copyright (C) 2023 Benjamin Thomas Schwertfeger
+# Github: https://github.com/btschwertfeger
+#
+# Workflow to apply pre-commit, build, test and upload the docker
+# image(s).
+
+name: CI/CD
+
+on:
+ push:
+ branches:
+ - "**"
+
+concurrency:
+ group: CICD-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ ## Run the pre-commit content
+ Pre-Commit:
+ uses: ./.github/workflows/_pre_commit.yml
+
+ ## Build the BiasAdjustCXX command-line tool
+ ##
+ Build:
+ needs: [Pre-Commit]
+ uses: ./.github/workflows/_build.yml
+
+ ## Build the test suite and run the unit tests
+ ##
+ Test:
+ needs: [Pre-Commit]
+ uses: ./.github/workflows/_test.yml
+
+ ## Create and upload a docker image
+ ##
+ Docker:
+ if: success() && github.ref != 'refs/heads/master'
+ needs: [Build, Test]
+ uses: ./.github/workflows/_build_docker.yml
+ with:
+ TAG: ${{ github.ref_name }}
+ secrets:
+ DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
+ DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ ## Create and upload the latest docker image
+ ##
+ Docker-latest:
+ needs: [Build, Test]
+ if: success() && github.ref == 'refs/heads/master'
+ uses: ./.github/workflows/_build_docker.yml
+ with:
+ TAG: latest
+ secrets:
+ DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
+ DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 84fd992..964a5d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,4 +40,4 @@ output/
qdm_result.nc
del/
del.cxx
-del.nc
\ No newline at end of file
+del.nc
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..e0b6ac5
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,19 @@
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.4.0
+ hooks:
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: trailing-whitespace
+ - id: check-added-large-files
+ - id: check-merge-conflict
+ - id: check-added-large-files
+ args: ["--maxkb=500"]
+ - id: check-executables-have-shebangs
+ - id: trailing-whitespace
+ - id: fix-byte-order-marker
+ - id: fix-encoding-pragma
+ - id: requirements-txt-fixer
+ - id: mixed-line-ending
+ - id: end-of-file-fixer
+ - id: detect-private-key
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
deleted file mode 100644
index 2ca9938..0000000
--- a/.vscode/extensions.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "recommendations": ["akamud.vscode-theme-onedark"]
-}
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index 4838bcf..0000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,115 +0,0 @@
-{
- "[cpp]": {
- "editor.defaultFormatter": "ms-vscode.cpptools"
- },
- "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 0, AlignAfterOpenBracket: BlockIndent}",
-
- "files.associations": {
- ".md": "md",
- "*.csv": "csv",
- "*.py": "python",
- "*.ejs": "ejs",
- "iostream": "cpp",
- "vector": "cpp",
- "chrono": "cpp",
- "__bit_reference": "cpp",
- "__bits": "cpp",
- "__config": "cpp",
- "__debug": "cpp",
- "__errc": "cpp",
- "__functional_base": "cpp",
- "__hash_table": "cpp",
- "__locale": "cpp",
- "__mutex_base": "cpp",
- "__node_handle": "cpp",
- "__nullptr": "cpp",
- "__split_buffer": "cpp",
- "__string": "cpp",
- "__threading_support": "cpp",
- "__tree": "cpp",
- "__tuple": "cpp",
- "algorithm": "cpp",
- "array": "cpp",
- "atomic": "cpp",
- "bit": "cpp",
- "bitset": "cpp",
- "cctype": "cpp",
- "clocale": "cpp",
- "cmath": "cpp",
- "codecvt": "cpp",
- "compare": "cpp",
- "complex": "cpp",
- "concepts": "cpp",
- "condition_variable": "cpp",
- "cstdarg": "cpp",
- "cstddef": "cpp",
- "cstdint": "cpp",
- "cstdio": "cpp",
- "cstdlib": "cpp",
- "cstring": "cpp",
- "ctime": "cpp",
- "cwchar": "cpp",
- "cwctype": "cpp",
- "deque": "cpp",
- "exception": "cpp",
- "fstream": "cpp",
- "functional": "cpp",
- "initializer_list": "cpp",
- "iomanip": "cpp",
- "ios": "cpp",
- "iosfwd": "cpp",
- "istream": "cpp",
- "iterator": "cpp",
- "limits": "cpp",
- "locale": "cpp",
- "map": "cpp",
- "memory": "cpp",
- "mutex": "cpp",
- "new": "cpp",
- "numbers": "cpp",
- "numeric": "cpp",
- "optional": "cpp",
- "ostream": "cpp",
- "queue": "cpp",
- "random": "cpp",
- "ratio": "cpp",
- "semaphore": "cpp",
- "set": "cpp",
- "sstream": "cpp",
- "stack": "cpp",
- "stdexcept": "cpp",
- "streambuf": "cpp",
- "string": "cpp",
- "string_view": "cpp",
- "system_error": "cpp",
- "thread": "cpp",
- "tuple": "cpp",
- "type_traits": "cpp",
- "typeinfo": "cpp",
- "unordered_map": "cpp",
- "unordered_set": "cpp",
- "utility": "cpp",
- "*.tcc": "cpp",
- "memory_resource": "cpp",
- "stop_token": "cpp",
- "cinttypes": "cpp",
- "variant": "cpp",
- "future": "cpp",
- "forward_list": "cpp",
- "list": "cpp",
- "typeindex": "cpp",
- "expected": "cpp",
- "cfenv": "cpp",
- "csetjmp": "cpp",
- "csignal": "cpp",
- "regex": "cpp",
- "scoped_allocator": "cpp",
- "shared_mutex": "cpp",
- "valarray": "cpp",
- "cuchar": "cpp",
- "filesystem": "cpp",
- "span": "cpp",
- "ranges": "cpp",
- "stacktrace": "cpp"
- }
-}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d722c88..de2486b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,7 +17,7 @@
cmake_minimum_required(VERSION 3.10)
project(BiasAdjustCXX
- VERSION 1.8
+ VERSION 1.8.2
DESCRIPTION "Bias correction command-line tool for NetCDF-based climate data"
LANGUAGES CXX
)
@@ -25,40 +25,17 @@ project(BiasAdjustCXX
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED OFF)
set(CMAKE_CXX_EXTENSIONS OFF)
-set(CMAKE_CXX_COMPILER g++)
-# add_compile_options(-Wall -v)
# find_package(netCDFCxx REQUIRED) # missing in cmake modules list (2022)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/")
include(FindNetCDFCxx)
if(netCDFCxx_FOUND)
- message (STATUS "netCDFCxx found!")
+ message (STATUS "NetCDFCxx found!")
include_directories(${NetCDFCxx_INCLUDE_DIRS})
else()
- message (FATAL_ERROR "Cannot find netCDFCxx!")
+ message (FATAL_ERROR "Cannot find NetCDFCxx!")
endif()
-# set sources
-set(SOURCES
- src/main.cxx
- src/CMethods.cxx
- src/Utils.cxx
- src/NcFileHandler.cxx
- src/MathUtils.cxx
- src/Manager.cxx
-)
-
-# add executable
-add_executable( BiasAdjustCXX ${SOURCES})
-
-# Include the netcdf headers
-target_include_directories( BiasAdjustCXX
- PUBLIC ${netCDFCxx_INCLUDE_DIRS}
- PRIVATE include
-)
-
-target_link_libraries( BiasAdjustCXX
- PUBLIC
- ${netCDFCxx_LIBRARIES}
-)
+add_subdirectory(src)
+add_subdirectory(tests EXCLUDE_FROM_ALL) # disables the binary from the ALL target
diff --git a/Dockerfile b/Dockerfile
index 9cee75c..9042214 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,22 +1,56 @@
-FROM ubuntu:22.04
+# -*- coding:utf-8 -*-
+# Copyright (C) 2023 Benjamin Thomas Schwertfeger
+# Github: https://github.com/btschwertfeger
+#
-RUN apt-get update \
- && apt-get -y install cmake git wget build-essential libnetcdf-dev
+FROM alpine:3.17 AS builder
-# download and instal NetCDFCXX
-RUN git clone https://github.com/Unidata/netcdf-cxx4.git \
- && cd netcdf-cxx4 \
- && mkdir build \
+# hadolint ignore=DL3018
+RUN apk add --update --no-cache \
+ linux-headers \
+ libc-dev \
+ libaec-dev \
+ netcdf-dev \
+ hdf5-dev \
+ curl-dev \
+ g++ \
+ build-base \
+ cmake \
+ git \
+ rm -rf /var/cache/apk/*
+
+# Install netcdf-cxx4
+WORKDIR /netcdf-cxx4
+# hadolint ignore=DL3003
+RUN git clone https://github.com/Unidata/netcdf-cxx4.git /netcdf-cxx4 \
+ && cmake -S . -B build \
+ && cmake --build build \
&& cd build \
- && cmake .. \
- && make \
&& ctest \
&& make install
-# download, build and install BiasAdjustCXX
-RUN git clone https://github.com/btschwertfeger/BiasAdjustCXX.git \
- && cd BiasAdjustCXX \
- && mkdir build && cd build \
- && cmake .. && cmake --build . \
- && cp BiasAdjustCXX /usr/local/bin
+# Install BiasAdjustCXX
+COPY . /BiasAdjustCXX/
+WORKDIR /BiasAdjustCXX
+RUN rm -rf build \
+ && make dev \
+ && make test \
+ && make clean \
+ && make build \
+ && make install
+
+# ===== N E X T - S T A G E ======
+
+FROM alpine:3.17
+
+# hadolint ignore=DL3018
+RUN apk add --update --no-cache \
+ libc-dev \
+ libaec-dev \
+ netcdf-dev \
+ hdf5-dev \
+ rm -rf /var/cache/apk/*
+
+COPY --from=builder /usr/local/ /usr/local/
+WORKDIR /work
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..4ea4933
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,73 @@
+#!make
+# -*- coding: utf-8 -*-
+# Copyright (C) 2023 Benjamin Thomas Schwertfeger
+# Github: https://github.com/btschwertfeger
+
+PROJECT := BiasAdjustCXX
+TEST_PROJECT := TestBiasAdjustCXX
+TEST_DIR := tests
+.PHONY := build rebuild dev redev install uninstall test changelog clean
+
+### Building
+
+## Compile the BiasAdjustCXX command-line tool
+##
+build:
+ cmake -S . -B build && cmake --build build --target $(PROJECT)
+
+rebuild: clean build
+
+## Build and Compile the testsuite
+##
+dev:
+ cmake -S . -B build && cmake --build build --target $(TEST_PROJECT)
+
+redev: clean dev
+
+## Build the validation tool (not actively maintained!)
+##
+build-val:
+ cmake -S validation/ -B validation/build && cmake --build validation/build
+
+### Un-/installation
+
+## Installation
+##
+install:
+ cd build && make install
+
+## Uninstallation
+##
+uninstall:
+ cd build && make uninstall
+
+### Testing
+
+## Run the unit tests
+##
+test:
+ cd build/tests && ctest
+
+### Misc
+
+## Create the changelog
+##
+changelog:
+ docker run -it --rm \
+ -v "$(pwd)":/usr/local/src/my-app \
+ githubchangeloggenerator/github-changelog-generator \
+ -u btschwertfeger \
+ -p BiasAdjustCXX \
+ -t $(GHTOKEN) \
+ --breaking-labels Breaking \
+ --enhancement-labels Feature
+
+pre-commit:
+ pre-commit run -a
+
+## Clean the generated files
+##
+clean:
+ rm -rf \
+ build tests/build validation/build \
+ .ipynb_checkpoints examples/.ipynb_checkpoints
diff --git a/README.md b/README.md
index e2e59be..a787f56 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,8 @@
[](https://github.com/btschwertfeger/BiasAdjustCXX)
[](https://www.gnu.org/licenses/gpl-3.0)

-
+
+


@@ -39,9 +40,10 @@ In addition - most of these methods available here have also been implemented in
## 1. About
-This repository started in 2022 as part of a Bachelor Thesis with the topic: ["The influence of bias corrections on variability, distribution, and correlation of temperatures in comparison to observed and modeled climate data in Europe"](https://epic.awi.de/id/eprint/56689/). A technical publication is available at [https://doi.org/10.1016/j.softx.2023.101379](https://doi.org/10.1016/j.softx.2023.101379).
+The BiasAdjustCXX command-line tool is the subject of a publication by Schwertfeger, Benjamin Thomas, Lohmann, Gerrit, and Lipskoch, Henrik (2023) _"Introduction of the BiasAdjustCXX command-line tool for the application of fast and efficient bias corrections in climatic research"_. It provides an insight into the architecture, possible applications and new scientific questions. This publication referencing [BiasAdjustCXX v1.8.1](https://github.com/btschwertfeger/BiasAdjustCXX/tree/v1.8.1) was published in the journal SoftwareX in March 2023 and is available at [https://doi.org/10.1016/j.softx.2023.101379](https://doi.org/10.1016/j.softx.2023.101379).
-These programs and data structures are designed to help minimize discrepancies between modeled and observed climate data. Data from past periods are used to adjust variables from current and future time series so that their distributional properties approximate possible actual values.
+
+These tool and data structures are developed with the aim of reducing discrepancies between modeled and observed climate data. Historical data is utilized to calibrate variables from current and future time series to achieve distributional properties that closely resemble the possible actual values.
Figure 1: Schematic representation of a bias adjustment procedure
-In this way, for example, modeled data, which on average represent values that are too cold, can be adjusted by applying an adjustment procedure. The following figure shows the observed, the modeled, and the adjusted values. It is directly visible that the delta adjusted time series ($T^{*DM}_{sim,p}$) are much more similar to the observed data ($T_{obs,p}$) than the raw modeled data ($T_{sim,p}$).
+For instance, modeled data typically indicate values that are colder than the actual values. To address this issue, an adjustment procedure is employed. The figure below illustrates the observed, modeled, and adjusted values, revealing that the delta adjusted time series ($T^{\*DM}{sim,p}$) are significantly more similar to the observed data ($T{obs,p}$) than the raw modeled data ($T_{sim,p}$).
Figure 2: Temperature per day of year in observed, modeled, and bias-adjusted climate data
+FΓΌr fragen, anmerkungen, hilfestellungne, ideen oder kooperationen, kann jederzeit ein [BiasAdjustCXX/issues](https://github.com/btschwertfeger/BiasAdjustCXX/issues) auf gemacht, der discussion bereich genutzt [BiasAdjustCXX/discussions](https://github.com/btschwertfeger/BiasAdjustCXX/discussions), oder sich direkt an contact@b-schwertfeger.de gewandt werden
+
---
@@ -74,11 +78,30 @@ In this way, for example, modeled data, which on average represent values that a
### Scaling-based techniques:
-- Delta (Change) Method\* (additive and multiplicative)
-- Linear Scaling\* (additive and multiplicative)
-- Variance Scaling\* (additive)
+- Delta (Change) Method (additive and multiplicative)
+- Linear Scaling (additive and multiplicative)
+- Variance Scaling (additive)
+
+#### General Notes:
+
+- Except for the variance scaling, all methods can be applied on stochastic and non-stochastic
+ climate variables. Variance scaling can only be applied on non-stochastic climate variables.
+
+ - Stochastic climate variables are those that are subject to random fluctuations
+ and are not predictable. They have no predictable trend or pattern. Examples of
+ stochastic climate variables include precipitation, air temperature, and humidity.
+
+ - Non-stochastic climate variables, on the other hand, have clear trend and pattern histories
+ and can be readily predicted. They are often referred to as climate elements and include
+ variables such as water temperature and air pressure.
+
+- The Delta Method requires that the time series of the control period have the same length as the time series to be adjusted.
-\* All data sets must exclude the 29th February and every year must have 365 entries. This is not required when using the `--no-group` flag which can be used to apply the scaling techniques in such a way that the scaling factors are based on the whole time series at once. This enables the possibility to apply the BiasAdjustCXX tool to data sets with custom time scales for example to adjust monthly separated time series individually to match the techniques described by Teutschbein ([2012](https://doi.org/10.1016/j.jhydrol.2012.05.052)) and Beyer ([2020](https://doi.org/10.5194/cp-16-1493-2020)). On the other hand the long-term 31-day interval procedures are customized variations and prevent disproportionately high differences in the long-term mean values at the monthly transitions. Thats why the long-term 31-day interval variant is the preferred method and is enabled by default for all scaling-based techniques.
+
+
+#### Notes regarding the scaling-based techniques:
+
+- All data sets must exclude the 29th February and every year must have 365 entries. This is not required when using the `--no-group` flag which can be used to apply the scaling techniques in such a way that the scaling factors are based on the whole time series at once. This enables the possibility to apply the BiasAdjustCXX tool to data sets with custom time scales for example to adjust monthly separated time series individually to match the techniques described by Teutschbein ([2012](https://doi.org/10.1016/j.jhydrol.2012.05.052)) and Beyer ([2020](https://doi.org/10.5194/cp-16-1493-2020)). On the other hand the long-term 31-day interval procedures are customized variations and prevent disproportionately high differences in the long-term mean values at the monthly transitions. Thats why the long-term 31-day interval variant is the preferred method and is enabled by default for all scaling-based techniques.
---
@@ -90,17 +113,21 @@ In this way, for example, modeled data, which on average represent values that a
Otherwise - you can build BiasAdjustCXX from source as described below.
-### 3.1 Compilation:
+### 3.1 Compilation and installation:
```bash
git clone https://github.com/btschwertfeger/BiasAdjustCXX.git
cd BiasAdjustCXX
-mkdir build && cd build
-cmake .. && cmake --build .
+make build
+make install
```
-Optional: Move the executable file `BiasAdjustCXX` into you binary directory.
+Uninstall using:
+
+```bash
+make uninstall
+```
### 3.2 Compilation requirements/dependencies:
@@ -108,7 +135,7 @@ cmake .. && cmake --build .
- CMake v3.10+ ([How to install CMake](https://cmake.org/install/))
- [optional] Climate Data Operators ([How to install cdo](https://www.isimip.org/protocol/preparing-simulation-files/cdo-help/))
-Optional for working example notebook `/examples/examples.ipynb`:
+Optional for working example notebook `/examples/examples.ipynb` (only for hardliner):
```bash
conda create --name clingenv
@@ -122,28 +149,28 @@ conda install xeus-cling notebook -c conda-forge/label/gcc7
### 3.3 Alternative: Docker π³
-The execution of BiasAdjustCXX is also possiblie within a Docker container. This is the preferred option when the installation of NetCDF4 C++ on the local system is not wanted. It also makes easier to access this tool since Docker container can run on nearly every operating system.
+The execution of BiasAdjustCXX is also possiblie within a Docker container. This is the preferred option when the installation of [NetCDF-4 C++](https://github.com/Unidata/netcdf-cxx4), [CMake](https://cmake.org) or BiasAdjustCXX on the local system is not desired. It also makes easier to access this tool since Docker container can run on nearly every operating system.
```bash
-docker run -it -v $(pwd):/work btschwertfeger/biasadjustcxx:latest sh -c "cd /work/ \
- && BiasAdjustCXX \
- --ref input_data/observations.nc \
- --contr input_data/control.nc \
- --scen input_data/scenario.nc \
- -o linear_scaling_result.nc \
- -m linear_scaling \
- -k \"+\" \
- -v tas \
- -p 4 \
-"
+# remove the comments before execution ...
+docker run -it -v $(PWD):/work btschwertfeger/biasadjustcxx:latest BiasAdjustCXX \
+ --ref input_data/observations.nc \ # observations/reference time series of the control period
+ --contr input_data/control.nc \ # simulated time series of the control period
+ --scen input_data/scenario.nc \ # time series to adjust
+ -o linear_scaling.nc \ # output file
+ -m linear_scaling \ # adjustment method
+ -k "+" \ # kind of adjustment ('+' == 'add' and '*' == 'mult')
+ -v tas \ # variable to adjust
+ -p 2 # number of threads
```
+See the Dockerhub registry to access the dev, pinned and older versions: [https://hub.docker.com/repository/docker/btschwertfeger/biasadjustcxx/general](https://hub.docker.com/repository/docker/btschwertfeger/biasadjustcxx/general)
+
### 3.4 Data requirements:
-- All input files must have the same shape, i.e. the same resolution and time span.
- The variable of interest must have the same name in all data sets.
-- The dimensions must be named 'time', 'lat' and 'lon' (i.e. times, latitudes and longitudes) in exactly this order in case the data sets have more than one dimension.
-- Executed scaling-based techniques without the `--no-group` flag require that the data sets exclude the 29th February and every year has exactly 365 entries.
+- The dimensions must be named "time", "lat" and "lon" (i.e. time, latitudes and longitudes) in exactly this order in case the data sets have more than one dimension.
+- Executed scaling-based techniques without the `--no-group` flag require that the data sets exclude the 29th February and every year has exactly 365 entries (see [Notes regarding the scaling-based techniques](#notes-scaling)).
---
@@ -163,7 +190,7 @@ docker run -it -v $(pwd):/work btschwertfeger/biasadjustcxx:latest sh -c "cd /wo
| `--1dim` | [optional] required if the data sets have no spatial dimensions (i.e. only one time dimension) |
| `--no-group` | [optional] Disables the adjustment based on 31-day long-term moving windows for the scaling-based methods. Scaling will be performed on the whole data set at once, so it is recommended to separate the input files for example by month and apply this program to every long-term month. (only for scaling-based methods) |
| `--max-scaling-factor` | [optional] Define the maximum scaling factor to avoid unrealistic results when adjusting ratio based variables for example in regions where heavy rainfall is not included in the modeled data and thus creating disproportional high scaling factors. (only for multiplicative methods except QM; default: 10) |
-| `-p`, `--n_processes` | [optional] How many threads to use (default: 1) |
+| `-p`, `--processes` | [optional] How many threads to use (default: 1) |
| `-h`, `--help` | [optional] display usage example, arguments, hints, and exits the program |
---
@@ -179,7 +206,7 @@ All methods to bias-adjust climate data can be found in `/src/CMethods.cxx`. The
Examples:
-a.) Additive Linear Scaling based on means of long-term 31-day intervals:
+a.) Additive Linear Scaling based on means of long-term 31-day intervals applied on a non-stochastic variable like temperatures ("tas"):
```bash
BiasAdjustCXX \
@@ -193,9 +220,9 @@ BiasAdjustCXX \
-p 4 \ # number of threads
```
-Note/alternative: The regular linear scaling procedure as described by Teutschbein ([2012](https://doi.org/10.1016/j.jhydrol.2012.05.052)) needs to be applied on monthly separated data sets. The `--no-group` flag needs to be used then.
+Note/alternative: The regular linear scaling procedure as described by Teutschbein ([2012](https://doi.org/10.1016/j.jhydrol.2012.05.052)) needs to be applied on monthly separated data sets. To do so, you have to separate the input data sets into individual long-term months and apply the tool on each of these long-term months using the `--no-group` flag. This is shown in `/examples/example_all_methods.run.sh`.
-b.) Multiplicative Delta Method based on means of long-term 31-day intervals:
+b.) Multiplicative Delta Method based on means of long-term 31-day intervals applied on a stochastic variable like precipitation ("pr"):
```bash
BiasAdjustCXX \
@@ -205,14 +232,14 @@ BiasAdjustCXX \
-o delta_method_result.nc \
-m delta_method \
-k "*" \
- -v tas \
- -p 4 \
+ -v pr \
+ -p 4 \ # use 4 threds (only if the input data is 3-dimensinoal)
--max-scaling-factor 3 # set custom max-scaling factor to avoid unrealistic results (default: 10)
```
-Note: The multiplicative variant is only preferred when adjusting ratio based variables like precipitaiton.
+Note: The multiplicative variant is only preferred when adjusting non-stochastic variables like precipitaiton.
-c.) Additive Quantile Delta Mapping:
+c.) Additive Quantile Delta Mapping applied on a non-stochastic variable like temperatures ("tas"):
```bash
BiasAdjustCXX \
@@ -230,19 +257,17 @@ BiasAdjustCXX \
d.) Help
```bash
-BiasAdjustCXX -h
+BiasAdjustCXX --help
```
---
-## 6. Notes
-- For adjusting data using the linear scaling, variance scaling or delta method and the `--no-group` flag:
+## π 6. Notes
-> You have to separate the files by month and then apply the correction for each month individually.
-> e.g. For 30 years of data to correct, you need to create a data set that contains all data for all Januaries and then apply the adjustment for this data set. After that you have to do the same for the rest of the months (see `/examples/example_adjust.run.sh`).
+- For adjusting data using the linear scaling, variance scaling or delta method and the `--no-group` flag: You have to separate the input files by month and then apply the correction for each month individually. e.g. For 30 years of data to correct, you need to prepare the three input data sets so that they first contain all time series for all Januaries and then apply the adjustment for this data set. After that you have to do the same for the rest of the months (see `/examples/example_all_methods.run.sh`).
- Formulas and references can be found below and at the implementation of the corresponding functions.
@@ -252,14 +277,15 @@ BiasAdjustCXX -h
-## 7. References
+## π¬ 7. References
-- Schwertfeger, Benjamin Thomas (2022) The influence of bias corrections on variability, distribution, and correlation of temperatures in comparison to observed and modeled climate data in Europe (https://epic.awi.de/id/eprint/56689/)
-- Linear Scaling and Variance Scaling based on: Teutschbein, Claudia and Seibert, Jan (2012) Bias correction of regional climate model simulations for hydrological climate-change impact studies: Review and evaluation of different methods (https://doi.org/10.1016/j.jhydrol.2012.05.052)
-- Delta Method based on: Beyer, R. and Krapp, M. and Manica, A. (2020): An empirical evaluation of bias correction methods for palaeoclimate simulations (https://doi.org/10.5194/cp-16-1493-2020)
-- Quantile Mapping based on: Alex J. Cannon and Stephen R. Sobie and Trevor Q. Murdock Bias Correction of GCM Precipitation by Quantile Mapping: How Well Do Methods Preserve Changes in Quantiles and Extremes? (https://doi.org/10.1175/JCLI-D-14-00754.1)
-- Quantile Delta Mapping based on: Tong, Y., Gao, X., Han, Z. et al. Bias correction of temperature and precipitation over China for RCM simulations using the QM and QDM methods. Clim Dyn 57, 1425β1443 (2021). (https://doi.org/10.1007/s00382-020-05447-4)
-- Schulzweida, U.: CDO User Guide, https://doi.org/10.5281/zenodo.7112925, 2022.
+- Schwertfeger, Benjamin Thomas, Lohmann, Gerrit, and Lipskoch, Henrik (2023): _"Introduction of the BiasAdjustCXX command-line tool for the application of fast and efficient bias corrections in climatic research"_. (https://doi.org/10.1016/j.softx.2023.101379)
+- Schwertfeger, Benjamin Thomas (2022) _"The influence of bias corrections on variability, distribution, and correlation of temperatures in comparison to observed and modeled climate data in Europe"_ (https://epic.awi.de/id/eprint/56689/)
+- Linear Scaling and Variance Scaling based on: Teutschbein, Claudia and Seibert, Jan (2012) _"Bias correction of regional climate model simulations for hydrological climate-change impact studies: Review and evaluation of different methods"_ (https://doi.org/10.1016/j.jhydrol.2012.05.052)
+- Delta Method based on: Beyer, R. and Krapp, M. and Manica, A. (2020): _"An empirical evaluation of bias correction methods for palaeoclimate simulations"_ (https://doi.org/10.5194/cp-16-1493-2020)
+- Quantile Mapping based on: Alex J. Cannon and Stephen R. Sobie and Trevor Q. Murdock _"Bias Correction of GCM Precipitation by Quantile Mapping: How Well Do Methods Preserve Changes in Quantiles and Extremes?"_ (https://doi.org/10.1175/JCLI-D-14-00754.1)
+- Quantile Delta Mapping based on: Tong, Y., Gao, X., Han, Z. et al. _"Bias correction of temperature and precipitation over China for RCM simulations using the QM and QDM methods"_. Clim Dyn 57, 1425β1443 (2021). (https://doi.org/10.1007/s00382-020-05447-4)
+- Schulzweida, U.: _"CDO User Guide"_, https://doi.org/10.5281/zenodo.7112925, 2022.
- This project took advantage of netCDF software developed by UCAR/Unidata (http://doi.org/10.5065/D6H70CW6).
---
diff --git a/cmake/modules/FindNetCDFCxx.cmake b/cmake/modules/FindNetCDFCxx.cmake
index eb0913f..103c058 100644
--- a/cmake/modules/FindNetCDFCxx.cmake
+++ b/cmake/modules/FindNetCDFCxx.cmake
@@ -1,6 +1,8 @@
-#
+#
# Find the netCDF C++ API cmake files
-# @github https://github.com/btschwertfeger/BiasAdjustCXX
+# @author Benjamin Thomas Schwertfeger
+# @email: contact@b-schwertfeger.de
+# @link https://github.com/btschwertfeger/BiasAdjustCXX
#
# * Copyright (C) 2023 Benjamin Thomas Schwertfeger
#
@@ -15,11 +17,11 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
+# along with this program. If not, see .
#
-# ----- R E F E R E N C E S -----
+# ------------------------- R E F E R E N C E S -------------------------
# Inspired by:
-# Peter Hill (2021)
+# Peter Hill (2021)
# @source: https://gitlab.mpcdf.mpg.de/dave/BOUT-dev/-/blob/manylinux/cmake/FindnetCDFCxx.cmake
#
# Exports the following variables:
@@ -41,7 +43,7 @@ find_path(netCDF_CXX_INCLUDE_DIR NAMES netcdf
"include"
)
find_library(netCDF_CXX_LIBRARY NAMES netcdf_c++4 netcdf-cxx4
- HINTS
+ HINTS
"${NCXX4_CONFIG_LOCATION}"
PATH_SUFFIXES
"lib" "lib64"
diff --git a/cmake/modules/uninstall.cmake b/cmake/modules/uninstall.cmake
new file mode 100644
index 0000000..cffe1d5
--- /dev/null
+++ b/cmake/modules/uninstall.cmake
@@ -0,0 +1,29 @@
+#
+# Creates the uninstall target to remove the installed program(s)
+#
+# Source: https://gist.github.com/royvandam/3033428 (March 24, 2023)
+#
+set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/../install_manifest.txt")
+
+if(NOT EXISTS ${MANIFEST})
+ message(FATAL_ERROR "Cannot find install manifest: '${MANIFEST}'")
+endif()
+
+file(STRINGS ${MANIFEST} files)
+foreach(file ${files})
+ if(EXISTS ${file})
+ message(STATUS "Removing file: '${file}'")
+
+ exec_program(
+ ${CMAKE_COMMAND} ARGS "-E remove ${file}"
+ OUTPUT_VARIABLE stdout
+ RETURN_VALUE result
+ )
+
+ if(NOT "${result}" STREQUAL 0)
+ message(FATAL_ERROR "Failed to remove file: '${file}'.")
+ endif()
+ else()
+ MESSAGE(STATUS "File '${file}' does not exist.")
+ endif()
+endforeach(file)
diff --git a/examples/example_all_methods.run.sh b/examples/example_all_methods.run.sh
index 5145943..8453b2f 100755
--- a/examples/example_all_methods.run.sh
+++ b/examples/example_all_methods.run.sh
@@ -1,9 +1,8 @@
#!/bin/zsh
# @author Benjamin Thomas Schwertfeger
-# @email development@b-schwertfeger.de
-# @link https://b-schwertfeger.de
-# @github https://github.com/btschwertfeger/BiasAdjustCXX
+# @email contact@b-schwertfeger.de
+# @link https://github.com/btschwertfeger/BiasAdjustCXX
#
# * Copyright (C) 2023 Benjamin Thomas Schwertfeger
#
@@ -95,7 +94,7 @@ done
# * ===== Default Scaling Adjustent (long-term 31-day intervals) =====
# * -------------------------------------------------------------------
-# ? Additive linear scaling based on 31 day long-term mean interval instead of
+# ? Additive linear scaling based on 31 day long-term mean interval instead of
# long-term monthly means to avoid high deviations between month transitions
# this only works if every dataset has 365 days per year (no January 29th)
# this is available for all scaling methods
@@ -108,7 +107,7 @@ $exec_file \
-v $variable \
-k "add" \
-p 4 \
- -o "${output_dir}/${variable}_linear_scaling_kind-add_scalingtype-31dayinterval_result_${timespan}.nc"
+ -o "${output_dir}/${variable}_linear_scaling_kind-add_scalingtype-31dayinterval_result_${timespan}.nc"
# * -------------------------------------------------------------------
@@ -143,7 +142,7 @@ for method in "${month_methods[@]}"; do
-m $method \
-o "${tmp_results}/${month}_${method}.nc" \
-p 4 \
- --no-group
+ --no-group
done
# ? Merge corrected datasets
@@ -167,7 +166,7 @@ $exec_file \
-q $n_quantiles \
-k $kind \
--1dim \
- -o "${output_dir}/${variable}_1d_quantile_mapping_kind-${kind}_quants-${n_quantiles}_result_${timespan}.nc"
+ -o "${output_dir}/${variable}_1d_quantile_mapping_kind-${kind}_quants-${n_quantiles}_result_${timespan}.nc"
# * -------------------------------------------------------------------
# * ===== clean-up =====
diff --git a/include/CMethods.hxx b/include/CMethods.hxx
index 5c3b9fb..984b6f6 100644
--- a/include/CMethods.hxx
+++ b/include/CMethods.hxx
@@ -2,11 +2,10 @@
/**
* @file CMethods.hxx
- * @brief declaration of the CMethod class
+ * @brief Declaration of the CMethod class
* @author Benjamin Thomas Schwertfeger
- * @copyright Benjamin Thomas Schwertfeger
- * @link https://b-schwertfeger.de
- * @github https://github.com/btschwertfeger/BiasAdjustCXX
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
@@ -21,7 +20,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*/
diff --git a/include/Manager.hxx b/include/Manager.hxx
index ab51d52..20b4d1a 100644
--- a/include/Manager.hxx
+++ b/include/Manager.hxx
@@ -2,11 +2,10 @@
/**
* @file CMethods.hxx
- * @brief declaration of the CMethod class
+ * @brief Declaration of the CMethod class
* @author Benjamin Thomas Schwertfeger
- * @copyright Benjamin Thomas Schwertfeger
- * @link https://b-schwertfeger.de
- * @github https://github.com/btschwertfeger/BiasAdjustCXX
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
diff --git a/include/MathUtils.hxx b/include/MathUtils.hxx
index c260fdb..550b8b9 100644
--- a/include/MathUtils.hxx
+++ b/include/MathUtils.hxx
@@ -4,9 +4,8 @@
* @file MathUtils.hxx
* @brief Declaration of the MathUtils class
* @author Benjamin Thomas Schwertfeger
- * @copyright Benjamin Thomas Schwertfeger
- * @link https://b-schwertfeger.de
- * @github https://github.com/btschwertfeger/BiasAdjustCXX
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
@@ -21,7 +20,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*/
@@ -71,4 +70,4 @@ class MathUtils {
private:
};
-#endif
\ No newline at end of file
+#endif
diff --git a/include/NcFileHandler.hxx b/include/NcFileHandler.hxx
index a5df78c..ca63a1b 100644
--- a/include/NcFileHandler.hxx
+++ b/include/NcFileHandler.hxx
@@ -4,9 +4,8 @@
* @file NcFileHandler.hxx
* @brief File to declare NcFileHandler Class
* @author Benjamin Thomas Schwertfeger
- * @copyright Benjamin Thomas Schwertfeger
- * @link https://b-schwertfeger.de
- * @github https://github.com/btschwertfeger/BiasAdjustCXX
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
@@ -21,7 +20,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*/
@@ -89,4 +88,4 @@ class NcFileHandler {
#endif
/**
* * ----- ----- E O F ----- ------ ----- ------ ----- ------ ----- ------ ----- ------ ----- ------ ----- ------ |
- */
\ No newline at end of file
+ */
diff --git a/include/Utils.hxx b/include/Utils.hxx
index e24f45a..8d70014 100644
--- a/include/Utils.hxx
+++ b/include/Utils.hxx
@@ -4,9 +4,8 @@
* @file Utils.hxx
* @brief Utility functions
* @author Benjamin Thomas Schwertfeger
- * @copyright Benjamin Thomas Schwertfeger
- * @link https://b-schwertfeger.de
- * @github https://github.com/btschwertfeger/BiasAdjustCXX
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
@@ -21,7 +20,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*/
@@ -46,11 +45,10 @@ class Log {
};
bool isInStrV(std::vector v, std::string string);
void progress_bar(float part, float all);
-void show_license();
+std::string get_version();
+void show_usage();
void show_copyright_notice(std::string program_name);
+void show_license();
} // namespace utils
#endif
-
-/*
- * ----- ----- ----- E O F ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
diff --git a/include/colors.h b/include/colors.h
index 63c2bf9..644fdcb 100644
--- a/include/colors.h
+++ b/include/colors.h
@@ -1,4 +1,28 @@
// -*- lsst-c++ -*-
+/**
+ * @file colors.h
+ * @brief Define some colors
+ * @author Benjamin Thomas Schwertfeger
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
+ *
+ * * Copyright (C) 2023 Benjamin Thomas Schwertfeger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
namespace colors {
/**
* Colors for cmd output
@@ -20,4 +44,4 @@ namespace colors {
#define BOLDBLUE "\033[1m\033[34m" /* Bold Blue */
#define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */
#define BOLDCYAN "\033[1m\033[36m" /* Bold Cyan */
-} // namespace colors
\ No newline at end of file
+} // namespace colors
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..be17b5f
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,47 @@
+# * Copyright (C) 2023 Benjamin Thomas Schwertfeger
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# ================================================================================
+
+
+set(BINARY ${CMAKE_PROJECT_NAME})
+
+set(SOURCES
+ main.cxx
+ CMethods.cxx
+ Utils.cxx
+ NcFileHandler.cxx
+ MathUtils.cxx
+ Manager.cxx
+)
+
+add_executable(${BINARY} ${SOURCES})
+
+# Include the netcdf headers
+target_include_directories( ${BINARY}
+ PUBLIC ${netCDFCxx_INCLUDE_DIRS}
+ PRIVATE ../include
+)
+
+target_link_libraries(${BINARY}
+ PUBLIC
+ ${netCDFCxx_LIBRARIES}
+)
+
+install(TARGETS ${BINARY})
+
+# Uninstall target
+add_custom_target(uninstall
+ "${CMAKE_COMMAND}" -P "${CMAKE_MODULE_PATH}/uninstall.cmake"
+)
diff --git a/src/CMethods.cxx b/src/CMethods.cxx
index 3765c06..ccc97e7 100644
--- a/src/CMethods.cxx
+++ b/src/CMethods.cxx
@@ -2,9 +2,10 @@
/**
* @file CMethods.cxx
- * @brief class/collection of procedures to bias adjust time series climate data
+ * @brief Class/collection of procedures to bias adjust time series climate data
* @author Benjamin Thomas Schwertfeger
- * @link https://b-schwertfeger.de
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
@@ -19,7 +20,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* * Notes:
@@ -122,14 +123,15 @@ std::vector> CMethods::get_long_term_dayofyear(std::vector 0 && factor > max_factor)
- ? max_factor
- : (factor < 0 && factor < -max_factor)
- ? -max_factor
+ const double abs_max_factor = std::abs(max_factor);
+ return (factor > 0 && factor > abs_max_factor)
+ ? abs_max_factor
+ : (factor < 0 && factor < -abs_max_factor)
+ ? -abs_max_factor
: factor;
}
@@ -627,11 +629,14 @@ void CMethods::Quantile_Delta_Mapping(
} else {
// clang-format off
- for (unsigned ts = 0; ts < v_scenario.size(); ts++)
- v_output[ts] = (float)(QDM1[ts] * get_adjusted_scaling_factor(
- v_scenario[ts] / MathUtils::interpolate(contr_cdf, v_xbins, epsilon[ts], false),
- settings.max_scaling_factor
- )); // Eq. 2.3f.
+ for (unsigned ts = 0; ts < v_scenario.size(); ts++) {
+ double delta_basis = MathUtils::interpolate(contr_cdf, v_xbins, epsilon[ts], false);
+ v_output[ts] = (delta_basis == 0)
+ ? (float)(QDM1[ts] * settings.max_scaling_factor) // Eq. 2.3f.
+ : (float)(QDM1[ts] * get_adjusted_scaling_factor(
+ v_scenario[ts] / delta_basis, settings.max_scaling_factor // Eq. 2.3f.
+ ));
+ }
// clang-format on
}
}
diff --git a/src/Manager.cxx b/src/Manager.cxx
index 265a411..268b081 100644
--- a/src/Manager.cxx
+++ b/src/Manager.cxx
@@ -4,7 +4,8 @@
* @file Manager.cxx
* @brief Manager class which controls the program flow of BiasAdjustCXX
* @author Benjamin Thomas Schwertfeger
- * @link https://b-schwertfeger.de
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
@@ -143,72 +144,6 @@ void Manager::run_adjustment() {
log.info("Done!");
}
-/**
- * Command-line output of the program usage hints.
- */
-void Manager::show_usage() {
- std::cout << BOLDBLUE << "Usage: " RESET << "BiasAdjustCXX"
- << "\t\t\t\\\n"
- << GREEN << "\t --ref " << RESET << "observation_data.nc\t\\\n"
- << GREEN << "\t --contr " << RESET << "control_data.nc\t\\\n"
- << GREEN << "\t --scen " << RESET << "data_to_adjust.nc\t\\\n"
- << GREEN << "\t -v " << RESET << "tas\t\t\t\t\\\n"
- << GREEN << "\t -m " << RESET << "linear_scaling\t\t\\\n"
- << GREEN << "\t -o " << RESET << "result_linear_scaling.nc\n\n"
- << BOLDBLUE << "Parameters:\n"
- << RESET
- << " required:\n"
- << GREEN << "\t--ref, --reference\t\t" << RESET << "observation/reanalysis data => input file/file path\n"
- << GREEN << "\t--contr, --control\t\t" << RESET << "modeled control period data => input file/file path\n"
- << GREEN << "\t--scen, --scenario\t\t" << RESET << "modeled scenario period data to adjust => input file/file path\n"
- << GREEN << "\t-o, --output\t\t\t" << RESET << "output file/file path\n"
- << GREEN << "\t-v, --variable\t\t\t" << RESET << "variable name (e.g.: tas, tsurf, pr) \n"
- << " optional:\n"
- << GREEN << "\t-h, --help\t\t\t" << RESET << "show this help message\n"
- << GREEN << "\t-q, --quantiles\t\t\t" << RESET << "number of quantiles to use when using a quantile adjustment method\n"
- << GREEN << "\t-k, --kind\t\t\t" << RESET << "kind of adjustment e.g.: '+' or '*' for additive or multiplicative method (default: '+')\n"
- << GREEN << "\t --1dim\t\t\t" << RESET << "select this, when all input data sets only contain the time dimension (i.e. no spatial dimensions)\n"
- << GREEN << "\t --no-group\t\t\t" << RESET << "disables the adjustment based on long-term 31-day intervals for the sclaing-based methods; "
- "mean calculation will be performed on the whole data set\n"
- << GREEN << "\t --max-scaling-factor\t" << RESET << "define the maximum scaling factor to avoid unrealistic results when adjusting ratio based variables "
- "(only for scaling methods; default: 10)\n"
- << GREEN << "\t-p, --n_processes\t\t" << RESET << "number of threads to start (only for 3-dimensional adjustments; default: 1)"
- << "\n\n"
- << BOLDBLUE << "Requirements: \n"
- << RESET
- << "-> data sets must have the file type NetCDF\n"
- << "-> for scaling-based adjustments: all input files must have 365 days per year (no February 29th.) otherwise the " << GREEN << "--no-group" << RESET << " flag is needed (see notes section below)\n"
- << "-> all data must be in format: [time][lat][lon] (if " << GREEN << "--1dim" << RESET << " is not slected) and values of type float\n"
- << "-> latitudes, longitudes and times must be named 'lat', 'lon' and 'time'\n"
- << RESET << std::endl;
-
- std::cout << BOLDBLUE << "Available methods: " << RESET << "\n-> ";
- std::vector all_methods;
- all_methods.reserve(CMethods::scaling_method_names.size() + CMethods::distribution_method_names.size()); // preallocate memory
- all_methods.insert(all_methods.end(), CMethods::scaling_method_names.begin(), CMethods::scaling_method_names.end());
- all_methods.insert(all_methods.end(), CMethods::distribution_method_names.begin(), CMethods::distribution_method_names.end());
-
- for (size_t i = 0; i < all_methods.size(); i++) std::cout << all_methods[i] << " ";
- std::cout << std::endl;
- std::cout << YELLOW << "\nNotes: " << RESET
- << "\n- When not using the " << GREEN << "--no-group" << RESET << " flag it is required that all input files must have 365 days per year (no February 29th.) "
- << "The Linear Scaling, Variance Scaling and Delta Method need a wrapper script when the " << GREEN << "--no-group" << RESET << " flag is used to apply this program on for example monthly separated files i.e. "
- << "to adjust 30 years of data, all input files need to be separated into 12 groups, one group for each month, than this program can be applied to every long-term month."
- << "\n\n- The Delta Method requires that the time series of the control period have the same length as the time series to be adjusted.";
-
- std::cout << YELLOW << "\n\n====== References ======" << RESET
- << "\n- Copyright (C) Benjamin Thomas Schwertfeger (2023) development@b-schwertfeger.de"
- << "\n- Unidata's NetCDF Programming Interface NetCDFCxx Data structures: http://doi.org/10.5065/D6H70CW6"
- << "\n- Mathematical foundations:"
- << "\n(1) Beyer, R., Krapp, M., and Manica, A.: An empirical evaluation of bias correction methods for palaeoclimate simulations, Climate of the Past, 16, 1493β1508, https://doi.org/10.5194/cp-16-1493-2020, 2020"
- << "\n\n(2) Cannon, A. J., Sobie, S. R., and Murdock, T. Q.: Bias Correction of GCM Precipitation by Quantile Mapping: How Well Do Methods Preserve Changes in Quantiles and Extremes?, Journal of Climate, 28, 6938 β 6959, https://doi.org/10.1175/JCLI-D-14-00754.1, 2015."
- << "\n\n(3) Maraun, D.: Nonstationarities of Regional Climate Model Biases in European Seasonal Mean Temperature and Precipitation Sums, Geophysical Research Letters, 39, 6706β, https://doi.org/10.1029/2012GL051210, 2012."
- << "\n\n(4) Teutschbein, C. and Seibert, J.: Bias correction of regional climate model simulations for hydrological climate-change impact studies: Review and evaluation of different methods, Journal of Hydrology, s 456β457, 12β29, https://doi.org/10.1016/j.jhydrol.2012.05.052, 2012."
- << "\n\n(5) Tong, Y., Gao, X., Han, Z., Xu, Y., Xu, Y., and Giorgi, F.: Bias correction of temperature and precipitation over China for RCM simulations using the QM and QDM methods, Climate Dynamics, 57, https://doi.org/10.1007/s00382-020-05447-4, 2021."
- << std::endl;
- std::cout.flush();
-}
-
/**
* Parses the arguments that were passed to the constructor.
* This includes loading the input data sets, checking if
@@ -217,9 +152,10 @@ void Manager::show_usage() {
*/
void Manager::parse_args() {
if (argc == 1) {
- show_usage();
+ utils::show_usage();
exit(0);
}
+
std::string
reference_fpath = "",
control_fpath = "",
@@ -227,7 +163,10 @@ void Manager::parse_args() {
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
- if (arg == "--ref" || arg == "--reference") {
+ if (arg == "-v" || arg == "--version") {
+ std::cout << utils::get_version() << std::endl;
+ exit(0);
+ } else if (arg == "--ref" || arg == "--reference") {
if (i + 1 < argc)
reference_fpath = argv[++i];
else
@@ -263,9 +202,12 @@ void Manager::parse_args() {
} else
throw std::runtime_error(arg + " requires one argument!");
} else if (arg == "--max-scaling-factor") {
- if (i + 1 < argc)
- adjustment_settings.max_scaling_factor = std::stoi(argv[++i]);
- else
+ if (i + 1 < argc) {
+ int max_scaling_factor = std::stoi(argv[++i]);
+ if (max_scaling_factor == 0)
+ throw std::runtime_error("max-scaling-factor cannot be 0!");
+ adjustment_settings.max_scaling_factor = max_scaling_factor;
+ } else
throw std::runtime_error(arg + " requires one argument!");
} else if (arg == "--no-group")
adjustment_settings.interval31_scaling = false;
@@ -276,10 +218,10 @@ void Manager::parse_args() {
throw std::runtime_error(arg + " requires one argument!");
} else if (arg == "--1dim")
one_dim = true;
- else if (arg == "-p" || arg == "--n_processes") {
+ else if (arg == "-p" || arg == "--processes") {
n_jobs = std::stoi(argv[++i]);
} else if (arg == "-h" || arg == "--help") {
- show_usage();
+ utils::show_usage();
exit(0);
} else if (arg == "show") {
if (i + 1 < argc) {
diff --git a/src/MathUtils.cxx b/src/MathUtils.cxx
index 0e10b4b..4de61e7 100644
--- a/src/MathUtils.cxx
+++ b/src/MathUtils.cxx
@@ -4,9 +4,8 @@
* @file MathUtils.cxx
* @brief Implementation of the MathUtils class
* @author Benjamin Thomas Schwertfeger
- * @copyright Benjamin Thomas Schwertfeger
- * @link https://b-schwertfeger.de
- * @github https://github.com/btschwertfeger/BiasAdjustCXX
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
@@ -21,7 +20,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*/
diff --git a/src/NcFileHandler.cxx b/src/NcFileHandler.cxx
index 8fe1d4a..6ec109c 100644
--- a/src/NcFileHandler.cxx
+++ b/src/NcFileHandler.cxx
@@ -4,9 +4,8 @@
* @file NcFileHandler.cxx
* @brief Class to store, manage and save netCDF datasets
* @author Benjamin Thomas Schwertfeger
- * @copyright Benjamin Thomas Schwertfeger
- * @link https://b-schwertfeger.de
- * @github https://github.com/btschwertfeger/BiasAdjustCXX
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
@@ -21,7 +20,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*/
diff --git a/src/Utils.cxx b/src/Utils.cxx
index d59b0f3..1884f4d 100644
--- a/src/Utils.cxx
+++ b/src/Utils.cxx
@@ -2,12 +2,10 @@
/**
* @file Utils.cxx
- * @brief
+ * @brief Implements the utility functions and classes
* @author Benjamin Thomas Schwertfeger
- * @link https://b-schwertfeger.de
- * @copyright Benjamin Thomas Schwertfeger
- * @link https://b-schwertfeger.de
- * @github https://github.com/btschwertfeger/BiasAdjustCXX
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
@@ -22,14 +20,9 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- *
+ * along with this program. If not, see .
*/
-/*
- * ----- ----- ----- I N C L U D E ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
-
#include "Utils.hxx"
#include
@@ -39,9 +32,6 @@
#include "colors.h"
-/*
- * ----- ----- ----- L O G G E R ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
-
namespace utils {
utils::Log::Log() {}
utils::Log::~Log() {}
@@ -52,9 +42,6 @@ void utils::Log::warning(std::string message) { std::cout << YELLOW << "WARNING:
void utils::Log::error(std::string message) { std::cout << BOLDRED << "ERROR: " << RESET << message << std::endl; }
} // namespace utils
-/*
- * ----- ----- ----- F U N C T I O N S ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
-
namespace utils {
/** Progress bar to bar the progress
* inspired by: https://stackoverflow.com/a/14539953/13618168
@@ -91,6 +78,10 @@ void progress_bar(float part, float all) {
namespace utils {
+std::string get_version() {
+ return "v1.8.2.devX";
+}
+
void show_copyright_notice(std::string program_name) {
std::cout << program_name << " Copyright (C) 2023 Benjamin Thomas Schwertfeger"
<< "\nThis program comes with ABSOLUTELY NO WARRANTY."
@@ -99,6 +90,99 @@ void show_copyright_notice(std::string program_name) {
<< "\n"
<< std::endl;
}
+
+/**
+ * Command-line output of the program usage hints.
+ */
+void show_usage() {
+ std::cout << BOLDBLUE << "Usage: " RESET << "BiasAdjustCXX"
+ << "\t\t\t\\\n"
+ << GREEN << "\t --ref " << RESET << "observation_data.nc\t\\\n"
+ << GREEN << "\t --contr " << RESET << "control_data.nc\t\\\n"
+ << GREEN << "\t --scen " << RESET << "data_to_adjust.nc\t\\\n"
+ << GREEN << "\t -v " << RESET << "tas\t\t\t\t\\\n"
+ << GREEN << "\t -m " << RESET << "linear_scaling\t\t\\\n"
+ << GREEN << "\t -o " << RESET << "result_linear_scaling.nc\n"
+ << std::endl;
+
+ std::cout << BOLDBLUE << "====== Parameters ======\n"
+ << RESET
+ << " required:\n"
+ << GREEN << "\t--ref, --reference\t\t" << RESET << "observation/reanalysis data => input file/file path\n"
+ << GREEN << "\t--contr, --control\t\t" << RESET << "modeled data (control period) => input file/file path\n"
+ << GREEN << "\t--scen, --scenario\t\t" << RESET << "data to adjust/correct (scenario period) => input file/file path\n"
+ << GREEN << "\t-o, --output\t\t\t" << RESET << "output file/file path\n"
+ << GREEN << "\t-m, --method\t\t\t" << RESET << "the bias correction technique to apply\n"
+ << GREEN << "\t-v, --variable\t\t\t" << RESET << "variable name (e.g.: tas, tsurf, pr) \n"
+ << " optional:\n"
+ << GREEN << "\t-k, --kind\t\t\t" << RESET << "kind of adjustment e.g.: '+' or '*' for additive or multiplicative method (default: '+')\n"
+ << GREEN << "\t-q, --quantiles\t\t\t" << RESET << "number of quantiles to respect when using a distribution-based techniques\n"
+ << GREEN << "\t --1dim\t\t\t" << RESET << "select this, when all input data sets only contain the time dimension (i.e. no spatial dimensions)\n"
+ << GREEN << "\t --no-group\t\t\t" << RESET << "disables the adjustment based on long-term 31-day intervals for the sclaing-based methods; "
+ "mean calculation will be performed on the whole data set\n"
+ << GREEN << "\t --max-scaling-factor\t" << RESET << "define the maximum scaling factor to avoid unrealistic results when adjusting ratio based variables "
+ "(default: 10)\n"
+ << GREEN << "\t-p, --processes\t\t\t" << RESET << "number of threads to start (only for 3-dimensional adjustments; default: 1)\n"
+ << GREEN << "\t-v, --version\t\t\t" << RESET << "show the executed version of this tool\n"
+ << GREEN << "\t-h, --help\t\t\t" << RESET << "show this help message\n"
+ << std::endl;
+
+ std::cout << BOLDBLUE << "====== Available Methods ======" << RESET << "\n"
+ << "\tDistribuition-based techniques\n"
+ << "\t\t- Quantile Mapping (quantile_mapping)\n"
+ << "\t\t- Quantile Delta Mapping (quantile_delta_mapping)\n"
+ << "\n"
+ << "\tScaling-based techniques\n"
+ << "\t\t- Linear Scaling (linear_scaling)\n"
+ << "\t\t- Variance Scaling (variance_scaling)\n"
+ << "\t\t- Delta Method (delta_method)\n"
+ << std::endl;
+
+ std::cout << BOLDBLUE << "====== Requirements ======\n"
+ << RESET
+ << "- data sets must be file type NetCDF\n"
+ << "- for scaling-based techniques: all input files must have 365 days per year (no February 29th.) otherwise the " << GREEN << "--no-group" << RESET << " flag is needed (see notes section below)\n"
+ << "- all data must be in format: [time][lat][lon] (if " << GREEN << "--1dim" << RESET << " is not slected) and values of type float\n"
+ << "- latitudes, longitudes and times must be named 'lat', 'lon' and 'time'\n"
+ << std::endl;
+
+ std::cout << YELLOW << "====== Notes ======" << RESET << "\n"
+ << "- Except for the variance scaling, all methods can be applied on stochastic and non-stochastic"
+ "climate variables. Variance scaling can only be applied on non-stochastic climate variables.\n"
+ << "\t- Stochastic climate variables are those that are subject to random fluctuations"
+ "and are not predictable. They have no predictable trend or pattern. Examples of"
+ "stochastic climate variables include precipitation, air temperature, and humidity.\n"
+ << "\t- Non-stochastic climate variables, on the other hand, have clear trend and pattern histories"
+ "and can be readily predicted. They are often referred to as climate elements and include"
+ "variables such as water temperature and air pressure.\n"
+
+ << "- All data sets must exclude the 29th February and every year must have 365 entries. "
+ "This is not required when using the "
+ << GREEN << "`--no-group`" << RESET
+ << "flag which can be used to apply the scaling techniques in such a way that the scaling "
+ "factors are based on the whole time series at once. This enables the possibility to apply the BiasAdjustCXX "
+ "tool to data sets with custom time scales for example to adjust monthly separated time series individually "
+ "to match the techniques described by Teutschbein ([2012](https://doi.org/10.1016/j.jhydrol.2012.05.052)) and Beyer "
+ "([2020](https://doi.org/10.5194/cp-16-1493-2020)). On the other hand the long-term 31-day "
+ "interval procedures are customized variations and prevent disproportionately high differences "
+ "in the long-term mean values at the monthly transitions. Thats why the long-term 31-day "
+ "interval variant is the preferred method and is enabled by default for all scaling-based techniques.\n"
+ << std::endl;
+
+ std::cout << YELLOW << "====== References ======" << RESET
+ << "\n- Copyright (C) Benjamin Thomas Schwertfeger (2023) contact@b-schwertfeger.de"
+ << "\n- Schwertfeger, B. T, Lohmann, G., Lipskoch, H.: \"Introduction of the BiasAdjustCXX command-line tool for the application of fast and efficient bias corrections in climatic research\", (2023) https://doi.org/10.1016/j.softx.2023.101379"
+ << "\n- Unidata's NetCDF Programming Interface NetCDFCxx Data structures: http://doi.org/10.5065/D6H70CW6"
+ << "\n- Mathematical foundations:"
+ << "\n (1) Beyer, R., Krapp, M., and Manica, A.: \"An empirical evaluation of bias correction methods for palaeoclimate simulations\", Climate of the Past, 16, 1493β1508, https://doi.org/10.5194/cp-16-1493-2020, 2020"
+ << "\n (2) Cannon, A. J., Sobie, S. R., and Murdock, T. Q.: \"Bias Correction of GCM Precipitation by Quantile Mapping: How Well Do Methods Preserve Changes in Quantiles and Extremes?\", Journal of Climate, 28, 6938 β 6959, https://doi.org/10.1175/JCLI-D-14-00754.1, 2015."
+ << "\n (3) Maraun, D.: \"Nonstationarities of Regional Climate Model Biases in European Seasonal Mean Temperature and Precipitation Sums\", Geophysical Research Letters, 39, 6706β, https://doi.org/10.1029/2012GL051210, 2012."
+ << "\n (4) Teutschbein, C. and Seibert, J.: \"Bias correction of regional climate model simulations for hydrological climate-change impact studies: Review and evaluation of different methods\", Journal of Hydrology, s 456β457, 12β29, https://doi.org/10.1016/j.jhydrol.2012.05.052, 2012."
+ << "\n (5) Tong, Y., Gao, X., Han, Z., Xu, Y., Xu, Y., and Giorgi, F.: Bias correction of temperature and precipitation over China for RCM simulations using the QM and QDM methods\", Climate Dynamics, 57, https://doi.org/10.1007/s00382-020-05447-4, 2021."
+ << std::endl;
+ std::cout.flush();
+}
+
void show_license() {
const char* license =
"GNU GENERAL PUBLIC LICENSE\n"
@@ -745,5 +829,3 @@ void show_license() {
std::cout << license << std::endl;
}
} // namespace utils
-/*
- * ----- ----- ----- E O F ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- */
diff --git a/src/main.cxx b/src/main.cxx
index 37952a4..14760e6 100644
--- a/src/main.cxx
+++ b/src/main.cxx
@@ -4,9 +4,8 @@
* @file main.cxx
* @brief Main program to bias adjust NetCDF-based climate data using the `BiasAdjustCXX` command-line tool
* @author Benjamin Thomas Schwertfeger
- * @email: development@b-schwertfeger.de
- * @link https://b-schwertfeger.de
- * @github https://github.com/btschwertfeger/BiasAdjustCXX
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
@@ -31,7 +30,7 @@
* - save results to .nc file
*
* * Compilation:
- * use the CMakeLists.txt for os-independand compilation.
+ * Run `make build` in the root of the BiasAdjustCXX repository
*/
/**
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..9c2626a
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,62 @@
+# * Copyright (C) 2023 Benjamin Thomas Schwertfeger
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# ================================================================================
+
+set(BINARY Test${CMAKE_PROJECT_NAME})
+
+include(FetchContent)
+FetchContent_Declare(
+ googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG v1.13.0
+)
+
+# For Windows: Prevent overriding the parent project's compiler/linker settings
+set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+set(BUILD_GMOCK ON CACHE BOOL "" FORCE)
+set(BUILD_GTEST ON CACHE BOOL "" FORCE)
+FetchContent_MakeAvailable(googletest)
+
+enable_testing()
+
+set(TEST_SOURCES
+ src/TestUtils.cxx
+ src/TestMathUtils.cxx
+ src/TestCMethods.cxx
+ src/TestManager.cxx
+ src/TestNcFileHandler.cxx
+ src/main.cxx
+ ../src/CMethods.cxx
+ ../src/Utils.cxx
+ ../src/NcFileHandler.cxx
+ ../src/MathUtils.cxx
+ ../src/Manager.cxx
+)
+
+# add executable
+add_executable(${BINARY} ${TEST_SOURCES})
+
+target_include_directories(${BINARY}
+ PUBLIC ${netCDFCxx_INCLUDE_DIRS}
+ PRIVATE ../include
+)
+
+target_link_libraries( ${BINARY}
+ GTest::gtest_main
+ ${netCDFCxx_LIBRARIES}
+)
+
+include(GoogleTest)
+gtest_discover_tests(${BINARY})
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..e5f0bcb
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,9 @@
+# Unittests fΓΌr BiasAdjustCXX
+
+This is the collection of unit tests for the BiasAdjustCXX command-line tool.
+They are compiled using the `dev` target of the root's Makefile. To run the unit tests, the
+tests must be compiled and executed from the root of the BiasAdjustCXX project.
+
+```bash
+make dev test
+```
diff --git a/tests/src/TestCMethods.cxx b/tests/src/TestCMethods.cxx
new file mode 100644
index 0000000..a9b20f8
--- /dev/null
+++ b/tests/src/TestCMethods.cxx
@@ -0,0 +1,317 @@
+// -*- lsst-c++ -*-
+/**
+ * @file TestCMethods.cxx
+ * @brief File containing unit tests for the CMethods class
+ * @author Benjamin Thomas Schwertfeger
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
+ *
+ * * Copyright (C) 2023 Benjamin Thomas Schwertfeger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+#include "CMethods.hxx"
+#include "gtest/gtest.h"
+
+namespace TestBiasAdjustCXX {
+namespace CMethods {
+namespace {
+
+// The fixture for testing class CMethods.
+class TestCMethods : public ::testing::Test {
+ protected:
+ TestCMethods() {
+ }
+
+ ~TestCMethods() override {
+ }
+
+ void SetUp() override {
+ // setup fake temperature and precipitation data for latitude = 52Β°N
+
+ // i.e., 30 years with 365 days each (without leap)
+ std::vector temperature(days);
+ std::vector precipitation(days);
+
+ for (unsigned ts = 0; ts < days; ts++) {
+ temperature[ts] = lat * cos(2 * pi * (ts % 365) / 365 + (2 * random_sample(0, temperature.size())) + kelvin + 0.1 * ((temperature.size() - 1) / 365));
+ precipitation[ts] = pow(cos(2 * pi * (ts % 365) / 365), 2) * random_sample(0, precipitation.size());
+ }
+
+ auto max_pr = *std::max_element(std::begin(precipitation), std::end(precipitation));
+ const int c = days_without_rain * years;
+
+ reference_temp = new std::vector(days);
+ target_temp = new std::vector(days);
+ control_temp = new std::vector(days);
+ scenario_temp = new std::vector(days);
+
+ reference_prec = new std::vector(days);
+ target_prec = new std::vector(days);
+ control_prec = new std::vector(days);
+ scenario_prec = new std::vector(days);
+
+ for (unsigned ts = 0; ts < days; ts++) {
+ float prec = precipitation[ts] * 0.0004 / max_pr; // scale down
+
+ if (random_sample(0, days) < days_without_rain)
+ prec = 0;
+
+ reference_prec->at(ts) = prec;
+ target_prec->at(ts) = prec * 1.02;
+ control_prec->at(ts) = prec * 0.98;
+ scenario_prec->at(ts) = prec * 0.09;
+
+ reference_temp->at(ts) = temperature[ts];
+ target_temp->at(ts) = temperature[ts] + 2.1;
+ control_temp->at(ts) = temperature[ts] - 1.1;
+ scenario_temp->at(ts) = temperature[ts] + 0.9;
+ }
+ }
+
+ /**
+ * Retuns a random number between min and max
+ */
+ int random_sample(int min, int max) {
+ int n = max - min + 1;
+ int remainder = RAND_MAX % n;
+ int x;
+ do {
+ x = rand();
+ } while (x >= RAND_MAX - remainder);
+ return min + x % n;
+ }
+
+ /** Returns the Mean Bias Error (taken from MathUtils.cxx)
+ * $MBE = \frac{1}{n} \sum_{i=1}^{n}(T_{y,i} - T_{x,i})$
+ *
+ * @param x 1D reference time series/vector
+ * @param y 1D prediction time series/vector
+ * @ return mean bias error
+ */
+ double mbe(std::vector &x, std::vector &y) {
+ if (x.size() != y.size()) throw std::runtime_error("Cannot calculate mbe of vectors with different size.");
+
+ double result = 0;
+ for (unsigned ts = 0; ts < x.size(); ts++) result += y[ts] - x[ts];
+ return result * (double(1.0) / (int)x.size());
+ }
+
+ void TearDown() override {
+ delete reference_temp;
+ delete target_temp;
+ delete control_temp;
+ delete scenario_temp;
+ delete reference_prec;
+ delete target_prec;
+ delete control_prec;
+ delete scenario_prec;
+ }
+ int
+ lat = 52,
+ days_without_rain = 239,
+ days = 10950,
+ years = 30;
+
+ double
+ kelvin = 273.15,
+ pi = atan(1) * 4;
+
+ std::vector *reference_temp;
+ std::vector *reference_prec;
+ std::vector *target_temp;
+ std::vector *target_prec;
+ std::vector *control_temp;
+ std::vector *control_prec;
+ std::vector *scenario_temp;
+ std::vector *scenario_prec;
+};
+
+// Test the calculation of the long-term day of year
+TEST_F(TestCMethods, CheckGetLongTermDayOfYear) {
+ std::vector> result = ::CMethods::get_long_term_dayofyear(
+ *reference_temp
+ );
+
+ ASSERT_EQ(result.size(), 365);
+ // maybe add some other checks but that would mock the whole method.
+}
+
+// Test the adjusted scaling factor method
+TEST_F(TestCMethods, CheckGetAdjustedScalingFactor) {
+ ASSERT_EQ(::CMethods::get_adjusted_scaling_factor(10, 5), 5);
+ ASSERT_EQ(::CMethods::get_adjusted_scaling_factor(10, 11), 10);
+ ASSERT_EQ(::CMethods::get_adjusted_scaling_factor(-10, -11), -10);
+ ASSERT_EQ(::CMethods::get_adjusted_scaling_factor(-11, -10), -10);
+}
+
+// Test the additive linear scaling procedure
+TEST_F(TestCMethods, CheckLinearScalingAdditive) {
+ AdjustmentSettings settings = AdjustmentSettings();
+ settings.kind = "+"; // default
+
+ std::vector *result = new std::vector(days); // 10950 entries
+ ::CMethods::Linear_Scaling(
+ *result, *reference_temp, *control_temp, *scenario_temp, settings
+ );
+
+ const double mbe_before = std::abs(mbe(*target_temp, *scenario_temp));
+ const double mbe_after = std::abs(mbe(*target_temp, *result));
+
+ ASSERT_LE(mbe_after, mbe_before);
+ delete result;
+}
+
+// Test the multiplicative linear scaling procedure
+TEST_F(TestCMethods, CheckLinearScalingMultiplicative) {
+ AdjustmentSettings settings = AdjustmentSettings();
+ settings.kind = "*";
+
+ std::vector *result = new std::vector(days);
+ ::CMethods::Linear_Scaling(
+ *result, *reference_prec, *control_prec, *scenario_prec, settings
+ );
+
+ const double mbe_before = std::abs(mbe(*target_prec, *scenario_prec));
+ const double mbe_after = std::abs(mbe(*target_prec, *result));
+
+ ASSERT_LE(mbe_after, mbe_before);
+ delete result;
+}
+
+// Test the (additive) variance scaling procedure
+TEST_F(TestCMethods, CheckVarianceScalingAdditive) {
+ AdjustmentSettings settings = AdjustmentSettings();
+ settings.kind = "+"; // default
+
+ std::vector *result = new std::vector(days);
+ ::CMethods::Variance_Scaling(
+ *result, *reference_temp, *control_temp, *scenario_temp, settings
+ );
+
+ const double mbe_before = std::abs(mbe(*target_temp, *scenario_temp));
+ const double mbe_after = std::abs(mbe(*target_temp, *result));
+
+ ASSERT_LE(mbe_after, mbe_before);
+ delete result;
+}
+
+// Test the additive delta method procedure
+TEST_F(TestCMethods, CheckDeltaMethodAdditive) {
+ AdjustmentSettings settings = AdjustmentSettings();
+ settings.kind = "+"; // default
+
+ std::vector *result = new std::vector(days); // 10950 entries
+ ::CMethods::Delta_Method(
+ *result, *reference_temp, *control_temp, *scenario_temp, settings
+ );
+
+ const double mbe_before = std::abs(mbe(*target_temp, *scenario_temp));
+ const double mbe_after = std::abs(mbe(*target_temp, *result));
+
+ ASSERT_LE(mbe_after, mbe_before);
+ delete result;
+}
+
+// Test the multiplicative delta method procedure
+TEST_F(TestCMethods, CheckDeltaMethodMultiplicative) {
+ AdjustmentSettings settings = AdjustmentSettings();
+ settings.kind = "*";
+
+ std::vector *result = new std::vector(days);
+ ::CMethods::Delta_Method(
+ *result, *reference_prec, *control_prec, *scenario_prec, settings
+ );
+
+ const double mbe_before = std::abs(mbe(*target_prec, *scenario_prec));
+ const double mbe_after = std::abs(mbe(*target_prec, *result));
+
+ ASSERT_LE(mbe_after, mbe_before);
+ delete result;
+}
+
+// Test the additive quantile mapping procedure
+TEST_F(TestCMethods, CheckQuantileMappingAdditive) {
+ AdjustmentSettings settings = AdjustmentSettings();
+ settings.kind = "+"; // default
+
+ std::vector *result = new std::vector(days); // 10950 entries
+ ::CMethods::Quantile_Mapping(
+ *result, *reference_temp, *control_temp, *scenario_temp, settings
+ );
+
+ const double mbe_before = std::abs(mbe(*target_temp, *scenario_temp));
+ const double mbe_after = std::abs(mbe(*target_temp, *result));
+
+ ASSERT_LE(mbe_after, mbe_before);
+ delete result;
+}
+
+// Test the multiplicative quantile mapping procedure
+TEST_F(TestCMethods, CheckQuantileMappingMultiplicative) {
+ AdjustmentSettings settings = AdjustmentSettings();
+ settings.kind = "*";
+
+ std::vector *result = new std::vector(days);
+ ::CMethods::Quantile_Mapping(
+ *result, *reference_prec, *control_prec, *scenario_prec, settings
+ );
+
+ const double mbe_before = std::abs(mbe(*target_prec, *scenario_prec));
+ const double mbe_after = std::abs(mbe(*target_prec, *result));
+
+ ASSERT_LE(mbe_after, mbe_before);
+ delete result;
+}
+
+// Test the additive quantile delta mapping procedure
+TEST_F(TestCMethods, CheckQuantileDeltaMappingAdditive) {
+ AdjustmentSettings settings = AdjustmentSettings();
+ settings.kind = "+"; // default
+
+ std::vector *result = new std::vector(days); // 10950 entries
+ ::CMethods::Quantile_Delta_Mapping(
+ *result, *reference_temp, *control_temp, *scenario_temp, settings
+ );
+
+ const double mbe_before = std::abs(mbe(*target_temp, *scenario_temp));
+ const double mbe_after = std::abs(mbe(*target_temp, *result));
+
+ ASSERT_LE(mbe_after, mbe_before);
+ delete result;
+}
+
+// Test the multiplicative quantile delta mapping procedure
+TEST_F(TestCMethods, CheckQuantileDeltaMappingMultiplicative) {
+ AdjustmentSettings settings = AdjustmentSettings();
+ settings.kind = "*";
+
+ std::vector *result = new std::vector(days);
+ ::CMethods::Quantile_Delta_Mapping(
+ *result, *reference_prec, *control_prec, *scenario_prec, settings
+ );
+
+ const double mbe_before = std::abs(mbe(*target_prec, *scenario_prec));
+ const double mbe_after = std::abs(mbe(*target_prec, *result));
+
+ ASSERT_LE(mbe_after, mbe_before);
+ delete result;
+}
+} // namespace
+} // namespace CMethods
+} // namespace TestBiasAdjustCXX
diff --git a/tests/src/TestManager.cxx b/tests/src/TestManager.cxx
new file mode 100644
index 0000000..078b4c3
--- /dev/null
+++ b/tests/src/TestManager.cxx
@@ -0,0 +1,51 @@
+// -*- lsst-c++ -*-
+/**
+ * @file TestManager.cxx
+ * @brief Implements the unit tests of the manager class
+ * @author Benjamin Thomas Schwertfeger
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
+ *
+ * * Copyright (C) 2023 Benjamin Thomas Schwertfeger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "Manager.hxx"
+#include "gtest/gtest.h"
+
+namespace TestBiasAdjustCXX {
+namespace Manager {
+namespace {
+
+// The fixture for testing class Manager.
+class TestManager : public ::testing::Test {
+ protected:
+ TestManager() {
+ }
+
+ ~TestManager() override {
+ }
+
+ void SetUp() override {
+ }
+
+ void TearDown() override {
+ }
+};
+
+// Not tested so far since this would required acceptance tests
+} // namespace
+} // namespace Manager
+} // namespace TestBiasAdjustCXX
diff --git a/tests/src/TestMathUtils.cxx b/tests/src/TestMathUtils.cxx
new file mode 100644
index 0000000..a7b5411
--- /dev/null
+++ b/tests/src/TestMathUtils.cxx
@@ -0,0 +1,218 @@
+// -*- lsst-c++ -*-
+/**
+ * @file TestMathUtils.cxx
+ * @brief Implements the unit tests of the MathUtils class
+ * @author Benjamin Thomas Schwertfeger
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
+ *
+ * * Copyright (C) 2023 Benjamin Thomas Schwertfeger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+#include "MathUtils.hxx"
+#include "gtest/gtest.h"
+
+namespace TestBiasAdjustCXX {
+namespace MathUtils {
+namespace {
+
+// The fixture for testing class MathUtills.
+class TestMathUtils : public ::testing::Test {
+ protected:
+ TestMathUtils() : sbuf{nullptr} {
+ }
+
+ ~TestMathUtils() override {
+ }
+
+ void SetUp() override {
+ sbuf = std::cout.rdbuf();
+ std::cout.rdbuf(buffer.rdbuf());
+
+ v = std::vector({1.0, 0.0, -1.0, 2.0, 0.0, -2.0});
+ w = std::vector({0.0, 1.0, 0.0, -2.0, -1.0, 0.0});
+ x = std::vector({0.0, 0.0, 0.0, 0.0, 0.0, 0.0});
+ y = std::vector({1.0, 1.5, 2.0, 2.5, 3.0, 3.5});
+ z = std::vector({-1.0, -1.5, -2.0, -2.5, -3.0, -3.5});
+ }
+
+ void TearDown() override {
+ std::cout.rdbuf(sbuf);
+ sbuf = nullptr;
+ }
+
+ std::stringstream buffer{};
+ std::streambuf* sbuf;
+
+ std::vector v;
+ std::vector w;
+ std::vector x;
+ std::vector y;
+ std::vector z;
+};
+
+// Test if available methods have not changed
+TEST_F(TestMathUtils, CheckAvailableMethods) {
+ EXPECT_EQ(::MathUtils::available_methods, std::vector({"rmse", "mbe", "ioa", "corr", "sd", "var", "mean"}));
+}
+
+// Test if available methods that require one data set have not changed
+TEST_F(TestMathUtils, CheckMethodsThatRequireOneDataset) {
+ EXPECT_EQ(::MathUtils::requires_1_ds, std::vector({"sd", "var", "mean"}));
+}
+
+// Test if available methods that require tow data sets have not changed
+TEST_F(TestMathUtils, CheckMethodsThatRequireTwoDatasets) {
+ EXPECT_EQ(::MathUtils::requires_2_ds, std::vector({"rmse", "mbe", "ioa", "corr"}));
+}
+
+// Test of the correlation coefficient function
+TEST_F(TestMathUtils, CheckCorrelationCoefficient) {
+ EXPECT_EQ(std::isnan(::MathUtils::correlation_coefficient(x, x)), true);
+ EXPECT_EQ(std::isnan(::MathUtils::correlation_coefficient(x, z)), true);
+ EXPECT_EQ(std::isnan(::MathUtils::correlation_coefficient(x, w)), true);
+ ASSERT_DOUBLE_EQ(::MathUtils::correlation_coefficient(v, v), 1);
+ ASSERT_DOUBLE_EQ(::MathUtils::correlation_coefficient(z, v), 0.45355736761107268);
+}
+
+// Test of the RMSE function
+TEST_F(TestMathUtils, CheckRootMeanSquareError) {
+ ASSERT_DOUBLE_EQ(::MathUtils::rmse(x, x), 0);
+ ASSERT_DOUBLE_EQ(::MathUtils::rmse(x, y), 2.4065881796989417);
+ ASSERT_DOUBLE_EQ(::MathUtils::rmse(x, z), 2.4065881796989417);
+}
+
+// Test of the Mean Bias Error function
+TEST_F(TestMathUtils, CheckMeanBiasError) {
+ ASSERT_DOUBLE_EQ(::MathUtils::mbe(x, x), 0);
+ ASSERT_DOUBLE_EQ(::MathUtils::mbe(x, y), 2.25);
+ ASSERT_DOUBLE_EQ(::MathUtils::mbe(y, z), -4.5);
+}
+
+// Test of the Index of Agreement function
+TEST_F(TestMathUtils, CheckIndexOfAgreement) {
+ ASSERT_DOUBLE_EQ(std::isnan(::MathUtils::ioa(x, x)), true);
+ // ASSERT_DOUBLE_EQ(::MathUtils::ioa(x, y), double(0));
+ // ASSERT_DOUBLE_EQ(::MathUtils::ioa(y, z), 0.18594436310395313);
+}
+
+// Test of the Standard Deviation function
+TEST_F(TestMathUtils, CheckStandardDeviation) {
+ ASSERT_DOUBLE_EQ(::MathUtils::sd(x), 0);
+ ASSERT_DOUBLE_EQ(::MathUtils::sd(y), 0.8539125638299665);
+ ASSERT_DOUBLE_EQ(::MathUtils::sd(z), 0.8539125638299665);
+}
+
+// Test to check the Mean (float)
+TEST_F(TestMathUtils, CheckFloatMean) {
+ ASSERT_FLOAT_EQ(::MathUtils::mean(v), 0);
+ ASSERT_FLOAT_EQ(::MathUtils::mean(w), -0.33333333333333331);
+ ASSERT_FLOAT_EQ(::MathUtils::mean(x), 0);
+ ASSERT_FLOAT_EQ(::MathUtils::mean(y), 2.25);
+ ASSERT_FLOAT_EQ(::MathUtils::mean(z), -2.25);
+}
+
+// Test to check the Mean (double)
+TEST_F(TestMathUtils, CheckDoubleMean) {
+ std::vector a(v.begin(), v.end());
+ std::vector b(w.begin(), w.end());
+ std::vector c(x.begin(), x.end());
+ std::vector d(y.begin(), y.end());
+ std::vector e(z.begin(), z.end());
+ ASSERT_DOUBLE_EQ(::MathUtils::mean(a), 0);
+ ASSERT_DOUBLE_EQ(::MathUtils::mean(b), -0.33333333333333331);
+ ASSERT_DOUBLE_EQ(::MathUtils::mean(c), 0);
+ ASSERT_DOUBLE_EQ(::MathUtils::mean(d), 2.25);
+ ASSERT_DOUBLE_EQ(::MathUtils::mean(e), -2.25);
+}
+
+// Test to check the Median (float)
+TEST_F(TestMathUtils, CheckFloatMedian) {
+ ASSERT_FLOAT_EQ(::MathUtils::median(v), 0);
+ ASSERT_FLOAT_EQ(::MathUtils::median(w), 0);
+ ASSERT_FLOAT_EQ(::MathUtils::median(x), 0);
+ ASSERT_FLOAT_EQ(::MathUtils::median(y), 2.5);
+ ASSERT_FLOAT_EQ(::MathUtils::median(z), -2);
+}
+
+// Test to check the Median (double)
+TEST_F(TestMathUtils, CheckDoubleMedian) {
+ std::vector a(v.begin(), v.end());
+ std::vector b(w.begin(), w.end());
+ std::vector c(x.begin(), x.end());
+ std::vector d(y.begin(), y.end());
+ std::vector e(z.begin(), z.end());
+ ASSERT_DOUBLE_EQ(::MathUtils::median(a), 0);
+ ASSERT_DOUBLE_EQ(::MathUtils::median(b), 0);
+ ASSERT_DOUBLE_EQ(::MathUtils::median(c), 0);
+ ASSERT_DOUBLE_EQ(::MathUtils::median(d), 2.5);
+ ASSERT_DOUBLE_EQ(::MathUtils::median(e), -2);
+}
+
+// Test to get the probability density function
+TEST_F(TestMathUtils, CheckProbabilityDensityFunction) {
+ std::vector
+ bins1({-5, 0, 5}),
+ bins2({-10, -5, -2.5, 0, 2.5, 5, 10});
+ std::vector target({2, 4});
+
+ std::vector pdf = ::MathUtils::get_pdf(v, bins1);
+ ASSERT_EQ(pdf.size(), bins1.size() - 1);
+
+ for (unsigned i = 0; i < bins1.size() - 1; i++)
+ ASSERT_EQ(pdf[i], target[i]);
+}
+
+// Test to get the cumulative distribution function
+TEST_F(TestMathUtils, CheckCumulativeDistributionFunction) {
+ std::vector
+ bins1({-5, 0, 5}),
+ bins2({-10, -5, -2.5, 0, 2.5, 5, 10});
+ std::vector target({0, 2, 6});
+
+ std::vector cdf = ::MathUtils::get_cdf(v, bins1);
+ ASSERT_EQ(cdf.size(), bins1.size());
+
+ for (unsigned i = 0; i < bins1.size() - 1; i++)
+ ASSERT_EQ(cdf[i], target[i]);
+}
+// Test the linear inpterpolation
+TEST_F(TestMathUtils, CheckLinearInterpolation) {
+ std::vector targets({2, -1.5, -3, 12, 3, -9});
+ for (unsigned i = 0; i < v.size(); i++)
+ ASSERT_EQ(targets[i], ::MathUtils::lerp(v[i], w[i], z[i]));
+}
+
+// Test linear interpolation of a value - 2-dimensional
+TEST_F(TestMathUtils, CheckLinearInterpolation2d) {
+ std::vector
+ xData({1.12, 1.1456, 1.234, 12.345, 13.456, 14.5678}),
+ yData({0.1, 0.5, -12, 1.2245, 17.98, 25.98}),
+ x({13, -1.223, -3.23, 3.33, 5.44, 0.9}),
+ targets_no_extrapolation({11.102849714462536, 0.10000000149011612, 0.10000000149011612, -9.5053053412748589, -6.9939476845575568, 0.10000000149011612}),
+ targets_with_extrapolation({11.102849714462536, -36.509437637865666, -67.868866230547567, -9.5053053412748589, -6.9939476845575568, -3.3375059476496061});
+
+ for (unsigned i = 0; i < x.size(); i++) {
+ ASSERT_EQ(targets_no_extrapolation[i], ::MathUtils::interpolate(xData, yData, x[i], false));
+ ASSERT_EQ(targets_with_extrapolation[i], ::MathUtils::interpolate(xData, yData, x[i], true));
+ }
+}
+} // namespace
+} // namespace MathUtils
+} // namespace TestBiasAdjustCXX
diff --git a/tests/src/TestNcFileHandler.cxx b/tests/src/TestNcFileHandler.cxx
new file mode 100644
index 0000000..810dfb6
--- /dev/null
+++ b/tests/src/TestNcFileHandler.cxx
@@ -0,0 +1,50 @@
+// -*- lsst-c++ -*-
+/**
+ * @file TestNcFileHandler.cxx
+ * @brief Implements the unit tests of the NcFileHandler class
+ * @author Benjamin Thomas Schwertfeger
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
+ *
+ * * Copyright (C) 2023 Benjamin Thomas Schwertfeger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "NcFileHandler.hxx"
+#include "gtest/gtest.h"
+
+namespace TestBiasAdjustCXX {
+namespace NcFileHandler {
+namespace {
+
+// The fixture for testing class NcFileHandler.
+class TestNcFileHandler : public ::testing::Test {
+ protected:
+ TestNcFileHandler() {
+ }
+
+ ~TestNcFileHandler() override {
+ }
+
+ void SetUp() override {
+ }
+
+ void TearDown() override {
+ }
+};
+
+} // namespace
+} // namespace NcFileHandler
+} // namespace TestBiasAdjustCXX
diff --git a/tests/src/TestUtils.cxx b/tests/src/TestUtils.cxx
new file mode 100644
index 0000000..cced537
--- /dev/null
+++ b/tests/src/TestUtils.cxx
@@ -0,0 +1,154 @@
+// -*- lsst-c++ -*-
+/**
+ * @file TestUtils.cxx
+ * @brief Implements the unit tests of the Utils class
+ * @author Benjamin Thomas Schwertfeger
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
+ *
+ * * Copyright (C) 2023 Benjamin Thomas Schwertfeger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "Utils.hxx"
+#include "gtest/gtest.h"
+
+namespace TestBiasAdjustCXX {
+namespace Utils {
+namespace {
+
+// The fixture for testing class Utills.
+class TestUtils : public ::testing::Test {
+ protected:
+ // You can remove any or all of the following functions if their bodies would
+ // be empty.
+
+ TestUtils() : sbuf{nullptr} {
+ // You can do set-up work for each test here.
+ }
+
+ ~TestUtils() override {
+ // You can do clean-up work that doesn't throw exceptions here.
+ }
+
+ // If the constructor and destructor are not enough for setting up
+ // and cleaning up each test, you can define the following methods:
+
+ void SetUp() override {
+ // Code here will be called immediately after the constructor (right
+ // before each test).
+ // Save cout's buffer...
+ sbuf = std::cout.rdbuf();
+ // Redirect cout to our stringstream buffer or any other ostream
+ std::cout.rdbuf(buffer.rdbuf());
+ }
+
+ // Called after each unit test
+ void TearDown() override {
+ // Code here will be called immediately after each test (right
+ // before the destructor).
+ // When done redirect cout to its old self
+ std::cout.rdbuf(sbuf);
+ sbuf = nullptr;
+ }
+
+ // The following objects can be reused in each unit test
+
+ // This can be an ofstream as well or any other ostream
+ std::stringstream buffer{};
+ // Save cout's buffer here
+ std::streambuf *sbuf;
+};
+
+// Test if Log class can write debug statements
+TEST_F(TestUtils, CheckLogCanDebug) {
+ const utils::Log log = utils::Log();
+ std::string expected{
+ "\x1B[37mDEBUG: \x1B[0mTest\n"};
+ log.debug("Test");
+ std::string actual{buffer.str()};
+ EXPECT_EQ(expected, actual);
+}
+
+// Test if Log class can write info statements
+TEST_F(TestUtils, CheckLogCanInfo) {
+ const utils::Log log = utils::Log();
+ std::string expected{
+ "\x1B[32mINFO: \x1B[0mTest\n"};
+ log.info("Test");
+ std::string actual{buffer.str()};
+ EXPECT_EQ(expected, actual);
+}
+
+// Test if Log class can write warning statements
+TEST_F(TestUtils, CheckLogCanWarning) {
+ const utils::Log log = utils::Log();
+ std::string expected{
+ "\x1B[33mWARNING: \x1B[0mTest\n"};
+ log.warning("Test");
+ std::string actual{buffer.str()};
+ EXPECT_EQ(expected, actual);
+}
+
+// Test if Log class can write error statements
+TEST_F(TestUtils, CheckLogCanError) {
+ const utils::Log log = utils::Log();
+ std::string expected{
+ "\x1B[1m\x1B[31mERROR: \x1B[0mTest\n"};
+ log.error("Test");
+ std::string actual{buffer.str()};
+ EXPECT_EQ(expected, actual);
+}
+
+// Tests the progress bar
+TEST_F(TestUtils, CheckProgressBar) {
+ std::string expected{
+ "50 / 100 [ ################################### ] 50 %\r"};
+ utils::progress_bar(50.0, 100.0);
+ std::string actual{buffer.str()};
+ EXPECT_EQ(expected, actual);
+}
+
+// Tests if a string is in a vector
+TEST_F(TestUtils, CheckStringInVector) {
+ const std::string string_to_find = "linear_scaling";
+ const std::vector v({"linear_scaling", "delta_method"});
+ EXPECT_EQ(utils::isInStrV(v, string_to_find), true);
+}
+
+// Tests if a string is not in a vector
+TEST_F(TestUtils, CheckStringNotInVector) {
+ const std::string string_to_find = "linear_scaling";
+ const std::vector v({"quantile_mapping", "delta_method"});
+ EXPECT_EQ(utils::isInStrV(v, string_to_find), false);
+}
+
+// Tests the copyright notice
+TEST_F(TestUtils, CheckCopyrightNotice) {
+ std::string expected{
+ "BiasAdjustCXX Copyright (C) 2023 Benjamin Thomas Schwertfeger"
+ "\nThis program comes with ABSOLUTELY NO WARRANTY."
+ "\nThis is free software, and you are welcome to redistribute it"
+ "\nunder certain conditions; type 'show -c' for details.\n\n"};
+ utils::show_copyright_notice("BiasAdjustCXX");
+ std::string actual{buffer.str()};
+ EXPECT_EQ(expected, actual);
+}
+
+} // namespace
+} // namespace Utils
+} // namespace TestBiasAdjustCXX
diff --git a/tests/src/main.cxx b/tests/src/main.cxx
new file mode 100644
index 0000000..33238eb
--- /dev/null
+++ b/tests/src/main.cxx
@@ -0,0 +1,31 @@
+// -*- lsst-c++ -*-
+/**
+ * @file main.c++
+ * @brief
+ * @author Benjamin Thomas Schwertfeger
+ * @link https://b-schwertfeger.de
+ * @copyright Benjamin Thomas Schwertfeger
+ * @link https://b-schwertfeger.de
+ * @github https://github.com/btschwertfeger/BiasAdjustCXX
+ *
+ * * Copyright (C) 2023 Benjamin Thomas Schwertfeger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "gtest/gtest.h"
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/CMakeLists.txt b/validation/CMakeLists.txt
similarity index 100%
rename from test/CMakeLists.txt
rename to validation/CMakeLists.txt
diff --git a/test/README.md b/validation/README.md
similarity index 95%
rename from test/README.md
rename to validation/README.md
index 6367849..73fe844 100644
--- a/test/README.md
+++ b/validation/README.md
@@ -1,5 +1,7 @@
# Validation/Testing of the adjusted time series
+> β οΈ This part is not actively maintained!
+
`ComputeIndicator.cxx` enables creating a matrix that contains for example the $RMSE$ of every time series between two data sets.
## Compilation:
diff --git a/test/src/ComputeIndicator.cxx b/validation/src/ComputeIndicator.cxx
similarity index 97%
rename from test/src/ComputeIndicator.cxx
rename to validation/src/ComputeIndicator.cxx
index 1bc7eed..c729c2b 100644
--- a/test/src/ComputeIndicator.cxx
+++ b/validation/src/ComputeIndicator.cxx
@@ -4,9 +4,8 @@
* @file ComputeIndicator.cxx
* @brief Program to compute data sets containing statistical indicator results (like MBE, RMSE, ...)
* @author Benjamin Thomas Schwertfeger
- * @email: development@b-schwertfeger.de
- * @link https://b-schwertfeger.de
- * @github https://github.com/btschwertfeger/BiasAdjustCXX
+ * @email: contact@b-schwertfeger.de
+ * @link https://github.com/btschwertfeger/BiasAdjustCXX
*
* * Copyright (C) 2023 Benjamin Thomas Schwertfeger
*
@@ -21,7 +20,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
/*
@@ -275,4 +274,4 @@ int main(int argc, char** argv) {
/**
* * ----- ----- E O F ----- ------ ----- ------ ----- ------ ----- ------ ----- ------ ----- ------ ----- ------ |
- */
\ No newline at end of file
+ */