diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2a06666c0ff..32c976df706 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -4,9 +4,52 @@ on: [push, pull_request]
env:
ERT_SHOW_BACKTRACE: 1
+ ECL_SKIP_SIGNAL: 1
jobs:
- build-and-test:
+ build-test-cmake:
+ name: CMake
+
+ strategy:
+ fail-fast: false
+ matrix:
+ os: ['ubuntu-latest', 'macos-latest']
+
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Install Ubuntu dependencies
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ sudo apt-get update -y
+ sudo apt-get install -y valgrind
+
+ - name: Build libecl
+ run: |
+ git clone https://github.com/equinor/libecl
+ mkdir libecl/build
+ cmake -S libecl -B libecl/build
+ sudo cmake --build libecl/build --target install
+ sudo rm -rf libecl
+
+ - name: Build libres
+ run: |
+ mkdir cmake-build
+ cmake -S libres -B cmake-build \
+ -DBUILD_TESTS=ON \
+ -DRES_VERSION=1.2.3 \
+ -DCMAKE_BUILD_TYPE=RelWithDebInfo
+ cmake --build cmake-build
+
+ - name: Run tests
+ run: |
+ cd cmake-build
+ export PATH=$PWD/bin:$PATH
+ ctest --output-on-failure
+
+ build-wheels:
timeout-minutes: 30
strategy:
fail-fast: false
@@ -21,73 +64,227 @@ jobs:
with:
fetch-depth: 0
+ - name: Install Ubuntu dependencies
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ sudo apt-get update
+ sudo apt-get install libxcb-image0 libxcb-icccm4 libxcb-keysyms1 libxcb-randr0 libxcb-render0 libxcb-render-util0 libxcb-shape0 libxcb-shm0 libxcb-xfixes0 libxcb-xinerama0 libfontconfig1 libxcb-xkb1 libxkbcommon-x11-0 libdbus-1-3 x11-xserver-utils herbstluftwm
+
+ - name: Build Linux Wheel
+ uses: docker://quay.io/pypa/manylinux2010_x86_64
+ with:
+ entrypoint: /github/workspace/ci/github/build_linux_wheel.sh
+ args: ${{ matrix.python-version }}
+ if: matrix.os == 'ubuntu-latest'
+
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
+ - name: Build macOS Wheel
+ run: pip wheel . --no-deps -w dist
+ if: matrix.os == 'macos-latest'
+
+ - name: Upload wheel as artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: ${{ matrix.os }} Python ${{ matrix.python-version }} wheel
+ path: dist/*
+
+ tests-ert:
+ name: Run ert tests
+ needs: [build-wheels]
+ timeout-minutes: 30
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: [3.6, 3.7, 3.8, 3.9]
+ os: [ubuntu-latest, macos-latest]
+ runs-on: ${{ matrix.os }}
+
+ steps:
- name: Install Ubuntu dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install libxcb-image0 libxcb-icccm4 libxcb-keysyms1 libxcb-randr0 libxcb-render0 libxcb-render-util0 libxcb-shape0 libxcb-shm0 libxcb-xfixes0 libxcb-xinerama0 libfontconfig1 libxcb-xkb1 libxkbcommon-x11-0 libdbus-1-3 x11-xserver-utils herbstluftwm
- - name: Install ERT and dependencies
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Get wheels
+ uses: actions/download-artifact@v2
+ with:
+ name: ${{ matrix.os }} Python ${{ matrix.python-version }} wheel
+
+ - name: Install wheel and test dependencies
run: |
+ find . -name "*.whl" -exec pip install '{}' \;
pip install -r dev-requirements.txt
- pip install .[storage]
+
+ - name: Make test directory
+ run: |
+ mkdir tmp_tests
+ mv tests tmp_tests/tests
+ mv test-data tmp_tests/test-data
+ mv setup.cfg tmp_tests/setup.cfg
+ mkdir tmp_tests/.git
- name: Test Ubuntu
if: matrix.os == 'ubuntu-latest'
env:
DISPLAY: ':99.0'
run: |
+ pushd tmp_tests
+
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 640x480x24 -ac +extension GLX
sleep 3
herbstluftwm &
sleep 1
- python -m pytest -sv
+ python -c "import sys; print(sys.path)"
+ pytest -sv
+ popd
- name: Test MacOS
if: matrix.os == 'macos-latest'
run: |
- python -m pytest -sv
+ pushd tmp_tests
- - name: Produce narratives
- run: |
- NARRATIVE_DIR=$(pwd)/narratives python -m pytest -m consumer_driven_contract_verification
+ pytest -sv
+ popd
- - name: Test docs and CLI
+ - name: Test CLI
run: |
- NARRATIVE_DIR=$(pwd)/narratives sphinx-build -n -v -E -W ./docs/rst/manual ./tmp/ert_docs
ert --help
- publish:
- name: Publish to PyPI
- runs-on: ubuntu-latest
- needs: [build-and-test]
-
- # If this is a tagged release
- if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
+ docs-ert:
+ name: Test ert docs
+ needs: [build-wheels]
+ timeout-minutes: 3
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: [3.6, 3.9]
+ os: [ubuntu-latest]
+ runs-on: ${{ matrix.os }}
steps:
+ - name: Install Ubuntu dependencies
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ sudo apt-get update
+ sudo apt-get install libxcb-image0 libxcb-icccm4 libxcb-keysyms1 libxcb-randr0 libxcb-render0 libxcb-render-util0 libxcb-shape0 libxcb-shm0 libxcb-xfixes0 libxcb-xinerama0 libfontconfig1 libxcb-xkb1 libxkbcommon-x11-0 libdbus-1-3 x11-xserver-utils herbstluftwm
+
- uses: actions/checkout@v2
with:
fetch-depth: 0
- - name: Set up Python 3.9
+ - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
- python-version: 3.9
+ python-version: ${{ matrix.python-version }}
+
+ - name: Get wheels
+ uses: actions/download-artifact@v2
+ with:
+ name: ${{ matrix.os }} Python ${{ matrix.python-version }} wheel
+
+ - name: Install wheel and test dependencies
+ run: |
+ find . -name "*.whl" -exec pip install '{}' \;
+ pip install -r dev-requirements.txt
+
+ - name: Make test directory
+ run: |
+ mkdir tmp_tests
+ mv tests tmp_tests/tests
+ mv test-data tmp_tests/test-data
+ mv setup.cfg tmp_tests/setup.cfg
+ mkdir tmp_tests/.git
+
+ - name: Produce narratives
+ run: |
+ export NARRATIVE_DIR=$(pwd)/narratives
+ pushd tmp_tests
+ pytest -m consumer_driven_contract_verification
+ popd
+
+ - name: Test docs
+ run: |
+ NARRATIVE_DIR=$(pwd)/narratives sphinx-build -n -v -E -W ./docs/rst/manual ./tmp/ert_docs
+
+ tests-libres:
+ name: Run libres tests
+ needs: [build-wheels]
+ timeout-minutes: 30
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: [3.6, 3.7, 3.8, 3.9]
+ os: [ubuntu-latest, macos-latest]
+ runs-on: ${{ matrix.os }}
- - name: Build wheel
+ steps:
+ - name: Install Ubuntu dependencies
+ if: matrix.os == 'ubuntu-latest'
run: |
- pip install --upgrade pip wheel setuptools
- pip wheel . --no-deps -w dist
+ sudo apt-get update
+ sudo apt-get install libxcb-image0 libxcb-icccm4 libxcb-keysyms1 libxcb-randr0 libxcb-render0 libxcb-render-util0 libxcb-shape0 libxcb-shm0 libxcb-xfixes0 libxcb-xinerama0 libfontconfig1 libxcb-xkb1 libxkbcommon-x11-0 libdbus-1-3 x11-xserver-utils herbstluftwm
+
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
- - name: Publish to PyPI
- uses: pypa/gh-action-pypi-publish@v1.3.1
+ - name: Get wheels
+ uses: actions/download-artifact@v2
with:
- user: statoil-travis
- password: ${{ secrets.pypi_password }}
+ name: ${{ matrix.os }} Python ${{ matrix.python-version }} wheel
+
+ - name: Install wheel and test dependencies
+ run: |
+ find . -name "*.whl" -exec pip install '{}' \;
+ pip install -r dev-requirements.txt
+
+ - name: Run Python tests
+ run: |
+ # Run tests
+ pytest libres/tests
+
+
+ publish:
+ name: Publish to PyPI
+ runs-on: ubuntu-latest
+ needs: [tests-libres, tests-ert, docs-ert]
+
+ # If this is a tagged release
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
+
+ steps:
+ - name: Get wheels
+ uses: actions/download-artifact@v2
+ with:
+ path: artifacts
+
+ - name: Move to dist/
+ run: |
+ mkdir dist
+ find artifacts -name "*.whl" -exec mv '{}' dist/ \;
+
+ - name: Publish to PyPI
+ uses: pypa/gh-action-pypi-publish@v1.3.1
+ with:
+ user: statoil-travis
+ password: ${{ secrets.pypi_password }}
diff --git a/.github/workflows/run_ert2_test_data_setups.yml b/.github/workflows/run_ert2_test_data_setups.yml
index 8bbc6e684cf..65d27cac0fd 100644
--- a/.github/workflows/run_ert2_test_data_setups.yml
+++ b/.github/workflows/run_ert2_test_data_setups.yml
@@ -7,7 +7,7 @@ env:
jobs:
run-ert2-test-data:
- timeout-minutes: 15
+ timeout-minutes: 20
strategy:
fail-fast: false
matrix:
diff --git a/.github/workflows/run_examples_polynomial.yml b/.github/workflows/run_examples_polynomial.yml
index 26c6af4031e..6dfcf3f4dfe 100644
--- a/.github/workflows/run_examples_polynomial.yml
+++ b/.github/workflows/run_examples_polynomial.yml
@@ -39,7 +39,7 @@ jobs:
popd
run-ert3-polynomial-postgres:
- timeout-minutes: 15
+ timeout-minutes: 20
services:
postgres:
image: postgres:10.8
diff --git a/.github/workflows/run_examples_spe1.yml b/.github/workflows/run_examples_spe1.yml
index a262995ce68..de4f115297a 100644
--- a/.github/workflows/run_examples_spe1.yml
+++ b/.github/workflows/run_examples_spe1.yml
@@ -4,7 +4,7 @@ on: [push, pull_request]
jobs:
run-ert3-spe1:
- timeout-minutes: 15
+ timeout-minutes: 20
services:
postgres:
image: postgres:10.8
diff --git a/.github/workflows/typing.yml b/.github/workflows/typing.yml
index 5824e9781a0..4ce77106750 100644
--- a/.github/workflows/typing.yml
+++ b/.github/workflows/typing.yml
@@ -22,7 +22,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- python -m pip install mypy
+ python -m pip install mypy==0.812
- name: Run mypy
run: |
mypy ert3
diff --git a/.gitignore b/.gitignore
index 3a679174e72..05f945524c8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ __res_lib_info.py
*.egg*
/dist
/ert_shared/version.py
-
/venv
.hypothesis
+/_skbuild
+.libs
diff --git a/.mypy-strict.ini b/.mypy-strict.ini
index 324d5e3ea6d..d2e4aa93972 100644
--- a/.mypy-strict.ini
+++ b/.mypy-strict.ini
@@ -13,3 +13,15 @@ ignore_missing_imports = True
[mypy-pandas.*]
ignore_missing_imports = True
+
+[mypy-res.*]
+ignore_errors = True
+
+[mypy-cwrap.*]
+ignore_errors = True
+
+[mypy-ecl.*]
+ignore_errors = True
+
+[mypy-job_runner.*]
+ignore_errors = True
diff --git a/.mypy.ini b/.mypy.ini
index 22bd972c7d5..92d66465afd 100644
--- a/.mypy.ini
+++ b/.mypy.ini
@@ -19,6 +19,12 @@ ignore_missing_imports = True
[mypy-res.*]
ignore_missing_imports = True
+[mypy-cwrap.*]
+ignore_missing_imports = True
+
+[mypy-ecl.*]
+ignore_missing_imports = True
+
[mypy-cloudevents.*]
ignore_missing_imports = True
@@ -33,4 +39,3 @@ ignore_missing_imports = True
[mypy-graphlib.*]
ignore_missing_imports = True
-
diff --git a/Jenkinsfile-libres b/Jenkinsfile-libres
new file mode 100644
index 00000000000..596c301f451
--- /dev/null
+++ b/Jenkinsfile-libres
@@ -0,0 +1,46 @@
+pipeline {
+ agent { label 'scout-ci7' }
+ environment {
+ WORKING_DIR = sh(script: 'pwd', , returnStdout: true).trim()
+ }
+ stages {
+ stage('setup') {
+ steps {
+ echo "Started with WORKING_DIR: ${WORKING_DIR}"
+ sh 'sh libres/testjenkins.sh setup'
+ }
+ }
+ stage('build libres') {
+ steps {
+ sh 'sh libres/testjenkins.sh build_libecl'
+ sh 'sh libres/testjenkins.sh build_libres'
+ }
+ }
+ stage('build res') {
+ steps {
+ sh 'sh libres/testjenkins.sh build_res'
+ }
+ }
+ stage('run ctest') {
+ steps {
+ sh 'sh libres/testjenkins.sh run_ctest'
+ }
+ }
+ stage('run pytest_equinor') {
+ steps {
+ sh 'sh libres/testjenkins.sh run_pytest_equinor'
+ }
+ }
+ stage('run pytest_normal') {
+ steps {
+ sh 'sh libres/testjenkins.sh run_pytest_normal'
+ }
+ }
+ }
+
+ post {
+ cleanup {
+ deleteDir()
+ }
+ }
+}
diff --git a/README.md b/README.md
index b18c5755d91..0b9945a4ef4 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,8 @@ ERT is now Python 3 only. The last Python 2 compatible release is [2.14](https:/
## Developing
-ERT is pure Python software. To start developing, install it in editable mode:
+*ERT* is Python and C software. To start developing, install it in editable
+mode:
```
$ git clone https://github.com/equinor/ert
@@ -55,6 +56,52 @@ $ pip install -r dev-requirements.txt
$ pytest tests/
```
+ERT is meant to be installed using `setup.py`, directly or using `pip
+install ./`. The `CMakeLists.txt` in libres exists, but is used by `setup.py`
+to generate the ERT C library (the C library formerly known as *libres*) and
+by Github Actions to run C tests.
+
+ERT requires a recent version of `pip` - hence you are advised to upgrade
+your `pip` installation with
+
+```sh
+$ pip install --upgrade pip
+```
+If your `pip` version is too old the installation of ERT will fail, and the error messages will be incomprehensible.
+
+### Testing C code
+
+Install [*ecl*](https://github.com/Equinor/ecl) using CMake as a C library. Then:
+
+``` sh
+$ mkdir build
+$ cd build
+$ cmake ../libres -DBUILD_TESTS=ON
+$ cmake --build .
+$ ctest --output-on-failure
+```
+
+### Building
+
+Use the following commands to start developing from a clean virtualenv
+```
+$ pip install -r requirements.txt
+$ python setup.py develop
+```
+
+Alternatively, `pip install -e .` will also setup ERT for development, but
+it will be more difficult to recompile the C library.
+
+[scikit-build](https://scikit-build.readthedocs.io/en/latest/index.html) is used
+for compiling the C library. It creates a directory named `_skbuild` which is
+reused upon future invocations of either `python setup.py develop`, or `python
+setup.py build_ext`. The latter only rebuilds the C library. In some cases this
+directory must be removed in order for compilation to succeed.
+
+The C library files get installed into `res/.libs`, which is where the
+`res` module will look for them.
+
+
## Example usage
To actually get ert to work at your site you need to configure details about
@@ -140,3 +187,37 @@ start `ert` by giving the full path to the installed binary:
Then the `ert` gui should come up and you can press the `Run simulations`
button. In addition to the gui there is a simple text interface which
can be invoked with the `--text` option.
+
+
+## Configuration
+
+### The `site_config` file
+As part of the installation process ERT will install a file called
+`site-config` in `share/ert/site-config`; when ert starts this file will be
+loaded before the users personal config file. For more extensive use of `ert` it
+might be beneficial to customize the `site-config` file to your personal site.
+
+To customize, you need to set the environment variable `ERT_SITE_CONFIG` to
+point to an alternative file that will be used.
+
+### 6.2 Forward models
+
+ERT contains basic functionality for forward models to run the reservoir
+simulators Eclipse/flow and the geomodelling program RMS. Exactly how these
+programs depend on the setup on your site and you must make some modifications
+to two files installed with ERT:
+
+#### 6.2.1. Eclipse/flow configuration
+
+In the Python distribution installed by ERT there is a file
+`res/fm/ecl/ecl_config.yml` which is used to configure the eclipse/flow versions
+are available at the location. You can provide an alternative configuration file
+by setting the environment variable `ECL_SITE_CONFIG`.
+
+#### 6.2.2. RMS configuration
+
+In the Python distribution installed by ERT there is a file:
+`res/fm/rms/rms_config.yml` which contains some site specific RMS configuration.
+You should provide an alternative file with your local path to the `rms` wrapper
+script supplied by _Roxar_ by setting the environment variable `RMS_SITE_CONFIG`
+to point to the alternative file.
diff --git a/ci/github/build_linux_wheel.sh b/ci/github/build_linux_wheel.sh
new file mode 100755
index 00000000000..110ef657358
--- /dev/null
+++ b/ci/github/build_linux_wheel.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+set -euo pipefail
+
+case "$1" in
+ 3.6) pyver=cp36-cp36m ;;
+ 3.7) pyver=cp37-cp37m ;;
+ 3.8) pyver=cp38-cp38 ;;
+ 3.9) pyver=cp39-cp39 ;;
+ *)
+ echo "Unknown Python version $1"
+ exit 1
+ ;;
+esac
+
+# Install dependencies
+yum install -y lapack-devel blas-devel
+
+# Build wheel
+cd /github/workspace
+/opt/python/$pyver/bin/pip wheel . --no-deps -w wheelhouse
+auditwheel repair wheelhouse/* -w dist
diff --git a/ci/jenkins/testkomodo.sh b/ci/jenkins/testkomodo.sh
index 47af8cff24b..2a57b605d2f 100755
--- a/ci/jenkins/testkomodo.sh
+++ b/ci/jenkins/testkomodo.sh
@@ -3,8 +3,21 @@ copy_test_files () {
cp -r $CI_SOURCE_ROOT/tests $CI_TEST_ROOT/tests
cp -r $CI_SOURCE_ROOT/test-data $CI_TEST_ROOT/test-data
cp -r $CI_SOURCE_ROOT/ert3_examples $CI_TEST_ROOT/ert3_examples
+
# Trick ERT to find a fake source root
mkdir $CI_TEST_ROOT/.git
+
+ # libres
+ mkdir ${CI_TEST_ROOT}/libres
+ mkdir -p ${CI_TEST_ROOT}/libres/res/fm/rms/
+ ln -s ${CI_SOURCE_ROOT}/res/fm/rms/rms_config.yml ${CI_TEST_ROOT}/libres/res/fm/rms/rms_config.yml
+
+ cp -r {$CI_SOURCE_ROOT,$CI_TEST_ROOT}/libres/tests
+ ln -s {$CI_SOURCE_ROOT,$CI_TEST_ROOT}/libres/test-data
+ ln -s {$CI_SOURCE_ROOT,$CI_TEST_ROOT}/libres/lib
+ ln -s {$CI_SOURCE_ROOT,$CI_TEST_ROOT}/libres/bin
+
+ ln -s $CI_SOURCE_ROOT/share ${CI_TEST_ROOT}/share
}
install_test_dependencies () {
@@ -27,5 +40,18 @@ start_tests () {
# Allow xvfb to find a new server
xvfb-run -s "-screen 0 640x480x24" --auto-servernum python -m \
pytest -k "not test_gui_load and not test_formatting" \
- -m "not requires_window_manager"
+ -m "not requires_window_manager" --ignore ${CI_TEST_ROOT}/libres
+
+ pushd ${CI_TEST_ROOT}/libres
+ export ECL_SKIP_SIGNAL=ON
+ pytest \
+ --ignore="tests/res/enkf/test_analysis_config.py" \
+ --ignore="tests/res/enkf/test_res_config.py" \
+ --ignore="tests/res/enkf/test_site_config.py" \
+ --ignore="tests/res/enkf/test_workflow_list.py" \
+ --ignore="tests/res/enkf/test_hook_manager.py" \
+ --ignore="tests/legacy" \
+ --ignore="tests/test_formatting.py"
+ popd
+
}
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 86c17d2bd0f..d9bb64357f1 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,4 +1,5 @@
black
+ert-storage==0.1.7
flaky
pytest>=6.2.0
pytest-qt
@@ -11,3 +12,6 @@ requests
pytest-timeout
jsonpath_ng
sphinxcontrib.datatemplates
+decorator
+pylint
+click
diff --git a/ert_gui/gert_main.py b/ert_gui/gert_main.py
index 550e1e97b2b..3bd8b223534 100755
--- a/ert_gui/gert_main.py
+++ b/ert_gui/gert_main.py
@@ -86,11 +86,7 @@ def _start_window(ert, args):
window.activateWindow()
window.raise_()
- ResLog.log(
- 3,
- "Versions: ecl:%s res:%s ert:%s"
- % (ecl.__version__, res.__version__, ert_gui.__version__),
- )
+ ResLog.log(3, f"Versions: ecl:{ecl.__version__} ert:{ert_gui.__version__}")
if not ert._real_enkf_main().have_observations():
QMessageBox.warning(
diff --git a/ert_gui/simulation/view/__init__.py b/ert_gui/simulation/view/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/ert_logger/__init__.py b/ert_logger/__init__.py
new file mode 100644
index 00000000000..651904871b3
--- /dev/null
+++ b/ert_logger/__init__.py
@@ -0,0 +1,36 @@
+import os, pwd, json, requests
+from typing import Any
+from copy import deepcopy
+
+LOG_URL = "http://devnull.statoil.no:4444"
+USER = pwd.getpwuid(os.getuid()).pw_name
+APPLICATION_NAME = "ERT"
+BASE_MESSAGE = {
+ "user": USER,
+ "application": APPLICATION_NAME,
+ "komodo_release": os.getenv("KOMODO_RELEASE", "--------"),
+}
+
+
+# Disabled temporarily, according to issue #1095.
+#
+# The tests in tests/job_runner/test_network_reporter.py are disabled
+# accordingly.
+#
+def log_message(input_payload: Any) -> None:
+ # payload = deepcopy(BASE_MESSAGE)
+ # payload.update(input_payload)
+ # try:
+ # data = json.dumps(payload)
+ # # Disabling proxies
+ # proxies = {"http": None, "https": None}
+ # requests.post(
+ # LOG_URL,
+ # timeout=3,
+ # headers={"Content-Type": "application/json"},
+ # data=data,
+ # proxies=proxies,
+ # )
+ # except: # noqa
+ # pass
+ pass
diff --git a/ert_shared/ensemble_evaluator/sync_ws_duplexer.py b/ert_shared/ensemble_evaluator/sync_ws_duplexer.py
index 0cf4d153c6a..0d30ac06af1 100644
--- a/ert_shared/ensemble_evaluator/sync_ws_duplexer.py
+++ b/ert_shared/ensemble_evaluator/sync_ws_duplexer.py
@@ -5,7 +5,7 @@
from concurrent.futures import CancelledError
import websockets
from ert_shared.ensemble_evaluator.utils import wait_for_evaluator
-from websockets.client import WebSocketClientProtocol
+from websockets.client import WebSocketClientProtocol # type: ignore
from websockets.datastructures import Headers
diff --git a/ert_shared/hook_implementations/jobs.py b/ert_shared/hook_implementations/jobs.py
index e9d02c78478..8fc2d8ba30d 100644
--- a/ert_shared/hook_implementations/jobs.py
+++ b/ert_shared/hook_implementations/jobs.py
@@ -1,19 +1,24 @@
-import inspect
import os
from jinja2 import Template
-import res
from ert_shared.plugins.plugin_manager import hook_implementation
from ert_shared.plugins.plugin_response import plugin_response
+from pathlib import Path
def _resolve_ert_share_path():
- share_path = os.path.realpath(
- os.path.join(os.path.dirname(inspect.getfile(res)), "../../../../share/ert")
- )
- return share_path
+ path = Path(__file__).parent
+ for p in path.parents:
+ npath = p / "share" / "ert"
+ if npath.is_dir():
+ path = npath
+ break
+ else:
+ raise ImportError("Could not find `share/ert`")
+
+ return str(path)
def _get_jobs_from_directories(directories):
diff --git a/ert_shared/status/entity/state.py b/ert_shared/status/entity/state.py
index 625812e8458..fd19ce2d88b 100644
--- a/ert_shared/status/entity/state.py
+++ b/ert_shared/status/entity/state.py
@@ -63,23 +63,23 @@
}
QUEUE_WAITING_FLAG = (
- JobStatusType.JOB_QUEUE_NOT_ACTIVE
+ JobStatusType.JOB_QUEUE_NOT_ACTIVE # type: ignore[operator]
| JobStatusType.JOB_QUEUE_WAITING
| JobStatusType.JOB_QUEUE_SUBMITTED
)
QUEUE_PENDING_FLAG = JobStatusType.JOB_QUEUE_PENDING
QUEUE_RUNNING_FLAG = (
- JobStatusType.JOB_QUEUE_RUNNING
- | JobStatusType.JOB_QUEUE_EXIT
+ JobStatusType.JOB_QUEUE_RUNNING # type: ignore[operator]
+ | JobStatusType.JOB_QUEUE_EXIT # type: ignore[operator]
| JobStatusType.JOB_QUEUE_RUNNING_DONE_CALLBACK
| JobStatusType.JOB_QUEUE_RUNNING_EXIT_CALLBACK
)
# Failed also includes simulations which have been killed by the MAX_RUNTIME system.
-QUEUE_FAILED_FLAG = JobStatusType.JOB_QUEUE_IS_KILLED | JobStatusType.JOB_QUEUE_DO_KILL
+QUEUE_FAILED_FLAG = JobStatusType.JOB_QUEUE_IS_KILLED | JobStatusType.JOB_QUEUE_DO_KILL # type: ignore[operator]
QUEUE_FAILED_FLAG |= (
- JobStatusType.JOB_QUEUE_FAILED | JobStatusType.JOB_QUEUE_DO_KILL_NODE_FAILURE
-)
-QUEUE_DONE_FLAG = JobStatusType.JOB_QUEUE_DONE | JobStatusType.JOB_QUEUE_SUCCESS
+ JobStatusType.JOB_QUEUE_FAILED | JobStatusType.JOB_QUEUE_DO_KILL_NODE_FAILURE # type: ignore[operator]
+) # type: ignore[operator]
+QUEUE_DONE_FLAG = JobStatusType.JOB_QUEUE_DONE | JobStatusType.JOB_QUEUE_SUCCESS # type: ignore[operator]
QUEUE_UNKNOWN_FLAG = JobStatusType.JOB_QUEUE_UNKNOWN
QUEUE_FLAG_TO_REAL_STATE = {
diff --git a/job_runner/__init__.py b/job_runner/__init__.py
new file mode 100644
index 00000000000..dbfb19a91e3
--- /dev/null
+++ b/job_runner/__init__.py
@@ -0,0 +1,3 @@
+CERT_FILE = ".ee.pem"
+JOBS_FILE = "jobs.json"
+LOG_URL = "http://devnull.statoil.no:4444"
diff --git a/job_runner/cli.py b/job_runner/cli.py
new file mode 100644
index 00000000000..a1cf13707ce
--- /dev/null
+++ b/job_runner/cli.py
@@ -0,0 +1,77 @@
+import argparse
+import os
+import signal
+import sys
+import json
+
+import job_runner.reporting as reporting
+from job_runner.reporting.message import Finish
+from job_runner.runner import JobRunner
+from job_runner import JOBS_FILE
+
+
+def _setup_reporters(
+ is_interactive_run, ee_id, evaluator_url, ee_token=None, ee_cert_path=None
+):
+ reporters = []
+ if is_interactive_run:
+ reporters.append(reporting.Interactive())
+ elif ee_id:
+ reporters.append(reporting.File(sync_disc_timeout=0))
+ reporters.append(reporting.Network())
+ reporters.append(
+ reporting.Event(
+ evaluator_url=evaluator_url, token=ee_token, cert_path=ee_cert_path
+ )
+ )
+ else:
+ reporters.append(reporting.File())
+ reporters.append(reporting.Network())
+ return reporters
+
+
+def main(args):
+
+ parser = argparse.ArgumentParser(
+ description="Run all the jobs specified in jobs.json, or specify the names of the jobs to run."
+ )
+ parser.add_argument("run_path", nargs="?", help="Path where jobs.json is located")
+ parser.add_argument(
+ "job",
+ nargs="*",
+ help="One or more jobs to be executed from the jobs.json file. If no jobs are specified, all jobs will be executed.",
+ )
+
+ parsed_args = parser.parse_args(args[1:])
+
+ # If run_path is defined, enter into that directory
+ if parsed_args.run_path is not None:
+ if not os.path.exists(parsed_args.run_path):
+ sys.exit("No such directory: {}".format(parsed_args.run_path))
+ os.chdir(parsed_args.run_path)
+
+ ee_id = None
+ try:
+ with open(JOBS_FILE, "r") as json_file:
+ jobs_data = json.load(json_file)
+ ee_id = jobs_data.get("ee_id")
+ ee_token = jobs_data.get("ee_token")
+ ee_cert_path = jobs_data.get("ee_cert_path")
+ evaluator_url = jobs_data.get("dispatch_url")
+ except ValueError as e:
+ raise IOError("Job Runner cli failed to load JSON-file.{}".format(str(e)))
+
+ is_interactive_run = len(parsed_args.job) > 0
+ reporters = _setup_reporters(
+ is_interactive_run, ee_id, evaluator_url, ee_token, ee_cert_path
+ )
+
+ job_runner = JobRunner(jobs_data)
+
+ for job_status in job_runner.run(parsed_args.job):
+ for reporter in reporters:
+ reporter.report(job_status)
+
+ if isinstance(job_status, Finish) and not job_status.success():
+ pgid = os.getpgid(os.getpid())
+ os.killpg(pgid, signal.SIGKILL)
diff --git a/job_runner/io/__init__.py b/job_runner/io/__init__.py
new file mode 100644
index 00000000000..dc23cd061fd
--- /dev/null
+++ b/job_runner/io/__init__.py
@@ -0,0 +1,34 @@
+import os
+
+
+def cond_unlink(file):
+ if os.path.exists(file):
+ os.unlink(file)
+
+
+def assert_file_executable(fname):
+ """The function raises an IOError if the given file is either not a file or
+ not an executable.
+
+ If the given file name is an absolute path, its functionality is straight
+ forward. When given a relative path it will look for the given file in the
+ current directory as well as all locations specified by the environment
+ path.
+
+ """
+ if not fname:
+ raise IOError("No executable provided!")
+ fname = os.path.expanduser(fname)
+
+ potential_executables = [os.path.abspath(fname)]
+ if not os.path.isabs(fname):
+ potential_executables = potential_executables + [
+ os.path.join(location, fname)
+ for location in os.environ["PATH"].split(os.pathsep)
+ ]
+
+ if not any(map(os.path.isfile, potential_executables)):
+ raise IOError("{} is not a file!".format(fname))
+
+ if not any([os.access(fn, os.X_OK) for fn in potential_executables]):
+ raise IOError("{} is not an executable!".format(fname))
diff --git a/job_runner/job.py b/job_runner/job.py
new file mode 100644
index 00000000000..59a0f8577f1
--- /dev/null
+++ b/job_runner/job.py
@@ -0,0 +1,255 @@
+import json
+import os
+import signal
+import time
+from datetime import datetime as dt
+from subprocess import Popen
+
+from psutil import Process, TimeoutExpired, NoSuchProcess, AccessDenied, ZombieProcess
+
+from job_runner.io import assert_file_executable
+from job_runner.reporting.message import Exited, Running, Start
+
+
+class Job(object):
+ MEMORY_POLL_PERIOD = 5 # Seconds between memory polls
+
+ def __init__(self, job_data, index, sleep_interval=1):
+ self.sleep_interval = sleep_interval
+ self.job_data = job_data
+ self.index = index
+ self.std_err = None
+ self.std_out = None
+ if "stderr" in job_data and job_data["stderr"]:
+ self.std_err = job_data["stderr"]
+ if "stdout" in job_data and job_data["stdout"]:
+ self.std_out = job_data["stdout"]
+
+ def run(self):
+ start_message = Start(self)
+
+ errors = self._check_job_files()
+
+ errors.extend(self._assert_arg_list())
+
+ self._dump_exec_env()
+
+ if errors:
+ yield start_message.with_error("\n".join(errors))
+ return
+
+ yield start_message
+
+ executable = self.job_data.get("executable")
+ assert_file_executable(executable)
+
+ arg_list = [executable]
+ if self.job_data.get("argList"):
+ arg_list += self.job_data["argList"]
+
+ if self.job_data.get("stdin"):
+ stdin = open(self.job_data.get("stdin"))
+ else:
+ stdin = None
+
+ if self.std_err:
+ stderr = open(self.std_err, "w")
+ else:
+ stderr = None
+
+ if self.std_out:
+ stdout = open(self.std_out, "w")
+ else:
+ stdout = None
+
+ if self.job_data.get("target_file"):
+ target_file_mtime = 0
+ if os.path.exists(self.job_data["target_file"]):
+ stat = os.stat(self.job_data["target_file"])
+ target_file_mtime = stat.st_mtime
+
+ exec_env = self.job_data.get("exec_env")
+ if exec_env:
+ exec_name, _ = os.path.splitext(
+ os.path.basename(self.job_data.get("executable"))
+ )
+ with open("{}_exec_env.json".format(exec_name), "w") as f:
+ f.write(json.dumps(exec_env))
+
+ max_running_minutes = self.job_data.get("max_running_minutes")
+ run_start_time = dt.now()
+
+ proc = Popen(
+ arg_list,
+ stdin=stdin,
+ stdout=stdout,
+ stderr=stderr,
+ env=self.job_data.get("environment"),
+ )
+
+ exit_code = None
+
+ process = Process(proc.pid)
+ max_memory_usage = 0
+ while exit_code is None:
+ try:
+ memory = process.memory_info().rss
+ except (NoSuchProcess, AccessDenied, ZombieProcess):
+ """In case of a process that has died and is in some
+ transitional state, we ignore any failures. Only seen on OSX
+ thus far.
+ See https://github.com/giampaolo/psutil/issues/1044#issuecomment-298745532
+ """
+ memory = 0
+ if memory > max_memory_usage:
+ max_memory_usage = memory
+
+ yield Running(self, max_memory_usage, memory)
+
+ try:
+ exit_code = process.wait(timeout=self.MEMORY_POLL_PERIOD)
+ except TimeoutExpired:
+ run_time = dt.now() - run_start_time
+ if (
+ max_running_minutes is not None
+ and run_time.seconds > max_running_minutes * 60
+ ):
+ """
+ If the spawned process is not in the same process group
+ as the callee (job_dispatch), we will kill the process
+ group explicitly.
+
+ Propagating the unsuccessful Exited message will kill the
+ callee group. See job_dispatch.py.
+ """
+ process_group_id = os.getpgid(proc.pid)
+ this_group_id = os.getpgid(os.getpid())
+ if process_group_id != this_group_id:
+ os.killpg(process_group_id, signal.SIGKILL)
+
+ yield Exited(self, exit_code).with_error(
+ "Job:{} has been running for more than {} minutes - explicitly killed.".format(
+ self.name(), max_running_minutes
+ )
+ )
+ return
+
+ exited_message = Exited(self, exit_code)
+
+ if exit_code != 0:
+ yield exited_message.with_error(
+ "Process exited with status code {}".format(exit_code)
+ )
+ return
+
+ # exit_code is 0
+
+ if self.job_data.get("error_file"):
+ if os.path.exists(self.job_data["error_file"]):
+ yield exited_message.with_error(
+ "Found the error file:{} - job failed.".format(
+ self.job_data["error_file"]
+ )
+ )
+ return
+
+ if self.job_data.get("target_file"):
+ target_file_error = self._check_target_file_is_written(target_file_mtime)
+ if target_file_error:
+ yield exited_message.with_error(target_file_error)
+ return
+
+ yield exited_message
+
+ def _assert_arg_list(self):
+ errors = []
+ if "arg_types" in self.job_data:
+ arg_types = self.job_data["arg_types"]
+ arg_list = self.job_data.get("argList")
+ for index, arg_type in enumerate(arg_types):
+ if arg_type == "RUNTIME_FILE":
+ file_path = os.path.join(os.getcwd(), arg_list[index])
+ if not os.path.isfile(file_path):
+ errors.append(
+ "In job {}: RUNTIME_FILE {} does not exist.".format(
+ self.name(), arg_list[index]
+ )
+ )
+ if arg_type == "RUNTIME_INT":
+ try:
+ int(arg_list[index])
+ except ValueError:
+ errors.append(
+ "In job {}: argument with index {} is of incorrect type, should be integer.".format(
+ self.name(), index
+ )
+ )
+ return errors
+
+ def name(self):
+ return self.job_data["name"]
+
+ def _dump_exec_env(self):
+ exec_env = self.job_data.get("exec_env")
+ if exec_env:
+ exec_name, _ = os.path.splitext(
+ os.path.basename(self.job_data.get("executable"))
+ )
+ with open("{}_exec_env.json".format(exec_name), "w") as f:
+ f.write(json.dumps(exec_env))
+
+ def _check_job_files(self):
+ """
+ Returns the empty list if no failed checks, or a list of errors in case
+ of failed checks.
+ """
+ errors = []
+ if self.job_data.get("stdin"):
+ if not os.path.exists(self.job_data["stdin"]):
+ errors.append(
+ "Could not locate stdin file: {}".format(self.job_data["stdin"])
+ )
+
+ if self.job_data.get("start_file"):
+ if not os.path.exists(self.job_data["start_file"]):
+ errors.append(
+ "Could not locate start_file:{}".format(self.job_data["start_file"])
+ )
+
+ if self.job_data.get("error_file"):
+ if os.path.exists(self.job_data.get("error_file")):
+ os.unlink(self.job_data.get("error_file"))
+
+ return errors
+
+ def _check_target_file_is_written(self, target_file_mtime, timeout=5):
+ """
+ Check whether or not a target_file eventually appear. Returns None in
+ case of success, an error message in the case of failure.
+ """
+ # no target file is expected at all, indicate success
+ if "target_file" not in self.job_data:
+ return None
+
+ target_file = self.job_data["target_file"]
+
+ start_time = time.time()
+ while True:
+ if os.path.exists(target_file):
+ stat = os.stat(target_file)
+ if stat.st_mtime > target_file_mtime:
+ return None
+
+ time.sleep(self.sleep_interval)
+ if time.time() - start_time > timeout:
+ break
+
+ # We have gone out of the loop via the break statement,
+ # i.e. on a timeout.
+ if os.path.exists(target_file):
+ stat = os.stat(target_file)
+ return "The target file:{} has not been updated; this is flagged as failure. mtime:{} stat_start_time:{}".format(
+ target_file, stat.st_mtime, target_file_mtime
+ )
+ else:
+ return "Could not find target_file:{}".format(target_file)
diff --git a/job_runner/job_dispatch.py b/job_runner/job_dispatch.py
new file mode 100644
index 00000000000..488529a386c
--- /dev/null
+++ b/job_runner/job_dispatch.py
@@ -0,0 +1,20 @@
+import os
+import signal
+import sys
+
+from job_runner.cli import main as job_runner_main
+
+
+def sigterm_handler(_signo, _stack_frame):
+ signal.signal(signal.SIGTERM, signal.SIG_DFL)
+ os.kill(0, signal.SIGTERM)
+
+
+def main():
+ os.nice(19)
+ signal.signal(signal.SIGTERM, sigterm_handler)
+ job_runner_main(sys.argv)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/job_runner/reporting/__init__.py b/job_runner/reporting/__init__.py
new file mode 100644
index 00000000000..01d6980e6a0
--- /dev/null
+++ b/job_runner/reporting/__init__.py
@@ -0,0 +1,8 @@
+"""
+The reporting package provides classes for reporting the results of forward
+model jobs.
+"""
+from .file import File
+from .interactive import Interactive
+from .network import Network
+from .event import Event
diff --git a/job_runner/reporting/event.py b/job_runner/reporting/event.py
new file mode 100644
index 00000000000..665c724daf1
--- /dev/null
+++ b/job_runner/reporting/event.py
@@ -0,0 +1,167 @@
+from cloudevents.http import CloudEvent, to_json
+from job_runner.reporting.message import (
+ Exited,
+ Finish,
+ Init,
+ Running,
+ Start,
+)
+import queue
+import threading
+from pathlib import Path
+from job_runner.util.client import Client
+
+_FM_JOB_START = "com.equinor.ert.forward_model_job.start"
+_FM_JOB_RUNNING = "com.equinor.ert.forward_model_job.running"
+_FM_JOB_SUCCESS = "com.equinor.ert.forward_model_job.success"
+_FM_JOB_FAILURE = "com.equinor.ert.forward_model_job.failure"
+
+
+class TransitionError(ValueError):
+ pass
+
+
+class Event:
+ def __init__(self, evaluator_url, token=None, cert_path=None):
+ self._evaluator_url = evaluator_url
+ self._token = token
+ if cert_path is not None:
+ with open(cert_path) as f:
+ self._cert = f.read()
+ else:
+ self._cert = None
+
+ self._ee_id = None
+ self._real_id = None
+ self._step_id = None
+ self._event_queue = queue.Queue()
+ self._event_publisher_thread = threading.Thread(target=self._publish_event)
+ self._initialize_state_machine()
+
+ def _publish_event(self):
+ with Client(self._evaluator_url, self._token, self._cert) as client:
+ while True:
+ event = self._event_queue.get()
+ if event is None:
+ return
+ client.send(to_json(event).decode())
+
+ def _initialize_state_machine(self):
+ initialized = (Init,)
+ jobs = (Start, Running, Exited)
+ finished = (Finish,)
+ self._states = {
+ initialized: self._init_handler,
+ jobs: self._job_handler,
+ finished: self._finished_handler,
+ }
+ self._transitions = {
+ None: initialized,
+ initialized: jobs + finished,
+ jobs: jobs + finished,
+ }
+ self._state = None
+
+ def report(self, msg):
+ new_state = None
+ for state in self._states.keys():
+ if isinstance(msg, state):
+ new_state = state
+
+ if self._state not in self._transitions or not isinstance(
+ msg, self._transitions[self._state]
+ ):
+ raise TransitionError(
+ f"Illegal transition {self._state} -> {new_state} for {msg}, expected to transition into {self._transitions[self._state]}"
+ )
+
+ self._states[new_state](msg)
+ self._state = new_state
+
+ def _dump_event(self, event):
+ self._event_queue.put(event)
+
+ def _step_path(self):
+ return f"/ert/ee/{self._ee_id}/real/{self._real_id}/step/{self._step_id}"
+
+ def _init_handler(self, msg):
+ self._ee_id = msg.ee_id
+ self._real_id = msg.real_id
+ self._step_id = msg.step_id
+ self._event_publisher_thread.start()
+
+ def _job_handler(self, msg):
+ job_path = f"{self._step_path()}/job/{msg.job.index}"
+
+ if isinstance(msg, Start):
+ self._dump_event(
+ CloudEvent(
+ {
+ "type": _FM_JOB_START,
+ "source": job_path,
+ "datacontenttype": "application/json",
+ },
+ {
+ "stdout": str(Path(msg.job.std_out).resolve()),
+ "stderr": str(Path(msg.job.std_err).resolve()),
+ },
+ )
+ )
+ if not msg.success():
+ self._dump_event(
+ CloudEvent(
+ {
+ "type": _FM_JOB_FAILURE,
+ "source": job_path,
+ "datacontenttype": "application/json",
+ },
+ {
+ "error_msg": msg.error_message,
+ },
+ )
+ )
+
+ elif isinstance(msg, Exited):
+ if msg.success():
+ self._dump_event(
+ CloudEvent(
+ {
+ "type": _FM_JOB_SUCCESS,
+ "source": job_path,
+ },
+ None,
+ )
+ )
+ else:
+ self._dump_event(
+ CloudEvent(
+ {
+ "type": _FM_JOB_FAILURE,
+ "source": job_path,
+ "datacontenttype": "application/json",
+ },
+ {
+ "exit_code": msg.exit_code,
+ "error_msg": msg.error_message,
+ },
+ )
+ )
+
+ elif isinstance(msg, Running):
+ self._dump_event(
+ CloudEvent(
+ {
+ "type": _FM_JOB_RUNNING,
+ "source": job_path,
+ "datacontenttype": "application/json",
+ },
+ {
+ "max_memory_usage": msg.max_memory_usage,
+ "current_memory_usage": msg.current_memory_usage,
+ },
+ )
+ )
+
+ def _finished_handler(self, msg):
+ self._event_queue.put(None)
+ self._event_publisher_thread.join()
diff --git a/job_runner/reporting/file.py b/job_runner/reporting/file.py
new file mode 100644
index 00000000000..33fa097f7a8
--- /dev/null
+++ b/job_runner/reporting/file.py
@@ -0,0 +1,188 @@
+import json
+import os
+import socket
+import time
+
+from job_runner.io import cond_unlink
+from job_runner.reporting.message import (
+ _JOB_STATUS_FAILURE,
+ _JOB_STATUS_RUNNING,
+ _JOB_STATUS_SUCCESS,
+ Exited,
+ Finish,
+ Init,
+ Running,
+ Start,
+)
+from job_runner.util import data as data_util
+
+
+class File(object):
+ LOG_file = "JOB_LOG"
+ ERROR_file = "ERROR"
+ STATUS_file = "STATUS"
+ OK_file = "OK"
+ STATUS_json = "status.json"
+
+ def __init__(self, sync_disc_timeout=10):
+ self.status_dict = {}
+ self.node = socket.gethostname()
+ self._sync_disc_timeout = sync_disc_timeout
+
+ def report(self, msg):
+ job_status = {}
+ if msg.job:
+ index = msg.job.index
+ job_status = self.status_dict["jobs"][index]
+
+ if isinstance(msg, Init):
+ self._delete_old_status_files()
+ self._init_status_file()
+ self.status_dict = self._init_job_status_dict(
+ msg.timestamp, msg.run_id, msg.jobs
+ )
+
+ elif isinstance(msg, Start):
+ if msg.success():
+ self._start_status_file(msg)
+ self._add_log_line(msg.job)
+ job_status["status"] = _JOB_STATUS_RUNNING
+ job_status["start_time"] = data_util.datetime_serialize(msg.timestamp)
+ else:
+ error_msg = msg.error_message
+ job_status["status"] = _JOB_STATUS_FAILURE
+ job_status["error"] = error_msg
+ job_status["end_time"] = data_util.datetime_serialize(msg.timestamp)
+
+ self._complete_status_file(msg)
+ elif isinstance(msg, Exited):
+ job_status["end_time"] = data_util.datetime_serialize(msg.timestamp)
+
+ if msg.success():
+ job_status["status"] = _JOB_STATUS_SUCCESS
+ self._complete_status_file(msg)
+ else:
+ error_msg = msg.error_message
+ job_status["error"] = error_msg
+ job_status["status"] = _JOB_STATUS_FAILURE
+
+ # A STATUS_file is not written if there is no exit_code, i.e.
+ # when the job is killed due to timeout.
+ if msg.exit_code:
+ self._complete_status_file(msg)
+ self._dump_error_file(msg.job, error_msg)
+
+ elif isinstance(msg, Running):
+ job_status["max_memory_usage"] = msg.max_memory_usage
+ job_status["current_memory_usage"] = msg.current_memory_usage
+ job_status["status"] = _JOB_STATUS_RUNNING
+
+ elif isinstance(msg, Finish):
+ if msg.success():
+ self.status_dict["end_time"] = data_util.datetime_serialize(
+ msg.timestamp
+ )
+ self._dump_ok_file()
+ else:
+ # this has already been handled by earlier event
+ pass
+
+ self._dump_status_json()
+
+ def _delete_old_status_files(self):
+ cond_unlink(self.ERROR_file)
+ cond_unlink(self.STATUS_file)
+ cond_unlink(self.OK_file)
+
+ def _init_status_file(self):
+ with open(self.STATUS_file, "a") as f:
+ f.write("{:32}: {}/{}\n".format("Current host", self.node, os.uname()[4]))
+
+ def _init_job_status_dict(self, start_time, run_id, jobs):
+ return {
+ "run_id": run_id,
+ "start_time": data_util.datetime_serialize(start_time),
+ "end_time": None,
+ "jobs": [data_util.create_job_dict(j) for j in jobs],
+ }
+
+ def _start_status_file(self, msg):
+ with open(self.STATUS_file, "a") as f:
+ f.write("{:32}: {:%H:%M:%S} .... ".format(msg.job.name(), msg.timestamp))
+
+ def _complete_status_file(self, msg):
+ if msg.success():
+ status = ""
+ else:
+ # There was no status code in the case of STARTUP_ERROR, so use
+ # an arbitrary code less than -9.
+ if isinstance(msg, Start):
+ exit_code = -10
+ else:
+ exit_code = msg.exit_code
+
+ status = " EXIT: {}/{}".format(exit_code, msg.error_message)
+ with open(self.STATUS_file, "a") as f:
+ f.write("{:%H:%M:%S} {}\n".format(msg.timestamp, status))
+
+ def _add_log_line(self, job):
+ now = time.localtime()
+ with open(self.LOG_file, "a") as f:
+ args = " ".join(job.job_data["argList"])
+ f.write(
+ "{:02d}:{:02d}:{:02d} Calling: {} {}\n".format(
+ now.tm_hour,
+ now.tm_min,
+ now.tm_sec,
+ job.job_data["executable"],
+ args,
+ )
+ )
+
+ # This file will be read by the job_queue_node_fscanf_EXIT() function
+ # in job_queue.c. Be very careful with changes in output format.
+ def _dump_error_file(self, job, error_msg):
+ fileH = open(self.ERROR_file, "a")
+ now = time.localtime()
+ fileH.write("\n")
+ fileH.write(
+ " \n".format(
+ now.tm_hour, now.tm_min, now.tm_sec
+ )
+ )
+ fileH.write(" {}\n".format(job.name()))
+ fileH.write(" {}\n".format(error_msg))
+ stderr_file = None
+ if job.std_err:
+ if os.path.exists(job.std_err):
+ with open(job.std_err, "r") as errH:
+ stderr = errH.read()
+ if stderr:
+ stderr_file = os.path.join(os.getcwd(), job.std_err)
+ else:
+ stderr = "\n".format(job.name())
+ else:
+ stderr = "\n".format(job.std_err)
+ else:
+ stderr = "\n"
+
+ fileH.write(" \n{}\n".format(stderr))
+ if stderr_file:
+ fileH.write(" {}\n".format(stderr_file))
+
+ fileH.write("\n")
+ fileH.close()
+
+ def _dump_ok_file(self):
+ now = time.localtime()
+ with open(self.OK_file, "w") as f:
+ f.write(
+ "All jobs complete {:02d}:{:02d}:{:02d} \n".format(
+ now.tm_hour, now.tm_min, now.tm_sec
+ )
+ )
+ time.sleep(self._sync_disc_timeout) # Let the disks sync up
+
+ def _dump_status_json(self):
+ with open(self.STATUS_json, "w") as fp:
+ json.dump(self.status_dict, fp, indent=1)
diff --git a/job_runner/reporting/interactive.py b/job_runner/reporting/interactive.py
new file mode 100644
index 00000000000..ced3e0d6dbc
--- /dev/null
+++ b/job_runner/reporting/interactive.py
@@ -0,0 +1,19 @@
+from job_runner.reporting.message import Finish, Start
+
+
+class Interactive:
+ def report(self, msg):
+ if isinstance(msg, Start):
+ print("Running job: {} ... ".format(msg.job.name()))
+ elif not msg.success() and not isinstance(msg, Finish):
+ print(
+ """failed ....
+ -----------------------------------------------------------------
+ Error: {}
+ -----------------------------------------------------------------
+ """.format(
+ msg.error_message
+ )
+ )
+ elif isinstance(msg, Finish) and msg.success():
+ print("OK")
diff --git a/job_runner/reporting/message.py b/job_runner/reporting/message.py
new file mode 100644
index 00000000000..18bd190166e
--- /dev/null
+++ b/job_runner/reporting/message.py
@@ -0,0 +1,72 @@
+from datetime import datetime as dt
+
+_JOB_STATUS_SUCCESS = "Success"
+_JOB_STATUS_RUNNING = "Running"
+_JOB_STATUS_FAILURE = "Failure"
+_JOB_STATUS_WAITING = "Waiting"
+
+_RUNNER_STATUS_INITIALIZED = "Initialized"
+_RUNNER_STATUS_SUCCESS = "Success"
+_RUNNER_STATUS_FAILURE = "Failure"
+
+
+class _MetaMessage(type):
+ def __repr__(cls):
+ return f"MessageType<{cls.__name__}>"
+
+
+class Message(metaclass=_MetaMessage):
+ def __init__(self, job=None):
+ self.timestamp = dt.now()
+ self.job = job
+ self.error_message = None
+
+ def __repr__(self):
+ return type(self).__name__
+
+ def with_error(self, message):
+ self.error_message = message
+ return self
+
+ def success(self):
+ return self.error_message is None
+
+
+# manager level messages
+
+
+class Init(Message):
+ def __init__(self, jobs, run_id, ert_pid, ee_id=None, real_id=None, step_id=None):
+ super(Init, self).__init__()
+ self.jobs = jobs
+ self.run_id = run_id
+ self.ert_pid = ert_pid
+ self.ee_id = ee_id
+ self.real_id = real_id
+ self.step_id = step_id
+
+
+class Finish(Message):
+ def __init__(self):
+ super(Finish, self).__init__()
+
+
+# job level messages
+
+
+class Start(Message):
+ def __init__(self, job):
+ super(Start, self).__init__(job)
+
+
+class Running(Message):
+ def __init__(self, job, max_memory_usage, current_memory_usage):
+ super(Running, self).__init__(job)
+ self.max_memory_usage = max_memory_usage
+ self.current_memory_usage = current_memory_usage
+
+
+class Exited(Message):
+ def __init__(self, job, exit_code):
+ super(Exited, self).__init__(job)
+ self.exit_code = exit_code
diff --git a/job_runner/reporting/network.py b/job_runner/reporting/network.py
new file mode 100644
index 00000000000..3350766d84f
--- /dev/null
+++ b/job_runner/reporting/network.py
@@ -0,0 +1,109 @@
+import json
+import os
+import pwd
+import socket
+import sys
+
+from sys import version as sys_version
+
+import requests
+from ecl import EclVersion
+from job_runner.reporting.message import Exited, Init, Finish
+from job_runner.util import pad_nonexisting, read_os_release
+from res import ResVersion
+from ert_logger import log_message
+
+
+class Network(object):
+ def __init__(self):
+ self.simulation_id = None
+ self.ert_pid = None
+ self.start_time = None
+ self.node = socket.gethostname()
+
+ pw_entry = pwd.getpwuid(os.getuid())
+ self.user = pw_entry.pw_name
+
+ def report(self, msg):
+ if isinstance(msg, Init):
+ self.start_time = msg.timestamp
+ self.simulation_id = msg.run_id
+ self.ert_pid = msg.ert_pid
+
+ self._post_initial(msg)
+ elif isinstance(msg, Exited):
+ if not msg.success():
+ self._post_job_failure(msg)
+ elif isinstance(msg, Finish):
+ if msg.success():
+ self._post_success(msg)
+
+ def _post_initial(self, msg):
+ os_info = read_os_release()
+ _, _, release, _, _ = os.uname()
+ python_vs, _ = sys_version.split("\n")
+ ecl_v = EclVersion()
+ res_v = ResVersion()
+ logged_fields = {
+ "status": "init",
+ "python_sys_path": list(map(pad_nonexisting, sys.path)),
+ "pythonpath": list(
+ map(pad_nonexisting, os.environ.get("PYTHONPATH", "").split(":"))
+ ),
+ "res_version": res_v.versionString(),
+ "ecl_version": ecl_v.versionString(),
+ "LSB_ID": os_info.get("LSB_ID", ""),
+ "LSB_VERSION_ID": os_info.get("LSB_VERSION_ID", ""),
+ "python_version": python_vs,
+ "kernel_version": release,
+ }
+
+ job_list = [j.name() for j in msg.jobs]
+ logged_fields.update({"jobs": job_list})
+
+ self._post_message(msg.timestamp, extra_fields=logged_fields)
+
+ def _post_message(self, timestamp, extra_fields=None):
+ payload = {
+ "cwd": os.getcwd(),
+ "subsystem": "ert_forward_model",
+ "node": self.node,
+ "start_time": self.start_time.isoformat(),
+ "node_timestamp": timestamp.isoformat(),
+ "simulation_id": self.simulation_id,
+ "ert_pid": self.ert_pid,
+ }
+ payload.update(extra_fields)
+ log_message(payload)
+
+ def _post_success(self, msg):
+ self._post_message(msg.timestamp, {"status": "OK"})
+
+ def _post_job_failure(self, msg):
+ fields = {
+ "status": "exit",
+ "exit_status": msg.exit_code,
+ "finished": True,
+ "error": True,
+ "error_msg": msg.error_message,
+ "ert_job": msg.job.name(),
+ "executable": msg.job.job_data["executable"],
+ "arg_list": " ".join(msg.job.job_data["argList"]),
+ }
+ fields.update(self._extract_stderr_stdout(msg.job))
+ self._post_message(msg.timestamp, fields)
+
+ def _extract_stderr_stdout(self, job):
+ extra_fields = {}
+ if job.std_err:
+ if os.path.exists(job.std_err):
+ with open(job.std_err, "r") as errH:
+ stderr = errH.read()
+ extra_fields.update({"stderr": stderr})
+ if job.std_out:
+ if os.path.exists(job.std_out):
+ with open(job.std_out, "r") as outH:
+ stdout = outH.read()
+ extra_fields.update({"stdout": stdout})
+
+ return extra_fields
diff --git a/job_runner/runner.py b/job_runner/runner.py
new file mode 100644
index 00000000000..307d8b5bb82
--- /dev/null
+++ b/job_runner/runner.py
@@ -0,0 +1,87 @@
+import json
+import os
+
+from job_runner.job import Job
+from job_runner.reporting.message import Init, Finish
+
+
+class JobRunner(object):
+ def __init__(self, jobs_data):
+ os.umask(int(jobs_data["umask"], 8))
+
+ self._data_root = jobs_data.get("DATA_ROOT")
+ if self._data_root:
+ os.environ["DATA_ROOT"] = self._data_root
+
+ self.simulation_id = jobs_data.get("run_id")
+ self.ee_id = jobs_data.get("ee_id")
+ self.real_id = jobs_data.get("real_id")
+ self.step_id = jobs_data.get("step_id")
+ self.ert_pid = jobs_data.get("ert_pid")
+ self.global_environment = jobs_data.get("global_environment")
+ self.global_update_path = jobs_data.get("global_update_path")
+ job_data_list = jobs_data["jobList"]
+
+ if self.simulation_id is not None:
+ os.environ["ERT_RUN_ID"] = self.simulation_id
+
+ self.jobs = []
+ for index, job_data in enumerate(job_data_list):
+ self.jobs.append(Job(job_data, index))
+
+ self._set_environment()
+ self._update_path()
+
+ def run(self, names_of_jobs_to_run):
+ # if names_of_jobs_to_run, create job_queue which contains jobs that
+ # are to be run.
+ if not names_of_jobs_to_run:
+ job_queue = self.jobs
+ else:
+ job_queue = [j for j in self.jobs if j.name() in names_of_jobs_to_run]
+
+ init_message = Init(
+ job_queue,
+ self.simulation_id,
+ self.ert_pid,
+ self.ee_id,
+ self.real_id,
+ self.step_id,
+ )
+
+ unused = set(names_of_jobs_to_run) - set([j.name() for j in job_queue])
+ if unused:
+ init_message.with_error(
+ "{} does not exist. Available jobs: {}".format(
+ unused, [j.name() for j in self.jobs]
+ )
+ )
+ yield init_message
+ return
+ else:
+ yield init_message
+
+ for job in job_queue:
+ for status_update in job.run():
+ yield status_update
+
+ if not status_update.success():
+ yield Finish().with_error("Not all jobs completed successfully.")
+ return
+
+ yield Finish()
+
+ def _set_environment(self):
+ if self.global_environment:
+ data = self.global_environment
+ for key in data.keys():
+ os.environ[key] = data[key]
+
+ def _update_path(self):
+ if self.global_update_path:
+ data = self.global_update_path
+ for key in data.keys():
+ if os.environ.get(key):
+ os.environ[key] = data[key] + ":" + os.environ[key]
+ else:
+ os.environ[key] = data[key]
diff --git a/job_runner/util/__init__.py b/job_runner/util/__init__.py
new file mode 100644
index 00000000000..50914db84f0
--- /dev/null
+++ b/job_runner/util/__init__.py
@@ -0,0 +1,29 @@
+import sys
+import os
+
+
+def read_os_release(pfx="LSB_"):
+ fname = "/etc/os-release"
+ if not os.path.isfile(fname):
+ return {}
+
+ def processline(ln):
+ return ln.strip().replace('"', "")
+
+ def splitline(ln, pfx=""):
+ if ln.count("=") == 1:
+ k, v = ln.split("=")
+ return pfx + k, v
+ return None
+
+ props = {}
+ with open(fname, "r") as f:
+ for line in f:
+ kv = splitline(processline(line), pfx=pfx)
+ if kv:
+ props[kv[0]] = kv[1]
+ return props
+
+
+def pad_nonexisting(path, pad="-- "):
+ return path if os.path.exists(path) else pad + path
diff --git a/job_runner/util/client.py b/job_runner/util/client.py
new file mode 100644
index 00000000000..6a5d62cb3e2
--- /dev/null
+++ b/job_runner/util/client.py
@@ -0,0 +1,81 @@
+import websockets
+import asyncio
+import cloudevents
+import ssl
+from websockets.exceptions import ConnectionClosed, ConnectionClosedOK
+from websockets.datastructures import Headers
+
+
+class Client:
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_traceback):
+ if self.websocket is not None:
+ self.loop.run_until_complete(self.websocket.close())
+ self.loop.close()
+
+ def __init__(
+ self, url, token=None, cert=None, max_retries=10, timeout_multiplier=5
+ ):
+ if url is None:
+ raise ValueError("url was None")
+ self.url = url
+ self.token = token
+ self._extra_headers = Headers()
+ if token is not None:
+ self._extra_headers["token"] = token
+
+ # Mimics the behavior of the ssl argument when connection to
+ # websockets. If none is specified it will deduce based on the url,
+ # if True it will enforce TLS, and if you want to use self signed
+ # certificates you need to pass an ssl_context with the certificate
+ # loaded.
+ if cert is not None:
+ ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+ ssl_context.load_verify_locations(cadata=cert)
+ else:
+ ssl_context = True if url.startswith("wss") else None
+ self._ssl_context = ssl_context
+
+ self._max_retries = max_retries
+ self._timeout_multiplier = timeout_multiplier
+ self.websocket = None
+ self.loop = asyncio.new_event_loop()
+
+ async def get_websocket(self):
+ return await websockets.connect(
+ self.url, ssl=self._ssl_context, extra_headers=self._extra_headers
+ )
+
+ async def _send(self, msg):
+ for retry in range(self._max_retries + 1):
+ try:
+ if self.websocket is None:
+ self.websocket = await self.get_websocket()
+ await self.websocket.send(msg)
+ return
+ except ConnectionClosedOK:
+ # Connection was closed no point in trying to send more messages
+ raise
+ except (ConnectionClosed, ConnectionRefusedError, OSError):
+ if retry == self._max_retries:
+ raise
+ await asyncio.sleep(0.2 + self._timeout_multiplier * retry)
+ self.websocket = None
+
+ def send(self, msg):
+ self.loop.run_until_complete(self._send(msg))
+
+ def send_event(self, ev_type, ev_source, ev_data=None):
+ if ev_data is None:
+ ev_data = {}
+ event = cloudevents.http.CloudEvent(
+ {
+ "type": ev_type,
+ "source": ev_source,
+ "datacontenttype": "application/json",
+ },
+ ev_data,
+ )
+ self.send(cloudevents.http.to_json(event).decode())
diff --git a/job_runner/util/data.py b/job_runner/util/data.py
new file mode 100644
index 00000000000..6fd309eadd0
--- /dev/null
+++ b/job_runner/util/data.py
@@ -0,0 +1,33 @@
+"""Utility to compensate for a weak job type."""
+import time
+from job_runner.reporting.message import (
+ Exited,
+ Finish,
+ Init,
+ Running,
+ Start,
+ _JOB_STATUS_SUCCESS,
+ _JOB_STATUS_RUNNING,
+ _JOB_STATUS_FAILURE,
+ _JOB_STATUS_WAITING,
+)
+
+
+def create_job_dict(job):
+ return {
+ "name": job.name(),
+ "status": _JOB_STATUS_WAITING,
+ "error": None,
+ "start_time": None,
+ "end_time": None,
+ "stdout": job.std_out,
+ "stderr": job.std_err,
+ "current_memory_usage": None,
+ "max_memory_usage": None,
+ }
+
+
+def datetime_serialize(dt):
+ if dt is None:
+ return None
+ return time.mktime(dt.timetuple())
diff --git a/libres/CMakeLists.txt b/libres/CMakeLists.txt
new file mode 100644
index 00000000000..1eeb50f8010
--- /dev/null
+++ b/libres/CMakeLists.txt
@@ -0,0 +1,135 @@
+cmake_minimum_required(VERSION 3.6.1)
+project(res C CXX)
+
+set(CMAKE_CXX_STANDARD 14)
+
+if(NOT SKBUILD)
+ message(WARNING "This CMakeLists.txt file should not be used directly.\n"
+ "Use 'pip install ${CMAKE_SOURCE_DIR}' to install this as a Python package.\n"
+ "Refer to the README for more information.")
+endif()
+
+if(NOT DEFINED CMAKE_MACOSX_RPATH)
+ # There is some weirdness around this variable, the default value is different depending on
+ # the cmake version, see policy CMP0042.
+ # A more explicit way to treat this would be `cmake_policy(SET CMP0042 NEW)` but that would
+ # fail on CMake 2.8.12 (the variable exists but the policy doesn't)
+ set(CMAKE_MACOSX_RPATH ON)
+endif()
+
+include(GNUInstallDirs)
+enable_testing()
+
+#-----------------------------------------------------------------
+
+set(RES_VERSION "0.0.0-unset" CACHE STRING "libres version")
+
+string( REGEX REPLACE "^([^.]+)\\.([^.]+)\\.(.+)$" "\\1" RES_VERSION_MAJOR "${RES_VERSION}")
+string( REGEX REPLACE "^([^.]+)\\.([^.]+)\\.(.+)$" "\\2" RES_VERSION_MINOR "${RES_VERSION}")
+string( REGEX REPLACE "^([^.]+)\\.([^.]+)\\.(.+)$" "\\3" RES_VERSION_MICRO "${RES_VERSION}")
+
+message( STATUS "libres version: ${RES_VERSION_MAJOR}.${RES_VERSION_MINOR}.${RES_VERSION_MICRO}" )
+
+list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
+find_package(CXX11Features)
+set(CMAKE_C_STANDARD 99)
+
+#-----------------------------------------------------------------
+
+option( BUILD_TESTS "Should the tests be built" OFF)
+option( USE_RPATH "Should we embed path to libraries" ON )
+option( ERT_LSF_SUBMIT_TEST "Build and run tests of LSF submit" OFF)
+
+if(NOT SKBUILD)
+ # We let setuptools handle installing share/ert normally.
+
+ set( SHARE_DIR "../share/ert")
+ # If the SITE_CONFIG_FILE is not set as -DSITE_CONFIG_FILE switch when invoking
+ # cmake we set it here to location of the to-be-installed site-config file. That
+ # implies that when testing the binary will have embedded a path to non-existent
+ # or stale version of the site configuration file. Irrespective of the value of
+ # SITE_CONFIG_FILE the environment variable ERT_SITE_CONFIG will be set to point
+ # to the in-source version of the site-config file for testing.
+ #
+ # The variable SITE_CONFIG_FILE can be set by the user, if that is set it will
+ # take presedence. If the user has not explicitly set the SITE_CONFIG_FILE variable
+ # we will use the default path based on the install prefix.
+ set( SITE_CONFIG_FILE "${CMAKE_INSTALL_PREFIX}/${SHARE_DIR}/site-config" CACHE FILEPATH "Path to global ERT Configuration file")
+ message(STATUS "The path ${SITE_CONFIG_FILE} will be compiled into the libres library as the location of the site configuration file")
+
+ if (USE_RPATH)
+ SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
+ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
+ endif ()
+endif()
+
+#-----------------------------------------------------------------
+
+set(ERT_LSF_LIB_PATH "" CACHE FILEPATH "Path to search for the LSF libraries")
+set(ERT_LSF_INCLUDE_PATH "" CACHE FILEPATH "Path to search for the LSF header files")
+find_path(LSF_HEADER_PATH lsf/lsf.h PATHS ${ERT_LSF_INCLUDE_PATH})
+find_library(LSF_LIBRARY NAMES lsf PATHS ${ERT_LSF_LIB_PATH})
+
+#-----------------------------------------------------------------
+
+set(EQUINOR_TESTDATA_ROOT "" CACHE PATH "Root to Equinor internal testdata")
+if (EXISTS ${EQUINOR_TESTDATA_ROOT})
+ set( LINK "${CMAKE_CURRENT_SOURCE_DIR}/test-data/Equinor" )
+ if (EXISTS ${LINK})
+ execute_process(COMMAND ${CMAKE_COMMAND} -E remove "${LINK}")
+ endif()
+
+ execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${EQUINOR_TESTDATA_ROOT}" "${LINK}")
+ message(STATUS "Linking testdata: ${LINK} -> ${EQUINOR_TESTDATA_ROOT}")
+endif()
+
+file(COPY ../job_runner/job_dispatch.py
+ DESTINATION ${PROJECT_BINARY_DIR}/bin
+ FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ)
+set( ERT_ROOT "${PROJECT_BINARY_DIR}" )
+
+#-----------------------------------------------------------------
+
+find_package(Threads)
+if (CMAKE_USE_PTHREADS_INIT)
+ set(HAVE_PTHREAD TRUE)
+endif ()
+
+# feature tests
+include(CheckFunctionExists)
+check_function_exists( regexec ERT_HAVE_REGEXP )
+
+#-----------------------------------------------------------------
+
+# This is to enable our dynamic lookup hack
+if (APPLE)
+ set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS} -undefined dynamic_lookup")
+ set(CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS} -undefined dynamic_lookup")
+ set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS} -undefined dynamic_lookup")
+ set(CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS} -undefined dynamic_lookup")
+endif ()
+
+#-----------------------------------------------------------------
+# install_example() is a small utility function which is used to install an
+# example file. If a file with the same name already exists in the installation
+# directory the example file will not be reinstalled on top.
+#
+# The decision whether to install the example file or not is evaluated at
+# configure - time; i.e. there is a race open between configure time and
+# installation. Should ideally use the INSTALL(CODE ...) functionality to defer
+# the decision to installation time.
+
+function(install_example src_file destination)
+
+ set (_full_destination "${CMAKE_INSTALL_PREFIX}/${destination}")
+ if (EXISTS "${_full_destination}/${src_file}")
+ message(STATUS "File: ${_full_destination}/${src_file} already exists - will not be updated")
+ else()
+ message(STATUS "An example ${src_file} will be installed as ${_full_destination}")
+ install(FILES ${src_file} DESTINATION "${_full_destination}")
+ endif()
+
+endfunction()
+
+
+add_subdirectory(lib)
diff --git a/libres/ci/jenkins/Jenkinsfile-komodo b/libres/ci/jenkins/Jenkinsfile-komodo
new file mode 100644
index 00000000000..11f4dc2d9ca
--- /dev/null
+++ b/libres/ci/jenkins/Jenkinsfile-komodo
@@ -0,0 +1,22 @@
+pipeline {
+ agent { label 'scout-ci' }
+ parameters {
+ string(defaultValue: "bleeding-py36", description: 'The komodo relase to be used', name: 'RELEASE_NAME')
+ string(defaultValue: "/prog/res/komodo", description: 'Root folder for komodo', name: 'KOMODO_ROOT')
+ string(defaultValue: "/opt/rh/devtoolset-7", description: 'The devtoolset folder', name: 'DEVTOOL')
+ string(defaultValue: '', description: 'commit id you want to build or refname (eg: origin/pr/9/head)', name: 'sha1')
+ string(defaultValue: "/prog/sdpsoft", description: 'The SDPSOFT folder', name: 'SDPSOFT')
+ }
+ stages{
+ stage('run tests') {
+ steps {
+ sh 'sh ci/jenkins/testkomodo.sh'
+ }
+ }
+ }
+ post {
+ cleanup {
+ deleteDir()
+ }
+ }
+}
diff --git a/libres/ci/jenkins/testkomodo.sh b/libres/ci/jenkins/testkomodo.sh
new file mode 100644
index 00000000000..19e17a10930
--- /dev/null
+++ b/libres/ci/jenkins/testkomodo.sh
@@ -0,0 +1,39 @@
+copy_test_files () {
+ mkdir $CI_TEST_ROOT/.git
+ mkdir -p $CI_TEST_ROOT/python/res/fm/rms/
+
+ local PWD=$(pwd)
+ cp -r {$PWD,$CI_TEST_ROOT}/tests
+ ln -s {$PWD,$CI_TEST_ROOT}/test-data
+ ln -s {$PWD,$CI_TEST_ROOT}/lib
+ ln -s {$PWD,$CI_TEST_ROOT}/share
+ ln -s {$PWD,$CI_TEST_ROOT}/bin
+ ln -s {$PWD,$CI_TEST_ROOT}/python/res/fm/rms/rms_config.yml
+}
+
+start_tests () {
+ echo "Running pytest"
+ pushd $CI_TEST_ROOT
+ export ECL_SKIP_SIGNAL=ON
+ pytest \
+ --ignore="tests/res/enkf/test_analysis_config.py" \
+ --ignore="tests/res/enkf/test_res_config.py" \
+ --ignore="tests/res/enkf/test_site_config.py" \
+ --ignore="tests/res/enkf/test_workflow_list.py" \
+ --ignore="tests/res/enkf/test_hook_manager.py" \
+ --ignore="tests/legacy" \
+ --ignore="tests/test_formatting.py"
+ popd
+}
+
+run_tests () {
+ if [[ ! -z "${CI_PR_RUN:-}" ]]
+ then
+ pip install .
+ fi
+
+ copy_test_files
+
+ install_test_dependencies
+ start_tests
+}
diff --git a/libres/cmake/Modules/FindCXX11Features.cmake b/libres/cmake/Modules/FindCXX11Features.cmake
new file mode 100644
index 00000000000..13b25e38568
--- /dev/null
+++ b/libres/cmake/Modules/FindCXX11Features.cmake
@@ -0,0 +1,22 @@
+#
+# Module that checks for supported C++11 (former C++0x) features.
+#
+
+include(CheckCXXCompilerFlag)
+if (NOT MSVC)
+ CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
+ CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
+
+ if(COMPILER_SUPPORTS_CXX11)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+ elseif(COMPILER_SUPPORTS_CXX0X)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+ else()
+ message(SEND_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
+ endif()
+endif()
+
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+set(CXX11FEATURES_FOUND TRUE)
diff --git a/libres/cmake/Modules/FindSphinx.cmake b/libres/cmake/Modules/FindSphinx.cmake
new file mode 100644
index 00000000000..e4f6d45c27a
--- /dev/null
+++ b/libres/cmake/Modules/FindSphinx.cmake
@@ -0,0 +1,14 @@
+find_program(SPHINX_EXECUTABLE NAMES sphinx-build
+ HINTS
+ $ENV{SPHINX_DIR}
+ PATH_SUFFIXES bin
+ DOC "Sphinx documentation generator"
+)
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(Sphinx DEFAULT_MSG
+ SPHINX_EXECUTABLE
+)
+
+mark_as_advanced(SPHINX_EXECUTABLE)
\ No newline at end of file
diff --git a/libres/cmake/Tests/test_getenv.c b/libres/cmake/Tests/test_getenv.c
new file mode 100644
index 00000000000..08c96d16c62
--- /dev/null
+++ b/libres/cmake/Tests/test_getenv.c
@@ -0,0 +1,7 @@
+#include
+
+
+int main(int argc, char ** argv) {
+ getenv("PATH");
+}
+
diff --git a/libres/cmake/Tests/test_have_sigbus.c b/libres/cmake/Tests/test_have_sigbus.c
new file mode 100644
index 00000000000..e8785a49476
--- /dev/null
+++ b/libres/cmake/Tests/test_have_sigbus.c
@@ -0,0 +1,10 @@
+#include
+
+void sigbus_handler(int signal) {
+
+}
+
+
+int main(int argc, char ** argv) {
+ signal(SIGBUS , sigbus_handler);
+}
diff --git a/libres/cmake/Tests/test_isfinite.c b/libres/cmake/Tests/test_isfinite.c
new file mode 100644
index 00000000000..8eafdbcb932
--- /dev/null
+++ b/libres/cmake/Tests/test_isfinite.c
@@ -0,0 +1,5 @@
+#include
+
+int main( int argc , char ** argv) {
+ isfinite(0.0);
+}
diff --git a/libres/cmake/Tests/test_mktime_before1970.c b/libres/cmake/Tests/test_mktime_before1970.c
new file mode 100644
index 00000000000..95feb71dfbc
--- /dev/null
+++ b/libres/cmake/Tests/test_mktime_before1970.c
@@ -0,0 +1,23 @@
+#include
+#include
+#include
+#include
+#include
+
+int main(int argc, char ** argv) {
+ struct tm ts;
+ ts.tm_sec = 0;
+ ts.tm_min = 0;
+ ts.tm_hour = 0;
+ ts.tm_mday = 1;
+ ts.tm_mon = 0;
+ ts.tm_year = 0;
+ ts.tm_isdst = -1;
+ {
+ time_t t = mktime( &ts );
+ if (t == -1)
+ exit(1);
+ else
+ exit(0);
+ }
+}
diff --git a/libres/cmake/Tests/test_mode_t.c b/libres/cmake/Tests/test_mode_t.c
new file mode 100644
index 00000000000..b502411b837
--- /dev/null
+++ b/libres/cmake/Tests/test_mode_t.c
@@ -0,0 +1,6 @@
+#include
+
+int main(int argc, char ** argv) {
+ mode_t new_mode = S_IWGRP;
+ chmod( "/tmp" , new_mode );
+}
diff --git a/libres/cmake/Tests/test_openmp.c b/libres/cmake/Tests/test_openmp.c
new file mode 100644
index 00000000000..0c8c25e6cd6
--- /dev/null
+++ b/libres/cmake/Tests/test_openmp.c
@@ -0,0 +1,11 @@
+#include
+#include
+
+int main(int argc, char ** argv) {
+ int sum = 0;
+#pragma omp parallel for
+ for (int i=0; i < 100; i++)
+ sum += i;
+
+}
+
diff --git a/libres/cmake/Tests/test_pid_t.c b/libres/cmake/Tests/test_pid_t.c
new file mode 100644
index 00000000000..424f2ac3607
--- /dev/null
+++ b/libres/cmake/Tests/test_pid_t.c
@@ -0,0 +1,6 @@
+#include
+#include
+
+int main(int argc, char ** argv) {
+ pid_t pid = getpid();
+}
diff --git a/libres/cmake/Tests/test_shared_ptr.cpp b/libres/cmake/Tests/test_shared_ptr.cpp
new file mode 100644
index 00000000000..78ca0bbff45
--- /dev/null
+++ b/libres/cmake/Tests/test_shared_ptr.cpp
@@ -0,0 +1,5 @@
+#include
+
+int main( int argc , char ** argv) {
+ std::shared_ptr ptr = std::make_shared( 10 );
+}
diff --git a/libres/cmake/Tests/test_va_copy.c b/libres/cmake/Tests/test_va_copy.c
new file mode 100644
index 00000000000..1a12b407b29
--- /dev/null
+++ b/libres/cmake/Tests/test_va_copy.c
@@ -0,0 +1,16 @@
+#include
+#include
+
+int func(int arg1 , ...) {
+ va_list ap;
+ va_list copy;
+ va_start(ap , arg1);
+ va_copy( copy , ap );
+
+ va_end(ap);
+ return 1;
+}
+
+int main(int argc, char ** argv) {
+ func(10 , NULL , "String" );
+}
diff --git a/libres/cmake/create_cmakelists.py b/libres/cmake/create_cmakelists.py
new file mode 100755
index 00000000000..173331e6bad
--- /dev/null
+++ b/libres/cmake/create_cmakelists.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+
+from os import listdir
+from os.path import isfile, join, isdir, islink
+import sys
+
+
+def findFilesAndDirectories(directory):
+ all_files = listdir(directory)
+ files = []
+ directories = []
+ for f in all_files:
+ path = join(directory, f)
+ if isfile(path) and not f == "CMakeLists.txt" and not islink(path):
+ files.append(f)
+ if isdir(path):
+ directories.append(f)
+
+ return sorted(files), sorted(directories)
+
+
+def findRelativeModulePath(directory):
+ """@type directory: str"""
+ index = directory.rfind("python/")
+ index += len("python/")
+ return directory[index : len(directory)]
+
+
+def createPythonSources(files, test_sources=False):
+ result = ""
+
+ if len(files) > 0:
+ result = "set(%s\n" % ("PYTHON_SOURCES" if not test_sources else "TEST_SOURCES")
+
+ files = [f for f in files if f.endswith(".py")]
+
+ for f in files:
+ result += " " + str(f) + "\n"
+
+ if len(files) > 0:
+ result += ")"
+
+ return result
+
+
+def addSubDirectories(directories):
+ result = ""
+
+ for d in directories:
+ result += "add_subdirectory(" + str(d) + ")\n"
+
+ return result
+
+
+def addPythonPackage(relative_module_path, test_sources=False):
+ module_name = ".".join(relative_module_path.split("/"))
+ source_type = "PYTHON_SOURCES" if not test_sources else "TEST_SOURCES"
+ template = 'add_python_package("python.%s" ${PYTHON_INSTALL_PREFIX}/%s "${%s}" %s)'
+
+ install = "False" if test_sources else "True"
+
+ return template % (module_name, relative_module_path, source_type, install)
+
+
+def addInclude(filename):
+ with open(filename, "r") as include_file:
+ content = include_file.read()
+ return content
+
+
+files, directories = findFilesAndDirectories(sys.argv[1])
+module_path = findRelativeModulePath(sys.argv[1])
+
+output_file = join(sys.argv[1], "CMakeLists.txt")
+test_sources = module_path.startswith("tests")
+with open(output_file, "w+") as text_file:
+ text_file.write(createPythonSources(files, test_sources=test_sources))
+ text_file.write("\n\n")
+ text_file.write(addPythonPackage(module_path, test_sources=test_sources))
+ text_file.write("\n\n")
+ text_file.write(addSubDirectories(directories))
+
+ if "local.cmake" in files:
+ text_file.write("\n\n")
+ text_file.write(addInclude(join(sys.argv[1], "local.cmake")))
diff --git a/libres/cmake/libres-config-version.cmake.in b/libres/cmake/libres-config-version.cmake.in
new file mode 100644
index 00000000000..bfbc5bef8dc
--- /dev/null
+++ b/libres/cmake/libres-config-version.cmake.in
@@ -0,0 +1,13 @@
+set(PACKAGE_VERSION @ERT_VERSION_MAJOR@.@ERT_VERSION_MINOR@.@ERT_VERSION_PATCH@)
+set(PACKAGE_VERSION_MAJOR @ERT_VERSION_MAJOR@)
+set(PACKAGE_VERSION_MINOR @ERT_VERSION_MINOR@)
+set(PACKAGE_VERSION_PATCH @ERT_VERSION_MICRO@)
+
+set(libres_INCLUDE_DIRS @CMAKE_INSTALL_PREFIX@/include)
+set(libres_LIBRARIES -lenkf -lsched -lrms -lconfig -lanalysis -ljob_queue)
+link_directories( @CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@ )
+include_directories( @CMAKE_INSTALL_PREFIX@/include )
+
+if (@ENABLE_PYTHON@)
+ set(libres_PYTHONPATH @CMAKE_INSTALL_PREFIX@/@PYTHON_INSTALL_PREFIX@ )
+endif()
diff --git a/libres/cmake/libres-config.cmake.in b/libres/cmake/libres-config.cmake.in
new file mode 100644
index 00000000000..a8ab6883c04
--- /dev/null
+++ b/libres/cmake/libres-config.cmake.in
@@ -0,0 +1,9 @@
+set(libres_INCLUDE_DIRS @CMAKE_INSTALL_PREFIX@/include)
+set(libres_LIBRARIES -lenkf -lsched -lrms -lconfig -lanalysis -ljob_queue)
+
+link_directories( @CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@ )
+include_directories( @CMAKE_INSTALL_PREFIX@/include )
+
+if (@ENABLE_PYTHON@)
+ set(libres_PYTHONPATH @CMAKE_INSTALL_PREFIX@/@PYTHON_INSTALL_PREFIX@ )
+endif()
diff --git a/libres/lib/CMakeLists.txt b/libres/lib/CMakeLists.txt
new file mode 100644
index 00000000000..2fa2b58efd3
--- /dev/null
+++ b/libres/lib/CMakeLists.txt
@@ -0,0 +1,935 @@
+if (LSF_LIBRARY AND LSF_HEADER_PATH)
+ set(lsb job_queue/lsb.c)
+ install(DIRECTORY include/
+ DESTINATION include
+ PATTERN lsb.h)
+ target_compile_definitions(job_queue PUBLIC -DHAVE_LSF_LIBRARY)
+ set(lsf ${LSF_LIBRARY})
+else ()
+ message(STATUS "LSF not found")
+endif ()
+
+if(NOT SKBUILD)
+ # Link directly to libecl if not using `pip install`
+ find_package(ecl REQUIRED)
+ set(ECL ecl)
+ set(RES res)
+endif()
+
+add_library(res SHARED
+ res_util/res_log.cpp
+ res_util/log.cpp
+ res_util/es_testdata.cpp
+ res_util/arg_pack.cpp
+ res_util/ui_return.cpp
+ res_util/subst_list.cpp
+ res_util/subst_func.cpp
+ res_util/matrix_stat.cpp
+ res_util/matrix_blas.cpp
+ res_util/matrix_lapack.cpp
+ res_util/matrix.cpp
+ res_util/template.cpp
+ res_util/path_fmt.cpp
+ res_util/res_env.cpp
+ res_util/res_portability.cpp
+ res_util/util_printf.cpp
+ res_util/block_fs.cpp
+ res_util/res_version.cpp
+ res_util/regression.cpp
+ res_util/thread_pool.cpp
+ res_util/template_loop.cpp # Highly deprecated
+
+ config/conf_util.cpp
+ config/conf.cpp
+ config/conf_data.cpp
+ config/config_parser.cpp
+ config/config_content.cpp
+ config/config_path_stack.cpp
+ config/config_content_item.cpp
+ config/config_content_node.cpp
+ config/config_error.cpp
+ config/config_path_elm.cpp
+ config/config_root_path.cpp
+ config/config_schema_item.cpp
+ config/config_settings.cpp
+
+ rms/rms_file.cpp
+ rms/rms_tag.cpp
+ rms/rms_tagkey.cpp
+ rms/rms_type.cpp
+ rms/rms_util.cpp
+
+ sched/history.cpp
+
+ analysis/analysis_module.cpp
+ analysis/bootstrap_enkf.cpp
+ analysis/cv_enkf.cpp
+ analysis/enkf_linalg.cpp
+ analysis/fwd_step_enkf.cpp
+ analysis/fwd_step_log.cpp
+ analysis/module_data_block.cpp
+ analysis/module_data_block_vector.cpp
+ analysis/module_info.cpp
+ analysis/module_obs_block.cpp
+ analysis/module_obs_block_vector.cpp
+ analysis/null_enkf.cpp
+ analysis/sqrt_enkf.cpp
+ analysis/std_enkf.cpp
+ analysis/stepwise.cpp
+
+ job_queue/ext_job.cpp
+ job_queue/ext_joblist.cpp
+ job_queue/forward_model.cpp
+ job_queue/job_status.cpp
+ job_queue/job_list.cpp
+ job_queue/job_node.cpp
+ job_queue/job_queue.cpp
+ job_queue/job_queue_status.cpp
+ job_queue/local_driver.cpp
+ job_queue/lsf_driver.cpp
+ job_queue/queue_driver.cpp
+ job_queue/rsh_driver.cpp
+ job_queue/slurm_driver.cpp
+ job_queue/torque_driver.cpp
+ job_queue/workflow.cpp
+ job_queue/workflow_job.cpp
+ job_queue/workflow_joblist.cpp
+ job_queue/environment_varlist.cpp
+ job_queue/job_kw_definitions.cpp
+ ${lsb}
+
+ enkf/active_list.cpp
+ enkf/time_map.cpp
+ enkf/analysis_config.cpp
+ enkf/analysis_iter_config.cpp
+ enkf/block_fs_driver.cpp
+ enkf/block_obs.cpp
+ enkf/callback_arg.cpp
+ enkf/cases_config.cpp
+ enkf/container.cpp
+ enkf/container_config.cpp
+ enkf/data_ranking.cpp
+ enkf/ecl_config.cpp
+ enkf/ecl_refcase_list.cpp
+ enkf/enkf_analysis.cpp
+ enkf/enkf_config_node.cpp
+ enkf/enkf_defaults.cpp
+ enkf/enkf_fs.cpp
+ enkf/enkf_main.cpp
+ enkf/enkf_main_jobs.cpp
+ enkf/enkf_node.cpp
+ enkf/enkf_obs.cpp
+ enkf/enkf_plot_data.cpp
+ enkf/enkf_plot_gendata.cpp
+ enkf/enkf_plot_gen_kw.cpp
+ enkf/enkf_plot_gen_kw_vector.cpp
+ enkf/enkf_plot_genvector.cpp
+ enkf/enkf_plot_tvector.cpp
+ enkf/enkf_serialize.cpp
+ enkf/enkf_state.cpp
+ enkf/enkf_types.cpp
+ enkf/enkf_util.cpp
+ enkf/ensemble_config.cpp
+ enkf/ert_run_context.cpp
+ enkf/ert_template.cpp
+ enkf/ert_test_context.cpp
+ enkf/ert_workflow_list.cpp
+ enkf/ext_param.cpp
+ enkf/ext_param_config.cpp
+ enkf/field.cpp
+ enkf/field_config.cpp
+ enkf/field_trans.cpp
+ enkf/forward_load_context.cpp
+ enkf/fs_driver.cpp
+ enkf/fs_types.cpp
+ enkf/gen_common.cpp
+ enkf/gen_data.cpp
+ enkf/gen_data_config.cpp
+ enkf/gen_kw.cpp
+ enkf/gen_kw_config.cpp
+ enkf/value_export.cpp
+ enkf/gen_obs.cpp
+ enkf/hook_manager.cpp
+ enkf/hook_workflow.cpp
+ enkf/local_config.cpp
+ enkf/local_dataset.cpp
+ enkf/local_ministep.cpp
+ enkf/local_obsdata.cpp
+ enkf/local_obsdata_node.cpp
+ enkf/local_updatestep.cpp
+ enkf/meas_data.cpp
+ enkf/misfit_ensemble.cpp
+ enkf/misfit_member.cpp
+ enkf/misfit_ranking.cpp
+ enkf/misfit_ts.cpp
+ enkf/model_config.cpp
+ enkf/obs_data.cpp
+ enkf/obs_vector.cpp
+ enkf/queue_config.cpp
+ enkf/ranking_table.cpp
+ enkf/rng_config.cpp
+ enkf/run_arg.cpp
+ enkf/runpath_list.cpp
+ enkf/site_config.cpp
+ enkf/rng_manager.cpp
+ enkf/res_config.cpp
+ enkf/row_scaling.cpp
+ enkf/state_map.cpp
+ enkf/state_map.cpp
+ enkf/summary.cpp
+ enkf/summary_config.cpp
+ enkf/summary_key_matcher.cpp
+ enkf/summary_key_set.cpp
+ enkf/summary_obs.cpp
+ enkf/surface.cpp
+ enkf/surface_config.cpp
+ enkf/trans_func.cpp
+ enkf/subst_config.cpp
+ enkf/log_config.cpp
+ enkf/config_keys.cpp
+
+ external/JSON/cJSON.c
+)
+
+#-----------------------------------------------------------------
+
+add_library(rml_enkf SHARED
+ analysis/modules/rml_enkf_config.c
+ analysis/modules/rml_enkf.c
+ analysis/modules/rml_enkf_common.c
+ analysis/modules/rml_enkf_log.c)
+target_link_libraries(rml_enkf PRIVATE ${RES} ${ECL} ${CMAKE_DL_LIBS})
+target_include_directories(rml_enkf PRIVATE ${ECL_INCLUDE_DIRS} include analysis/modules)
+set_target_properties(rml_enkf PROPERTIES VERSION 1.0
+ SOVERSION 1.0
+ PREFIX "")
+
+add_library(ies SHARED
+ analysis/modules/ies_enkf.c
+ analysis/modules/ies_enkf_config.c
+ analysis/modules/ies_enkf_data.c)
+target_link_libraries(ies PRIVATE ${RES} ${ECL} ${CMAKE_DL_LIBS})
+target_include_directories(ies PRIVATE ${ECL_INCLUDE_DIRS} include analysis/modules)
+
+add_library(std_enkf_debug SHARED
+ analysis/modules/std_enkf_debug.c)
+target_link_libraries(std_enkf_debug PRIVATE ${RES} ${ECL} ${CMAKE_DL_LIBS})
+target_include_directories(std_enkf_debug PRIVATE ${ECL_INCLUDE_DIRS} include modules)
+set_target_properties(std_enkf_debug PROPERTIES VERSION 1.0
+ SOVERSION 1.0
+ PREFIX "")
+
+#-----------------------------------------------------------------
+
+# The INTERNAL_LINK property is required to get the
+# system with analysis modules to work, might have some side-effects?
+target_compile_definitions(res PRIVATE -DINTERNAL_LINK)
+
+find_package(LAPACK REQUIRED)
+target_link_libraries(res PUBLIC ${ECL} ${LAPACK_LIBRARIES} ${LAPACK_LINKER_FLAGS})
+target_include_directories(res
+ PUBLIC $
+ $
+ PRIVATE $
+ PRIVATE $
+ PRIVATE "${ECL_INCLUDE_DIRS}")
+
+target_compile_definitions(res PRIVATE
+ -DGIT_COMMIT=${GIT_COMMIT}
+ -DGIT_COMMIT_SHORT=${GIT_COMMIT_SHORT}
+ -DRES_VERSION_MAJOR=${RES_VERSION_MAJOR}
+ -DRES_VERSION_MINOR=${RES_VERSION_MINOR}
+ -DRES_VERSION_MICRO=${RES_VERSION_MICRO}
+ -DCOMPILE_TIME_STAMP="${RES_BUILD_TIME}")
+
+install(TARGETS res
+ EXPORT res-config
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+install(TARGETS rml_enkf
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+install(TARGETS ies
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+#-----------------------------------------------------------------
+
+if (NOT BUILD_TESTS)
+ return ()
+endif()
+
+foreach(name ies_enkf_config ies_enkf_data)
+ add_executable(${name} analysis/modules/tests/${name}.cpp ${ies_source})
+ target_include_directories(${name} PRIVATE analysis/modules)
+ target_link_libraries(${name} res ies)
+ add_test(NAME ${name} COMMAND ${name})
+endforeach()
+
+add_executable( ies_module analysis/modules/tests/ies_enkf_module.cpp)
+target_link_libraries(ies_module res ies)
+add_test(NAME ies_module COMMAND ies_module $ ${CMAKE_CURRENT_SOURCE_DIR}/analysis/modules/tests/test-data/poly)
+
+add_executable(ies_linalg analysis/modules/tests/ies_linalg.cpp ${ies_source})
+target_include_directories(ies_linalg PRIVATE analysis/modules)
+target_link_libraries(ies_linalg res ies)
+add_test(NAME ies_linalg COMMAND ies_linalg ${CMAKE_CURRENT_SOURCE_DIR}/analysis/modules/tests/test-data/poly)
+
+add_executable(ies_std_compare analysis/modules/tests/ies_std_compare.cpp ${ies_source})
+target_include_directories(ies_std_compare PRIVATE analysis/modules)
+target_link_libraries(ies_std_compare res ies)
+add_test(NAME ies_std_compare COMMAND ies_std_compare ${CMAKE_CURRENT_SOURCE_DIR}/analysis/modules/tests/test-data/poly)
+
+add_executable(ies_iteration analysis/modules/tests/ies_iteration.cpp ${ies_source})
+target_include_directories(ies_iteration PRIVATE analysis/modules)
+target_link_libraries(ies_iteration res ies)
+add_test(NAME ies_iteration COMMAND ies_iteration ${CMAKE_CURRENT_SOURCE_DIR}/analysis/modules/tests/test-data/poly_normal)
+
+foreach(name ert_util_logh
+ ert_util_arg_pack
+ ert_util_matrix
+ es_testdata
+ ert_util_matrix_lapack
+ ert_util_subst_list
+ ert_util_block_fs
+ test_thread_pool
+ res_util_PATH)
+
+ add_executable(${name} res_util/tests/${name}.cpp)
+ target_link_libraries(${name} res)
+ add_test(NAME ${name} COMMAND ${name})
+endforeach()
+
+find_library( VALGRIND NAMES valgr )
+if (VALGRIND)
+ set(valgrind_cmd valgrind --error-exitcode=1 --tool=memcheck)
+ add_test(NAME test_thread_pool COMMAND ${valgrind_cmd} test_thread_pool)
+endif()
+
+#-----------------------------------------------------------------
+
+foreach(name config_content_node
+ config_path_elm
+ config_error
+ config_content
+ config_config
+ config_schema_item)
+
+ add_executable(${name} config/tests/${name}.cpp)
+ target_link_libraries(${name} res)
+ add_test(NAME ${name} COMMAND ${name})
+endforeach()
+
+
+foreach(name config_append_test
+ config_node_test
+ config_typeOK
+ config_typeFail
+ config_root_path
+ config_include_test
+ config_content_item
+ config_define
+ config_argc)
+
+ add_executable(${name} config/tests/${name}.cpp)
+ target_link_libraries(${name} res)
+endforeach()
+
+macro(config_test name input_file)
+ add_test(NAME ${name} COMMAND ${name} ${CMAKE_CURRENT_SOURCE_DIR}/config/tests/data/${input_file})
+endmacro()
+
+config_test( config_append_test append_test)
+config_test( config_node_test append_test)
+config_test( config_typeOK type_testOK)
+config_test( config_typeFail type_testFail)
+config_test( config_content_item content_item_test)
+config_test( config_define define_test)
+
+
+add_test(NAME config_root_path
+ COMMAND config_root_path ${CMAKE_CURRENT_SOURCE_DIR}/config/tests/data)
+
+add_test(NAME config_include_test
+ COMMAND config_include_test ${CMAKE_CURRENT_SOURCE_DIR}/config/tests/data include_test)
+
+add_test(NAME config_argc
+ COMMAND config_argc
+ ${CMAKE_CURRENT_SOURCE_DIR}/config/tests/data/argc_OK
+ ${CMAKE_CURRENT_SOURCE_DIR}/config/tests/data/argc_less
+ ${CMAKE_CURRENT_SOURCE_DIR}/config/tests/data/argc_more)
+
+
+
+#-----------------------------------------------------------------
+
+add_executable(sched_history_summary sched/tests/sched_history_summary.cpp)
+target_link_libraries(sched_history_summary res)
+
+add_executable(rms_file_test rms/tests/rms_file_test.cpp)
+target_link_libraries(rms_file_test res)
+
+add_executable(analysis_external_module analysis/tests/analysis_test_external_module.cpp)
+target_link_libraries(analysis_external_module res)
+
+add_test(NAME analysis_module_rml
+ COMMAND analysis_external_module
+ "RML_ENKF"
+ $ 41
+ ITER:45
+ USE_PRIOR:False
+ LAMBDA_REDUCE:0.10
+ LAMBDA_INCREASE:2.5
+ ENKF_TRUNCATION:0.77
+ LAMBDA0:0.25
+ LAMBDA_MIN:0.01
+ LOG_FILE:LogFile.txt
+ CLEAR_LOG:True
+ LAMBDA_RECALCULATE:True)
+
+
+foreach(name analysis_test_module_info analysis_module_test)
+ add_executable(${name} analysis/tests/${name}.cpp)
+ target_link_libraries(${name} res)
+ add_test(NAME ${name} COMMAND ${name})
+endforeach()
+
+#-----------------------------------------------------------------
+
+
+foreach(name job_status_test
+ ext_job_test
+ job_node_test
+ job_lsf_parse_bsub_stdout
+ job_lsf_test
+ job_queue_driver_test
+ job_slurm_driver
+ job_mock_slurm
+ job_torque_test)
+
+ add_executable(${name} job_queue/tests/${name}.cpp)
+ target_include_directories(${name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/private-include)
+ target_link_libraries(${name} res)
+ add_test(NAME ${name} COMMAND ${name})
+endforeach()
+
+find_program(SBATCH "sbatch")
+foreach(name job_slurm_submit
+ job_slurm_runtest)
+ add_executable(${name} job_queue/tests/${name}.cpp)
+ target_include_directories(${name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/private-include)
+ target_link_libraries(${name} res)
+ if (SBATCH)
+ add_test(NAME ${name} COMMAND ${name})
+ endif()
+endforeach()
+
+
+foreach(name job_loadOK
+ ext_joblist_test
+ job_torque_submit_test
+ job_queue_stress_task
+ job_program_output
+ job_lsf_exclude_hosts_test
+ job_workflow_test
+ create_file
+ job_loadFail)
+
+ add_executable(${name} job_queue/tests/${name}.cpp)
+ target_link_libraries(${name} res)
+endforeach()
+
+if (NOT APPLE)
+
+ add_executable(job_list_test job_queue/tests/job_list_test.cpp)
+ target_link_libraries(job_list_test res)
+
+ add_test(NAME job_list_test
+ COMMAND valgrind --leak-check=full --error-exitcode=1
+ $)
+endif()
+
+
+add_test(NAME job_loadOK1
+ COMMAND job_loadOK
+ ${CMAKE_CURRENT_SOURCE_DIR}/job_queue/tests/data/internalOK)
+add_test(NAME job_loadOK2
+ COMMAND job_loadOK
+ ${CMAKE_CURRENT_SOURCE_DIR}/job_queue/tests/data/externalOK)
+add_test(NAME job_loadOK3
+ COMMAND job_loadOK
+ ${CMAKE_CURRENT_SOURCE_DIR}/job_queue/tests/data/internalOK
+ ${CMAKE_CURRENT_SOURCE_DIR}/job_queue/tests/data/externalOK)
+
+add_test(NAME job_loadFail1
+ COMMAND job_loadFail
+ ${CMAKE_CURRENT_SOURCE_DIR}/job_queue/tests/data/internalFail)
+add_test(NAME job_loadFail2
+ COMMAND job_loadFail
+ ${CMAKE_CURRENT_SOURCE_DIR}/job_queue/tests/data/externalFail)
+add_test(NAME job_loadFail3
+ COMMAND job_loadFail
+ ${CMAKE_CURRENT_SOURCE_DIR}/job_queue/tests/data/internalFail
+ ${CMAKE_CURRENT_SOURCE_DIR}/job_queue/tests/data/externalFail)
+
+set_target_properties(job_workflow_test PROPERTIES ENABLE_EXPORTS ON)
+add_test(NAME job_workflow_test
+ COMMAND job_workflow_test
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/job_queue/tests/data/internal_job)
+
+add_test(NAME job_lsf_exclude_hosts_test
+ COMMAND job_lsf_exclude_hosts_test job_program NULL LOCAL)
+
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/job_queue/tests/data/qsub_emulators/
+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+
+add_test(NAME job_torque_submit_test
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ COMMAND job_torque_submit_test dummyparam)
+set_property(TEST job_torque_submit_test PROPERTY ENVIRONMENT “setenv PATH ${CMAKE_CURRENT_BINARY_DIR}:$PATHâ€)
+
+add_test(NAME ext_joblist_test
+ COMMAND ext_joblist_test
+ ${CMAKE_CURRENT_SOURCE_DIR}/job_queue/tests/data/jobs/util
+ ${CMAKE_CURRENT_SOURCE_DIR})
+
+# The testing of the lsf submit capabilities is quite troublesome for
+# two reasons, and therefor by default disabled:
+#
+#
+# 1. The shell based LSF commands require that user running the
+# bsub/bjobs/bxxx command has passwordless ssh configured to log in
+# to the lsf server. When the build and testing is run as a common
+# 'jenkins' user this becomes difficult.
+#
+# 2. Submitting through the lsf library requires that the build/test
+# server actually is a LIM host; which it typically is not.
+#
+#-----------------------------------------------------------------
+#
+# This should be a space separated list of servers which will be
+# tried out when testing the LSF submit capability. The test program
+# will interpret the special strings 'NULL' and 'LOCAL' as follows:
+#
+# NULL: Submit using the linked in library functions.
+# LOCAL: Submit using shell commands on the current server
+#
+if (ERT_LSF_SUBMIT_TEST)
+ set(LSF_SERVER "" CACHE STRING "List of LSF servers for testing")
+
+ if (LSF_LIBRARY)
+ add_executable(job_lsb_test job_queue/tests/job_lsb_test.cpp)
+ target_link_libraries(job_lsb_test res)
+ add_test(NAME job_lsb_test COMMAND job_lsb_test)
+
+ add_executable(job_lsb job_queue/tests/job_lsb.cpp)
+ target_link_libraries(job_lsb res)
+ add_test(NAME job_lsb COMMAND job_lsb)
+ endif()
+
+ add_executable(job_lsf_remote_submit_test job_queue/tests/job_lsf_remote_submit_test.cpp)
+ target_link_libraries(job_lsf_remote_submit_test res)
+
+ add_executable(job_lsf_library_submit_test job_queue/tests/job_lsf_library_submit_test.cpp)
+ target_link_libraries(job_lsf_library_submit_test res)
+
+ add_executable(job_program job_queue/tests/job_program.cpp)
+
+ if (LSF_SERVER)
+ add_test(NAME job_lsf_remote_submit_test
+ COMMAND job_lsf_remote_submit_test
+ job_program
+ ${LSF_SERVER}
+ NULL
+ LOCAL)
+ else()
+ add_test(NAME job_lsf_remote_submit_test
+ COMMAND job_lsf_remote_submit_test
+ job_program
+ NULL
+ LOCAL)
+ endif()
+
+ install(TARGETS job_program job_lsf_library_submit_test
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif()
+#-----------------------------------------------------------------
+
+foreach (test enkf_active_list
+ enkf_analysis_config
+ enkf_analysis_config_ext_module
+ enkf_cases_config
+ enkf_config_node
+ enkf_enkf_config_node_gen_data
+ enkf_ensemble
+ enkf_ensemble_config
+ enkf_ert_run_context
+ enkf_fs
+ enkf_gen_data_config_parse
+ enkf_iter_config
+ enkf_local_dataset
+ enkf_local_obsdata
+ enkf_local_obsdata_node
+ enkf_meas_data
+ enkf_model_config
+ enkf_obs_invalid_path
+ enkf_obs_tests
+ enkf_obs_vector
+ enkf_plot_data
+ enkf_plot_gendata
+ enkf_plot_gen_kw
+ enkf_plot_gen_kw_vector
+ enkf_plot_genvector
+ enkf_plot_tvector
+ enkf_update_log_test
+ enkf_run_arg
+ enkf_state_map
+ obs_vector_tests
+ log_config_level_parse
+ row_scaling
+ rng_manager
+ rng_config
+ trans_func
+ value_export
+ )
+ add_executable(${test} enkf/tests/${test}.cpp)
+ target_link_libraries(${test} res)
+ add_test(NAME ${test} COMMAND ${test})
+ target_include_directories(${test} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/private-include)
+endforeach ()
+
+function( add_config_test name command )
+ add_test( NAME ${name}
+ COMMAND ${command} ${ARGN})
+
+ set_property( TEST ${name} PROPERTY ENVIRONMENT "ERT_ROOT=${ERT_ROOT};ERT_SITE_CONFIG=${PROJECT_SOURCE_DIR}/${SHARE_DIR}/site-config")
+endfunction()
+
+
+foreach(test enkf_magic_string_in_workflows
+ enkf_queue_config
+ enkf_gen_obs_load
+ enkf_ert_workflow_list
+ enkf_main
+ enkf_select_case_job
+ enkf_plot_gen_kw_fs
+ enkf_workflow_job_test_directory
+ enkf_ert_test_context
+ gen_kw_logarithmic_test
+ enkf_analysis_config_analysis_load
+ enkf_forward_init_GEN_KW
+ enkf_forward_init_GEN_PARAM
+ enkf_umask_config_test
+ enkf_executable_path
+ enkf_forward_load_context
+ enkf_ensemble_GEN_PARAM
+ enkf_workflow_job_test2
+ gen_kw_test
+ enkf_runpath_list)
+
+ add_executable(${test} enkf/tests/${test}.cpp)
+ target_link_libraries(${test} res)
+endforeach()
+
+add_config_test(enkf_executable_path enkf_executable_path)
+
+add_config_test(enkf_workflow_job_test2
+ enkf_workflow_job_test2
+ ${CMAKE_SOURCE_DIR}/test-data/local/snake_oil_no_data/snake_oil_GEN_DATA.ert
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/LOAD_RESULTS)
+
+add_config_test(enkf_forward_load_context
+ enkf_forward_load_context
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config
+ forward_load_context)
+
+add_config_test(enkf_ensemble_GEN_PARAM
+ enkf_ensemble_GEN_PARAM
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/ensemble/GEN_PARAM)
+
+
+add_config_test(enkf_umask_config_test
+ enkf_umask_config_test
+ ${CMAKE_SOURCE_DIR}/test-data/local/simple_config/config_umask)
+
+add_config_test(enkf_forward_init_GEN_PARAM_TRUE
+ enkf_forward_init_GEN_PARAM
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/forward/ert
+ config_GEN_PARAM_true
+ TRUE)
+
+add_config_test(enkf_forward_init_GEN_PARAM_FALSE
+ enkf_forward_init_GEN_PARAM
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/forward/ert
+ config_GEN_PARAM_false
+ FALSE)
+
+add_config_test(enkf_select_case_job
+ enkf_select_case_job
+ ${CMAKE_SOURCE_DIR}/test-data/local/snake_oil/snake_oil.ert
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal-tui/config/SELECT_CASE)
+add_config_test(enkf_forward_init_GEN_KW_TRUE
+ enkf_forward_init_GEN_KW
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/forward/ert
+ config_GEN_KW_true
+ TRUE)
+add_config_test(enkf_forward_init_GEN_KW_FALSE
+ enkf_forward_init_GEN_KW
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/forward/ert
+ config_GEN_KW_false
+ FALSE)
+add_config_test(enkf_queue_config enkf_queue_config)
+add_config_test(enkf_magic_string_in_workflows enkf_magic_string_in_workflows ${CMAKE_SOURCE_DIR}/test-data/local/config/workflows/config)
+add_config_test(enkf_main enkf_main ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config rng)
+add_config_test(enkf_runpath_list enkf_runpath_list ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/runpath_list/config)
+add_config_test(enkf_gen_obs_load enkf_gen_obs_load ${CMAKE_SOURCE_DIR}/test-data/local/config/gen_data/config)
+add_config_test(enkf_ert_workflow_list enkf_ert_workflow_list ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/SCALE_STD)
+add_config_test(enkf_ert_test_context
+ enkf_ert_test_context
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/test_context/config
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/test_context/wf_job
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/test_context/wf_job_fail)
+add_config_test(enkf_plot_gen_kw_fs
+ enkf_plot_gen_kw_fs
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/gen_kw_plot/config)
+add_config_test(gen_kw_test
+ gen_kw_test
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/forward/ert/config_GEN_KW_true)
+add_config_test(gen_kw_logarithmic_test
+ gen_kw_logarithmic_test
+ ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/gen_kw_logarithmic/config_GEN_KW_logarithmic)
+
+add_config_test(enkf_analysis_config_analysis_load enkf_analysis_config_analysis_load ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/analysis_load_config)
+# It seems necessary to set the LD_LIBRARY_PATH for the loading of the
+# rml_enkf module to work. Do understand why!
+if (UNIX AND NOT APPLE)
+set_property(TEST enkf_analysis_config_analysis_load PROPERTY
+ ENVIRONMENT
+ "ERT_SITE_CONFIG=${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/analysis_load_site_config;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}:$ENV{LD_LIBRARY_PATH}")
+elseif (APPLE)
+set_property(TEST enkf_analysis_config_analysis_load PROPERTY
+ ENVIRONMENT
+ "ERT_SITE_CONFIG=${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/config/analysis_load_site_config_osx;DYLD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}:$ENV{DYLD_LIBRARY_PATH}")
+endif()
+
+add_config_test(enkf_workflow_job_test_directory enkf_workflow_job_test_directory ${CMAKE_CURRENT_SOURCE_DIR}/enkf/tests/data/workflow_jobs)
+#-----------------------------------------------------------------
+
+foreach(test enkf_site_config
+ enkf_main_fs_current_file_test
+ enkf_scale_correlated_std
+ enkf_plot_gendata_fs
+ enkf_state_report_step_compatible
+ enkf_state_manual_load_test
+ enkf_export_field_test
+ enkf_workflow_job_test
+ enkf_forward_init_SURFACE
+ enkf_forward_init_FIELD
+ enkf_forward_init_transform
+ enkf_state_skip_summary_load_test
+ enkf_export_inactive_cells
+ enkf_refcase_list
+ enkf_ecl_config
+ enkf_ecl_config_config
+ enkf_gen_data_config
+ enkf_block_obs
+ enkf_obs_fs
+ enkf_obs_vector_fs
+ enkf_plot_data_fs
+ enkf_time_map
+ enkf_main_fs )
+
+ add_executable(${test} enkf/tests/${test}.cpp)
+ target_link_libraries(${test} res)
+endforeach()
+
+
+if (NOT EQUINOR_TESTDATA_ROOT)
+ return ()
+endif ()
+
+add_test(NAME rms_file_test
+ COMMAND rms_file_test ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/rms_file_test/rms_file)
+
+add_test(NAME sched_history_summary1
+ COMMAND sched_history_summary ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/Gurbat/ECLIPSE)
+
+add_test(NAME sched_history_summary2
+ COMMAND sched_history_summary ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/Snorre/SNORRE)
+
+
+
+function( add_equinor_test name command )
+ add_test( NAME ${name}
+ COMMAND ${command} ${ARGN})
+
+ set_property( TEST ${name} PROPERTY LABELS EquinorData)
+ set_property( TEST ${name} PROPERTY ENVIRONMENT "ERT_ROOT=${ERT_ROOT}")
+endfunction()
+
+
+add_equinor_test(enkf_site_config
+ enkf_site_config /project/res/etc/ERT/site-config)
+
+add_equinor_test(enkf_gen_data_config
+ enkf_gen_data_config
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/gendata_test/RFT_E-3H_21
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/gendata_test/RFT_E-3H_21_empty)
+
+add_equinor_test(enkf_block_obs
+ enkf_block_obs
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/Gurbat/ECLIPSE.EGRID)
+
+add_equinor_test(enkf_obs_fs
+ enkf_obs_fs
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/obs_testing/config)
+
+add_equinor_test(enkf_obs_vector_fs
+ enkf_obs_vector_fs
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/obs_testing/config)
+
+add_equinor_test(enkf_plot_data_fs
+ enkf_plot_data_fs
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/plotData/config)
+
+add_equinor_test(enkf_time_map1 enkf_time_map)
+add_equinor_test(enkf_time_map2
+ enkf_time_map
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/Gurbat/ECLIPSE
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/ModifiedSummary/EXTRA_TSTEP
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/ModifiedSummary/SHORT
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/ModifiedSummary/MISSING_TSTEP)
+
+add_equinor_test(enkf_main_fs
+ enkf_main_fs
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/plotData/config)
+
+add_equinor_test(enkf_main_fs_current_file_test
+ enkf_main_fs_current_file_test
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/plotData/config)
+
+add_equinor_test(enkf_scale_correlated_std
+ enkf_scale_correlated_std
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/with_data/config
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/STD_SCALE_CORRELATED_OBS)
+
+add_equinor_test(enkf_plot_gendata_fs
+ enkf_plot_gendata_fs
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/with_GEN_DATA/config)
+
+add_equinor_test(enkf_state_report_step_compatible_TRUE
+ enkf_state_report_step_compatible
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/ecl_sum_compatible_true
+ config_ecl_sum_compatible_true
+ TRUE)
+
+add_equinor_test(enkf_state_report_step_compatible_FALSE
+ enkf_state_report_step_compatible
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/ecl_sum_compatible_false
+ config_ecl_sum_compatible_false
+ FALSE)
+
+add_equinor_test(enkf_state_manual_load_test
+ enkf_state_manual_load_test
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/ecl_sum_compatible_true
+ config_ecl_sum_compatible_true)
+
+
+add_equinor_test(enkf_state_summary_vars_present
+ enkf_state_skip_summary_load_test
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/ecl_summary_vars_config
+ config_summary_vars)
+
+add_equinor_test(enkf_state_no_summary_vars_present
+ enkf_state_skip_summary_load_test
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/ecl_no_summary_vars_config
+ config_no_summary_vars)
+
+add_equinor_test(enkf_export_field_test
+ enkf_export_field_test
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/export_fields/config
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/EXPORT_FIELD
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/EXPORT_FIELD_ECL_GRDECL
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/EXPORT_FIELD_RMS_ROFF)
+
+add_equinor_test(enkf_workflow_job_test
+ enkf_workflow_job_test
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/with_data/config
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/enkf_state_runpath/config_runpath_multiple_iterations
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal-tui/config/CREATE_CASE
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal-tui/config/INIT_CASE_FROM_EXISTING
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/LOAD_RESULTS
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/LOAD_RESULTS_ITER
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/OBSERVATION_RANKING
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/DATA_RANKING
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/EXPORT_RANKING
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/INIT_MISFIT_TABLE
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/EXPORT_RUNPATH
+ ${CMAKE_SOURCE_DIR}/${SHARE_DIR}/workflows/jobs/internal/config/PRE_SIMULATION_COPY)
+
+
+add_equinor_test(enkf_forward_init_SURFACE_TRUE
+ enkf_forward_init_SURFACE
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/forward_init/surface config_surface_true
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/forward_init/surface/Surface.irap
+ TRUE)
+
+add_equinor_test(enkf_forward_init_SURFACE_FALSE
+ enkf_forward_init_SURFACE
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/forward_init/surface config_surface_false
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/forward_init/surface/Surface.irap
+ FALSE)
+
+add_equinor_test(enkf_forward_init_FIELD_TRUE
+ enkf_forward_init_FIELD
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/forward_init/field config_field_true
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/forward_init/field/petro.grdecl
+ TRUE)
+
+add_equinor_test(enkf_forward_init_FIELD_FALSE
+ enkf_forward_init_FIELD
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/forward_init/field config_field_false
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/forward_init/field/petro.grdecl
+ FALSE)
+
+add_equinor_test(enkf_forward_init_transform_TRUE
+ enkf_forward_init_transform
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/transform transform_forward_init_true
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/transform/petro.grdecl
+ TRUE)
+
+add_equinor_test(enkf_forward_init_transform_FALSE
+ enkf_forward_init_transform
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/transform transform_forward_init_false
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/transform/petro.grdecl
+ FALSE)
+
+add_equinor_test(enkf_export_inactive_cells
+ enkf_export_inactive_cells
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/export_inactive_cells/config
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/export_inactive_cells/petro.grdecl)
+
+add_equinor_test(enkf_refcase_list
+ enkf_refcase_list
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/Gurbat/ECLIPSE
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/Gurbat*/ECLIPSE)
+
+add_equinor_test(enkf_refcase_list2
+ enkf_refcase_list
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/Gurbat/ECLIPSE
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/Gurbat*/ECLIPSE.*)
+
+add_equinor_test(enkf_ecl_config1 enkf_ecl_config)
+add_equinor_test(enkf_ecl_config2
+ enkf_ecl_config
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/ECLIPSE/Gurbat/ECLIPSE)
+
+add_equinor_test(enkf_ecl_config_config
+ enkf_ecl_config_config
+ ${CMAKE_SOURCE_DIR}/test-data/Equinor/config/ecl_config)
diff --git a/libres/lib/analysis/analysis_module.cpp b/libres/lib/analysis/analysis_module.cpp
new file mode 100644
index 00000000000..dcaf391b6b0
--- /dev/null
+++ b/libres/lib/analysis/analysis_module.cpp
@@ -0,0 +1,498 @@
+/*
+ Copyright (C) 2011 Equinor ASA, Norway.
+
+ The file 'analysis_module.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+#include
+#include
+
+#define ANALYSIS_MODULE_TYPE_ID 6610123
+
+struct analysis_module_struct {
+ UTIL_TYPE_ID_DECLARATION;
+ void * lib_handle;
+ void * module_data;
+ char * symbol_table;
+ char * lib_name;
+
+ analysis_free_ftype * freef;
+ analysis_alloc_ftype * alloc;
+ analysis_initX_ftype * initX;
+ analysis_updateA_ftype * updateA;
+ analysis_init_update_ftype * init_update;
+ analysis_complete_update_ftype * complete_update;
+
+ analysis_get_options_ftype * get_options;
+ analysis_set_int_ftype * set_int;
+ analysis_set_double_ftype * set_double;
+ analysis_set_bool_ftype * set_bool;
+ analysis_set_string_ftype * set_string;
+
+ analysis_has_var_ftype * has_var;
+ analysis_get_int_ftype * get_int;
+ analysis_get_double_ftype * get_double;
+ analysis_get_bool_ftype * get_bool;
+ analysis_get_ptr_ftype * get_ptr;
+
+ bool internal;
+ char * user_name; /* String used to identify this module for the user; not used in
+ the linking process. */
+};
+
+
+static std::string analysis_modules_dir;
+
+
+
+static analysis_module_type * analysis_module_alloc_empty( const char * symbol_table , const char * lib_name) {
+ analysis_module_type * module = (analysis_module_type*)util_malloc( sizeof * module );
+ UTIL_TYPE_ID_INIT( module , ANALYSIS_MODULE_TYPE_ID );
+
+ module->lib_handle = NULL;
+ module->initX = NULL;
+ module->updateA = NULL;
+ module->set_int = NULL;
+ module->set_bool = NULL;
+ module->set_double = NULL;
+ module->set_string = NULL;
+ module->module_data = NULL;
+ module->init_update = NULL;
+ module->complete_update = NULL;
+ module->has_var = NULL;
+ module->get_int = NULL;
+ module->get_double = NULL;
+ module->get_bool = NULL;
+ module->get_ptr = NULL;
+ module->alloc = NULL;
+
+ module->user_name = NULL;
+ module->symbol_table = util_alloc_string_copy( symbol_table );
+ module->lib_name = util_alloc_string_copy( lib_name );
+
+ return module;
+}
+
+
+static bool analysis_module_internal_check( analysis_module_type * module ) {
+ if (!module->user_name)
+ fprintf(stderr,"Invalid module loaded from lib:%s / symbol_table:%s - name not set\n", module->lib_name , module->symbol_table);
+
+ return true;
+}
+
+
+static analysis_module_type * analysis_module_alloc__( const analysis_table_type * table ,
+ const char * symbol_table ,
+ const char * lib_name ,
+ void * lib_handle ) {
+
+ analysis_module_type * module = analysis_module_alloc_empty( symbol_table , lib_name );
+
+ module->lib_handle = lib_handle;
+ module->initX = table->initX;
+ module->updateA = table->updateA;
+ module->init_update = table->init_update;
+ module->complete_update = table->complete_update;
+ module->set_int = table->set_int;
+ module->set_double = table->set_double;
+ module->set_string = table->set_string;
+ module->set_bool = table->set_bool;
+ module->alloc = table->alloc;
+ module->freef = table->freef;
+ module->get_options = table->get_options;
+ module->has_var = table->has_var;
+ module->get_int = table->get_int;
+ module->get_double = table->get_double;
+ module->get_bool = table->get_bool;
+ module->get_ptr = table->get_ptr;
+ analysis_module_set_name( module , table->name );
+
+ if (module->alloc)
+ module->module_data = module->alloc( );
+
+ if (!analysis_module_internal_check( module )) {
+ fprintf(stderr,"** Warning loading module: %s failed - internal inconsistency\n", module->user_name);
+ analysis_module_free( module );
+ module = NULL;
+ }
+
+ return module;
+}
+
+
+static analysis_module_type * analysis_module_alloc( const char * libname ,
+ const char * table_name ,
+ bool verbose,
+ analysis_module_load_status_enum * load_status) {
+ analysis_module_type * module = NULL;
+ void * lib_handle = nullptr;
+
+ if (libname == nullptr) {
+ // internal
+ lib_handle = dlopen(nullptr, RTLD_NOW);
+ } else {
+ // external, look for the library in /res/.libs
+ if (!analysis_modules_dir.empty()) {
+ auto lib_path = analysis_modules_dir + "/" + libname;
+ lib_handle = dlopen(lib_path.c_str(), RTLD_NOW);
+ if (lib_handle == nullptr && verbose)
+ fprintf(stderr, "Failed to load library:%s Error:%s\n", lib_path.c_str(), dlerror());
+ }
+
+ // external, look for library system-wide
+ if (lib_handle == nullptr) {
+ lib_handle = dlopen(libname, RTLD_NOW);
+ }
+
+ // error handling
+ if (lib_handle == nullptr) {
+ *load_status = DLOPEN_FAILURE;
+ if (verbose)
+ fprintf(stderr, "Failed to load library:%s Error:%s \n", libname, dlerror());
+ return NULL;
+ }
+ }
+
+ analysis_table_type * analysis_table = (analysis_table_type *) dlsym( lib_handle , table_name );
+ if (analysis_table != NULL) {
+ *load_status = LOAD_OK;
+ module = analysis_module_alloc__( analysis_table , table_name , libname , lib_handle );
+ } else {
+ *load_status = LOAD_SYMBOL_TABLE_NOT_FOUND;
+ if (verbose)
+ fprintf(stderr , "Failed to load symbol table:%s Error:%s \n",table_name , dlerror());
+ }
+
+ if (module == NULL)
+ dlclose( lib_handle );
+
+ if (module != NULL) {
+ if (libname == NULL)
+ module->internal = true;
+ else
+ module->internal = false;
+ }
+
+ return module;
+}
+
+analysis_module_type * analysis_module_alloc_internal__( const char * symbol_table , bool verbose , analysis_module_load_status_enum * load_status) {
+ return analysis_module_alloc( NULL , symbol_table , verbose , load_status);
+}
+
+analysis_module_type * analysis_module_alloc_internal( const char * symbol_table ) {
+ analysis_module_load_status_enum load_status;
+ return analysis_module_alloc_internal__( symbol_table , true , &load_status);
+}
+
+
+analysis_module_type * analysis_module_alloc_external__( const char * lib_name , bool verbose , analysis_module_load_status_enum * load_status) {
+ return analysis_module_alloc( lib_name , EXTERNAL_MODULE_NAME , verbose , load_status);
+}
+
+
+analysis_module_type * analysis_module_alloc_external( const char * lib_name) {
+ analysis_module_load_status_enum load_status;
+ return analysis_module_alloc_external__( lib_name , true , &load_status);
+}
+
+/*****************************************************************/
+
+const char * analysis_module_get_name( const analysis_module_type * module ) {
+ return module->user_name;
+}
+
+void analysis_module_set_name( analysis_module_type * module , const char * name) {
+ module->user_name = util_realloc_string_copy( module->user_name , name );
+}
+
+
+const char * analysis_module_get_table_name( const analysis_module_type * module) {
+ return module->symbol_table;
+}
+
+const char * analysis_module_get_lib_name( const analysis_module_type * module) {
+ return module->lib_name;
+}
+
+bool analysis_module_internal( const analysis_module_type * module ) {
+ return module->internal;
+}
+
+/*****************************************************************/
+
+static UTIL_SAFE_CAST_FUNCTION( analysis_module , ANALYSIS_MODULE_TYPE_ID )
+UTIL_IS_INSTANCE_FUNCTION( analysis_module , ANALYSIS_MODULE_TYPE_ID )
+
+
+void analysis_module_free( analysis_module_type * module ) {
+ if (module->freef != NULL)
+ module->freef( module->module_data );
+
+ free( module->lib_name );
+ free( module->user_name );
+ free( module->symbol_table );
+ dlclose( module->lib_handle );
+ free( module );
+}
+
+
+/*****************************************************************/
+/* Update functions */
+
+
+void analysis_module_initX(analysis_module_type * module ,
+ matrix_type * X ,
+ const matrix_type * A ,
+ const matrix_type * S ,
+ const matrix_type * R ,
+ const matrix_type * dObs ,
+ const matrix_type * E ,
+ const matrix_type * D,
+ rng_type * rng) {
+
+
+ module->initX(module->module_data , X , A , S , R , dObs , E , D, rng);
+}
+
+
+void analysis_module_updateA(analysis_module_type * module ,
+ matrix_type * A ,
+ const matrix_type * S ,
+ const matrix_type * R ,
+ const matrix_type * dObs ,
+ const matrix_type * E ,
+ const matrix_type * D ,
+ const module_info_type* module_info,
+ rng_type * rng) {
+
+ module->updateA(module->module_data , A , S , R , dObs , E , D, module_info, rng);
+}
+
+
+
+
+void analysis_module_init_update( analysis_module_type * module ,
+ const bool_vector_type * ens_mask ,
+ const bool_vector_type * obs_mask ,
+ const matrix_type * S ,
+ const matrix_type * R ,
+ const matrix_type * dObs ,
+ const matrix_type * E ,
+ const matrix_type * D,
+ rng_type * rng) {
+
+ /*
+ The ensemble and observation masks sent to the init_update() function can be
+ misleading? When assembling the S,R,E and D matrices the inactive
+ observations & realisatons have been filtered out completely, i.e. the
+ ens_mask and obs_mask variables are *not* used to filter out rows and
+ columns from the S,R,E and D matrices.
+
+ In the case of multi iteration updates we need to detect the changes in
+ active realisatons/observations between iterations, and that is the purpose
+ of the ens_mask and obs_mask variables.
+ */
+
+
+ if (bool_vector_count_equal(ens_mask, true) != matrix_get_columns(S))
+ throw std::invalid_argument("Internal error - number of columns in S must be equal to number of *active* realisatons");
+
+ if (bool_vector_count_equal(obs_mask, true) != matrix_get_rows(S))
+ throw std::invalid_argument("Internal error - number of rows in S must be equal to number of *active* observations");
+
+ if (module->init_update != NULL)
+ module->init_update( module->module_data , ens_mask , obs_mask, S , R , dObs , E , D, rng);
+}
+
+
+void analysis_module_complete_update( analysis_module_type * module ) {
+ if (module->complete_update != NULL)
+ module->complete_update( module->module_data );
+}
+
+/*****************************************************************/
+
+
+static bool analysis_module_set_int(analysis_module_type * module , const char * flag , int value) {
+ if (module->set_int != NULL)
+ return module->set_int( module->module_data , flag , value );
+ else
+ return false;
+}
+
+static bool analysis_module_set_double(analysis_module_type * module , const char * var , double value) {
+ if (module->set_double != NULL)
+ return module->set_double( module->module_data , var , value );
+ else
+ return false;
+}
+
+
+static bool analysis_module_set_bool(analysis_module_type * module , const char * var , bool value) {
+ if (module->set_bool != NULL)
+ return module->set_bool( module->module_data , var , value );
+ else
+ return false;
+}
+
+
+static bool analysis_module_set_string(analysis_module_type * module , const char * var , const char * value) {
+ if (module->set_string != NULL)
+ return module->set_string( module->module_data , var , value );
+ else
+ return false;
+}
+
+
+
+/*
+ The input value typically comes from the configuration system and
+ is in terms of a string, irrespective of the fundamental type of
+ the underlying parameter. The algorithm for setting the parameter
+ tries datatypes as follows: integer - double - string.
+
+ For the numeric datatypes the algorithm is two step:
+
+ 1. Try the conversion string -> numeric.
+ 2. Try calling the analysis_module_set_xxx() function.
+
+ Observe that this implies that the same variable name can NOT be
+ used for different variable types.
+*/
+
+bool analysis_module_set_var( analysis_module_type * module , const char * var_name , const char * string_value ) {
+ bool set_ok = false;
+ {
+ int int_value;
+
+ if (util_sscanf_int( string_value , &int_value ))
+ set_ok = analysis_module_set_int( module , var_name , int_value );
+
+
+ if (set_ok)
+ return true;
+ }
+
+ {
+ double double_value;
+ if (util_sscanf_double( string_value , &double_value ))
+ set_ok = analysis_module_set_double( module , var_name , double_value );
+
+ if (set_ok)
+ return true;
+ }
+
+ {
+ bool bool_value;
+ if (util_sscanf_bool( string_value , &bool_value))
+ set_ok = analysis_module_set_bool( module , var_name , bool_value );
+
+ if (set_ok)
+ return true;
+ }
+
+
+ set_ok = analysis_module_set_string( module , var_name , string_value );
+ if (!set_ok)
+ fprintf(stderr,"** Warning: failed to set %s=%s for analysis module:%s\n", var_name , string_value , module->user_name);
+
+ return set_ok;
+}
+
+
+bool analysis_module_check_option( const analysis_module_type * module , long flag) {
+ if ((flag & module->get_options( module->module_data , flag )) == flag)
+ return true;
+ else
+ return false;
+}
+
+
+bool analysis_module_has_var( const analysis_module_type * module , const char * var) {
+ if (module->has_var)
+ return module->has_var( module->module_data , var );
+ else
+ return false;
+}
+
+
+int analysis_module_get_int( const analysis_module_type * module , const char * var) {
+ if (analysis_module_has_var( module , var )) {
+ if (module->get_int != NULL)
+ return module->get_int( module->module_data , var );
+ else
+ util_exit("%s: Tried to get integer variable:%s from module:%s - get_int() method not implemented for this module\n" , __func__ , var , module->user_name);
+ } else
+ util_exit("%s: Tried to get integer variable:%s from module:%s - module does not support this variable \n" , __func__ , var , module->user_name);
+
+ return 0;
+}
+
+
+bool analysis_module_get_bool( const analysis_module_type * module , const char * var) {
+ if (analysis_module_has_var( module , var )) {
+ if (module->get_bool != NULL)
+ return module->get_bool( module->module_data , var );
+ else
+ util_exit("%s: Tried to get bool variable:%s from module:%s - get_int() method not implemented for this module\n" , __func__ , var , module->user_name);
+ } else
+ util_exit("%s: Tried to get bool variable:%s from module:%s - module does not support this variable \n" , __func__ , var , module->user_name);
+
+ return false;
+}
+
+
+double analysis_module_get_double( const analysis_module_type * module , const char * var) {
+ if (analysis_module_has_var( module , var )) {
+ if (module->get_double != NULL)
+ return module->get_double( module->module_data , var );
+ else
+ util_exit("%s: Tried to get double variable:%s from module:%s - get_double() method not implemented for this module\n" , __func__ , var , module->user_name);
+ } else
+ util_exit("%s: Tried to get double variable:%s from module:%s - module does not support this variable \n" , __func__ , var , module->user_name);
+
+ return 0;
+}
+
+
+void * analysis_module_get_ptr( const analysis_module_type * module , const char * var) {
+ if (analysis_module_has_var( module , var )) {
+ if (module->get_double != NULL)
+ return module->get_ptr( module->module_data , var );
+ else
+ util_exit("%s: Tried to get pointer variable:%s from module:%s - get_ptr() method not implemented for this module\n" , __func__ , var , module->user_name);
+ } else
+ util_exit("%s: Tried to get pointer variable:%s from module:%s - module does not support this variable \n" , __func__ , var , module->user_name);
+
+ return NULL;
+}
+
+
+extern "C"
+void set_analysis_modules_dir(const char * lib) {
+ analysis_modules_dir = lib;
+}
diff --git a/libres/lib/analysis/bootstrap_enkf.cpp b/libres/lib/analysis/bootstrap_enkf.cpp
new file mode 100644
index 00000000000..86e10ce6986
--- /dev/null
+++ b/libres/lib/analysis/bootstrap_enkf.cpp
@@ -0,0 +1,294 @@
+/*
+ Copyright (C) 2011 Equinor ASA, Norway.
+
+ The file 'bootstrap_enkf.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+
+#define BOOTSTRAP_ENKF_TYPE_ID 741223
+
+#define INVALID_SUBSPACE_DIMENSION -1
+#define INVALID_TRUNCATION -1
+#define DEFAULT_TRUNCATION 0.95
+#define DEFAULT_NCOMP INVALID_SUBSPACE_DIMENSION
+
+#define DEFAULT_DO_CV false
+#define DEFAULT_NFOLDS 10
+#define NFOLDS_KEY "BOOTSTRAP_NFOLDS"
+
+
+typedef struct {
+ UTIL_TYPE_ID_DECLARATION;
+ std_enkf_data_type * std_enkf_data;
+ cv_enkf_data_type * cv_enkf_data;
+ long option_flags;
+ bool doCV;
+} bootstrap_enkf_data_type;
+
+
+static UTIL_SAFE_CAST_FUNCTION( bootstrap_enkf_data , BOOTSTRAP_ENKF_TYPE_ID )
+static UTIL_SAFE_CAST_FUNCTION_CONST( bootstrap_enkf_data , BOOTSTRAP_ENKF_TYPE_ID )
+
+
+void bootstrap_enkf_set_doCV( bootstrap_enkf_data_type * data , bool doCV) {
+ data->doCV = doCV;
+}
+
+
+
+void bootstrap_enkf_set_truncation( bootstrap_enkf_data_type * boot_data , double truncation ) {
+ std_enkf_set_truncation( boot_data->std_enkf_data , truncation );
+ cv_enkf_set_truncation( boot_data->cv_enkf_data , truncation );
+}
+
+
+void bootstrap_enkf_set_subspace_dimension( bootstrap_enkf_data_type * boot_data , int ncomp) {
+ std_enkf_set_subspace_dimension( boot_data->std_enkf_data , ncomp );
+ cv_enkf_set_subspace_dimension( boot_data->cv_enkf_data , ncomp );
+}
+
+
+void * bootstrap_enkf_data_alloc( ) {
+ bootstrap_enkf_data_type * boot_data = (bootstrap_enkf_data_type*)util_malloc( sizeof * boot_data );
+ UTIL_TYPE_ID_INIT( boot_data , BOOTSTRAP_ENKF_TYPE_ID );
+
+ boot_data->std_enkf_data = (std_enkf_data_type*)std_enkf_data_alloc( );
+ boot_data->cv_enkf_data = (cv_enkf_data_type*)cv_enkf_data_alloc( );
+
+ bootstrap_enkf_set_truncation( boot_data , DEFAULT_TRUNCATION );
+ bootstrap_enkf_set_subspace_dimension( boot_data , DEFAULT_NCOMP );
+ bootstrap_enkf_set_doCV( boot_data , DEFAULT_DO_CV);
+ boot_data->option_flags = ANALYSIS_NEED_ED + ANALYSIS_UPDATE_A + ANALYSIS_SCALE_DATA;
+ return boot_data;
+}
+
+
+
+
+
+void bootstrap_enkf_data_free( void * arg ) {
+ bootstrap_enkf_data_type * boot_data = bootstrap_enkf_data_safe_cast( arg );
+ {
+ std_enkf_data_free( boot_data->std_enkf_data );
+ cv_enkf_data_free( boot_data->cv_enkf_data );
+ }
+ free( boot_data );
+}
+
+
+static int ** alloc_iens_resample( rng_type * rng , int ens_size ) {
+ int ** iens_resample;
+ int iens;
+
+ iens_resample = (int**)util_calloc( ens_size , sizeof * iens_resample );
+ for (iens = 0; iens < ens_size; iens++)
+ iens_resample[iens] = (int*)util_calloc( ens_size , sizeof( ** iens_resample ) );
+
+ {
+ int i,j;
+ for (i=0; i < ens_size; i++)
+ for (j=0; j < ens_size; j++)
+ iens_resample[i][j] = rng_get_int( rng , ens_size );
+ }
+ return iens_resample;
+}
+
+
+static void free_iens_resample( int ** iens_resample, int ens_size ) {
+ for (int i=0; i < ens_size; i++)
+ free( iens_resample[i] );
+ free( iens_resample );
+}
+
+
+
+void bootstrap_enkf_updateA(void * module_data ,
+ matrix_type * A ,
+ const matrix_type * S ,
+ const matrix_type * R ,
+ const matrix_type * dObs ,
+ const matrix_type * E ,
+ const matrix_type * D ,
+ const module_info_type* module_info,
+ rng_type * rng) {
+
+ bootstrap_enkf_data_type * bootstrap_data = bootstrap_enkf_data_safe_cast( module_data );
+ {
+ const int num_cpu_threads = 4;
+ int ens_size = matrix_get_columns( A );
+ matrix_type * X = matrix_alloc( ens_size , ens_size );
+ matrix_type * A0 = matrix_alloc_copy( A );
+ matrix_type * S_resampled = matrix_alloc_copy( S );
+ matrix_type * A_resampled = matrix_alloc( matrix_get_rows(A0) , matrix_get_columns( A0 ));
+ int ** iens_resample = alloc_iens_resample( rng , ens_size );
+ {
+ int ensemble_members_loop;
+ for ( ensemble_members_loop = 0; ensemble_members_loop < ens_size; ensemble_members_loop++) {
+ int ensemble_counter;
+ /* Resample A and meas_data. Here we are careful to resample the working copy.*/
+ {
+ {
+ int_vector_type * bootstrap_components = int_vector_alloc( ens_size , 0);
+ for (ensemble_counter = 0; ensemble_counter < ens_size; ensemble_counter++) {
+ int random_column = iens_resample[ ensemble_members_loop][ensemble_counter];
+ int_vector_iset( bootstrap_components , ensemble_counter , random_column );
+ matrix_copy_column( A_resampled , A0 , ensemble_counter , random_column );
+ matrix_copy_column( S_resampled , S , ensemble_counter , random_column );
+ }
+ int_vector_select_unique( bootstrap_components );
+ int_vector_free( bootstrap_components );
+ }
+
+ if (bootstrap_data->doCV) {
+ const bool_vector_type * ens_mask = NULL;
+ const bool_vector_type * obs_mask = NULL;
+ cv_enkf_init_update(bootstrap_data->cv_enkf_data, ens_mask, obs_mask, S_resampled, R, dObs, E, D, rng);
+ cv_enkf_initX(bootstrap_data->cv_enkf_data, X, A_resampled, S_resampled, R, dObs, E, D, rng);
+ } else
+ std_enkf_initX(bootstrap_data->std_enkf_data, X, NULL, S_resampled, R, dObs, E, D, rng);
+
+
+ matrix_inplace_matmul_mt1( A_resampled , X , num_cpu_threads );
+ matrix_inplace_add( A_resampled , A0 );
+ matrix_copy_column( A , A_resampled, ensemble_members_loop, ensemble_members_loop);
+
+ }
+ }
+ }
+
+
+ free_iens_resample( iens_resample , ens_size);
+ matrix_free( X );
+ matrix_free( S_resampled );
+ matrix_free( A_resampled );
+ matrix_free( A0 );
+ }
+}
+
+
+
+
+
+
+long bootstrap_enkf_get_options( void * arg , long flag) {
+ bootstrap_enkf_data_type * bootstrap_data = bootstrap_enkf_data_safe_cast( arg );
+ {
+ return bootstrap_data->option_flags;
+ }
+}
+
+
+bool bootstrap_enkf_set_double( void * arg , const char * var_name , double value) {
+ bootstrap_enkf_data_type * bootstrap_data = bootstrap_enkf_data_safe_cast( arg );
+ {
+ if (std_enkf_set_double( bootstrap_data->std_enkf_data , var_name , value ))
+ return true;
+ else {
+ return false;
+ }
+ }
+}
+
+
+bool bootstrap_enkf_set_int( void * arg , const char * var_name , int value) {
+ bootstrap_enkf_data_type * bootstrap_data = bootstrap_enkf_data_safe_cast( arg );
+ {
+ if (std_enkf_set_int( bootstrap_data->std_enkf_data , var_name , value ))
+ return true;
+ else {
+ return false;
+ }
+ }
+}
+
+
+bool bootstrap_enkf_set_bool( void * arg , const char * var_name , bool value) {
+ bootstrap_enkf_data_type * bootstrap_data = bootstrap_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , "CV" ) == 0)
+ bootstrap_data->doCV = value;
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+
+bool bootstrap_enkf_has_var( const void * arg, const char * var_name) {
+ const bootstrap_enkf_data_type * module_data = bootstrap_enkf_data_safe_cast_const( arg );
+ {
+ return std_enkf_has_var(module_data->std_enkf_data, var_name);
+ }
+}
+
+double bootstrap_enkf_get_double( const void * arg, const char * var_name) {
+ const bootstrap_enkf_data_type * module_data = bootstrap_enkf_data_safe_cast_const( arg );
+ {
+ return std_enkf_get_double( module_data->std_enkf_data , var_name);
+ }
+}
+
+int bootstrap_enkf_get_int( const void * arg, const char * var_name) {
+ const bootstrap_enkf_data_type * module_data = bootstrap_enkf_data_safe_cast_const( arg );
+ {
+ return std_enkf_get_int( module_data->std_enkf_data , var_name);
+ }
+}
+
+
+
+
+#ifdef INTERNAL_LINK
+#define LINK_NAME BOOTSTRAP_ENKF
+#else
+#define LINK_NAME EXTERNAL_MODULE_SYMBOL
+#endif
+
+
+
+analysis_table_type LINK_NAME = {
+ .name = "BOOTSTRAP_ENKF",
+ .updateA = bootstrap_enkf_updateA,
+ .initX = NULL,
+ .init_update = NULL,
+ .complete_update = NULL,
+ .freef = bootstrap_enkf_data_free,
+ .alloc = bootstrap_enkf_data_alloc,
+ .set_int = bootstrap_enkf_set_int ,
+ .set_double = bootstrap_enkf_set_double ,
+ .set_bool = bootstrap_enkf_set_bool ,
+ .set_string = NULL ,
+ .get_options = bootstrap_enkf_get_options ,
+ .has_var = bootstrap_enkf_has_var,
+ .get_int = bootstrap_enkf_get_int,
+ .get_double = bootstrap_enkf_get_double,
+ .get_bool = NULL,
+ .get_ptr = NULL,
+};
diff --git a/libres/lib/analysis/cv_enkf.cpp b/libres/lib/analysis/cv_enkf.cpp
new file mode 100644
index 00000000000..b76aa2ed224
--- /dev/null
+++ b/libres/lib/analysis/cv_enkf.cpp
@@ -0,0 +1,726 @@
+/*
+ Copyright (C) 2011 Equinor ASA, Norway.
+
+ The file 'cv_enkf.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#define CV_ENKF_TYPE_ID 765523
+
+#define INVALID_SUBSPACE_DIMENSION -1
+#define INVALID_TRUNCATION -1
+#define DEFAULT_SUBSPACE_DIMENSION INVALID_SUBSPACE_DIMENSION
+
+#define DEFAULT_NFOLDS 10
+#define DEFAULT_PEN_PRESS false
+#define NFOLDS_KEY "CV_NFOLDS"
+#define CV_PEN_PRESS_KEY "CV_PEN_PRESS"
+
+
+
+
+struct cv_enkf_data_struct {
+ UTIL_TYPE_ID_DECLARATION;
+ matrix_type * Z;
+ matrix_type * Rp;
+ matrix_type * Dp;
+ double truncation;
+ int nfolds;
+ int subspace_dimension; // ENKF_NCOMP_KEY (-1: use Truncation instead)
+ long option_flags;
+ bool penalised_press;
+};
+
+
+
+static UTIL_SAFE_CAST_FUNCTION( cv_enkf_data , CV_ENKF_TYPE_ID )
+static UTIL_SAFE_CAST_FUNCTION_CONST( cv_enkf_data , CV_ENKF_TYPE_ID )
+
+
+void cv_enkf_set_truncation( cv_enkf_data_type * data , double truncation ) {
+ data->truncation = truncation;
+ if (truncation > 0.0)
+ data->subspace_dimension = INVALID_SUBSPACE_DIMENSION;
+}
+
+
+void cv_enkf_set_subspace_dimension( cv_enkf_data_type * data , int subspace_dimension) {
+ data->subspace_dimension = subspace_dimension;
+ if (subspace_dimension > 0)
+ data->truncation = INVALID_TRUNCATION;
+}
+
+
+void cv_enkf_set_nfolds( cv_enkf_data_type * data , int nfolds ) {
+ data->nfolds = nfolds;
+}
+
+void cv_enkf_set_pen_press( cv_enkf_data_type * data , bool value ) {
+ data->penalised_press = value;
+}
+
+
+void * cv_enkf_data_alloc( ) {
+ cv_enkf_data_type * data = (cv_enkf_data_type*)util_malloc( sizeof * data);
+ UTIL_TYPE_ID_INIT( data , CV_ENKF_TYPE_ID );
+
+ data->Z = NULL;
+ data->Rp = NULL;
+ data->Dp = NULL;
+
+ data->penalised_press = DEFAULT_PEN_PRESS;
+ data->option_flags = ANALYSIS_NEED_ED + ANALYSIS_USE_A + ANALYSIS_SCALE_DATA;
+ data->nfolds = DEFAULT_NFOLDS;
+ cv_enkf_set_truncation( data , DEFAULT_ENKF_TRUNCATION_ );
+
+ return data;
+}
+
+
+
+void cv_enkf_data_free( void * arg ) {
+ cv_enkf_data_type * cv_data = cv_enkf_data_safe_cast( arg );
+ {
+ matrix_safe_free( cv_data->Z );
+ matrix_safe_free( cv_data->Rp );
+ matrix_safe_free( cv_data->Dp );
+ }
+ free( cv_data );
+}
+
+
+
+
+
+
+void cv_enkf_init_update( void * arg ,
+ const bool_vector_type * ens_mask ,
+ const bool_vector_type * obs_mask ,
+ const matrix_type * S ,
+ const matrix_type * R ,
+ const matrix_type * dObs ,
+ const matrix_type * E ,
+ const matrix_type * D,
+ rng_type * rng) {
+
+ cv_enkf_data_type * cv_data = cv_enkf_data_safe_cast( arg );
+ {
+ int i, j;
+ const int nrobs = matrix_get_rows( S );
+ const int nrens = matrix_get_columns( S );
+ const int nrmin = util_int_min( nrobs , nrens );
+
+ cv_data->Z = matrix_alloc( nrmin , nrens );
+ cv_data->Rp = matrix_alloc( nrmin , nrmin );
+ cv_data->Dp = matrix_alloc( nrmin , nrens );
+
+ /*
+ Compute SVD(S)
+ */
+ matrix_type * U0 = matrix_alloc( nrobs , nrmin ); /* Left singular vectors. */
+ matrix_type * V0T = matrix_alloc( nrmin , nrens ); /* Right singular vectors. */
+
+ double * inv_sig0 = (double*)util_calloc( nrmin , sizeof * inv_sig0 );
+ double * sig0 = inv_sig0;
+
+ printf("Computing svd using truncation %0.4f\n",cv_data->truncation);
+
+ enkf_linalg_svdS(S , cv_data->truncation , cv_data->subspace_dimension , DGESVD_MIN_RETURN , inv_sig0 , U0 , V0T);
+
+ /* Need to use the original non-inverted singular values. */
+ for(i = 0; i < nrmin; i++)
+ if ( inv_sig0[i] > 0 )
+ sig0[i] = 1.0 / inv_sig0[i];
+
+ /*
+ Compute the actual principal components, Z = sig0 * VOT
+ NOTE: Z contains potentially alot of redundant zeros, but
+ we do not care about this for now
+ */
+
+ for(i = 0; i < nrmin; i++)
+ for(j = 0; j < nrens; j++)
+ matrix_iset( cv_data->Z , i , j , sig0[i] * matrix_iget( V0T , i , j ) );
+
+ /* Also compute Rp */
+ {
+ matrix_type * X0 = matrix_alloc( nrmin , matrix_get_rows( R ));
+ matrix_dgemm(X0 , U0 , R , true , false , 1.0 , 0.0); /* X0 = U0^T * R */
+ matrix_dgemm(cv_data->Rp , X0 , U0 , false , false , 1.0 , 0.0); /* Rp = X0 * U0 */
+ matrix_free(X0);
+ }
+
+ /*We also need to compute the reduced "Innovation matrix" Dp = U0' * D */
+ matrix_dgemm(cv_data->Dp , U0 , D , true , false , 1.0 , 0.0);
+
+
+ free(inv_sig0);
+ matrix_free(U0);
+ matrix_free(V0T);
+
+ /*
+ 2: Diagonalize the S matrix; singular vectors etc. needed later in the local CV:
+ (V0T = transposed right singular vectors of S, Z = scaled principal components,
+ eig = scaled, inverted singular vectors, U0 = left singular vectors of S
+ eig = inv(I+Lambda1),(Eq.14.30, and 14.29, Evensen, 2007, respectively)
+ */
+ }
+}
+
+
+
+/*Function that computes the PRESS for different subspace dimensions using
+ m-fold CV
+ INPUT :
+ A : State-Vector ensemble matrix
+ Z : Ensemble matrix of principal components
+ Rp : Reduced order Observation error matrix
+ indexTrain: index of training ensemble
+ indexTest: index of test ensemble
+ nTest : number of members in the training ensemble
+ nTrain . number of members in the test ensemble
+ foldIndex: integer specifying which "cv-fold" we are considering
+
+ OUTPUT:
+ cvErr : UPDATED MATRIX OF PRESS VALUES
+
+*/
+
+static void cv_enkf_get_cv_error_prin_comp( cv_enkf_data_type * cv_data ,
+ matrix_type * cvErr ,
+ const matrix_type * A ,
+ const int * indexTest,
+ const int * indexTrain ,
+ const int nTest ,
+ const int nTrain ,
+ const int foldIndex,
+ const int maxP) {
+ /*
+ We need to predict ATest(p), for p = 1,...,nens -1, based on the estimated regression model:
+ AHatTest(p) = A[:,indexTrain] * Z[1:p,indexTrain]'* inv( Z[1:p,indexTrain] * Z[1:p,indexTrain]' + (nens-1) * Rp[1:p,1:p] ) * Z[1:p,indexTest];
+ */
+
+
+ const int nx = matrix_get_rows( A );
+ matrix_type * AHat = matrix_alloc(nx , nTest );
+ matrix_type * ATrain = matrix_alloc( nx , nTrain );
+
+ int p,i,j;
+
+
+ /* Copy elements*/
+ for (i = 0; i < nx; i++)
+ for (j = 0; j < nTrain; j++)
+ matrix_iset(ATrain , i , j , matrix_iget( A , i , indexTrain[j]));
+
+
+ for (p = 0; p < maxP; p++) {
+ matrix_type * ZpTrain = matrix_alloc( p + 1, nTrain );
+ matrix_type *SigDp = matrix_alloc( p + 1 , p + 1);
+
+
+ for (i = 0; i <= p ; i++)
+ for (j = 0; j < nTrain; j++)
+ matrix_iset(ZpTrain , i , j , matrix_iget(cv_data->Z , i , indexTrain[j]));
+
+ /* SigDp = ZpTrain * ZpTrain' */
+ matrix_dgemm( SigDp , ZpTrain , ZpTrain, false , true , 1.0, 0.0);
+
+ /* SigDp += (nTrain - 1) * Rp */
+ for(i = 0; i <= p; i++)
+ for( j = 0; j <= p; j++)
+ matrix_iadd( SigDp , i , j , (nTrain - 1) * matrix_iget(cv_data->Rp, i, j));
+
+
+ /* Invert the covariance matrix for the principal components */
+ {
+ int inv_ok = matrix_inv( SigDp );
+ if ( inv_ok != 0 )
+ util_abort("%s: inversion of covariance matrix for the principal components failed for subspace dimension p = %d\n - aborting \n",__func__,p+1);
+ }
+
+
+ {
+ matrix_type * W = matrix_alloc(p + 1 , nTest );
+ matrix_type * W2 = matrix_alloc(nTrain , nTest );
+
+ /* W = inv(SigDp) * ZTest */
+ for (i = 0; i <= p; i++) {
+ for (j = 0; j < nTest; j++) {
+ double ksum = 0.0;
+ for (int k = 0; k <= p; k++)
+ ksum += matrix_iget(SigDp , i , k) * matrix_iget(cv_data->Z , k , indexTest[j]);
+
+ matrix_iset(W , i , j , ksum);
+ }
+ }
+
+ /* W2 = ZpTrain' * W */
+ matrix_dgemm( W2 , ZpTrain , W , true , false , 1.0 , 0.0);
+
+ /* Estimate the state-vector */
+ matrix_matmul( AHat , ATrain , W2 );
+
+ matrix_free( W2 );
+ matrix_free( W );
+ }
+
+
+ /*Compute Press Statistic: */
+ {
+ double R2Sum = 0;
+
+ for (i = 0; i < nx; i++) {
+ for (j = 0; j < nTest; j++) {
+ double tmp = matrix_iget(A , i , indexTest[j]) - matrix_iget(AHat , i , j);
+ R2Sum += tmp * tmp;
+ }
+ }
+ matrix_iset( cvErr , p , foldIndex , R2Sum );
+ }
+
+
+
+ matrix_free( ZpTrain );
+ matrix_free( SigDp );
+ } /*end for p */
+
+ matrix_free( AHat );
+ matrix_free( ATrain );
+}
+
+
+
+
+
+
+int cv_enkf_get_optimal_numb_comp(cv_enkf_data_type * cv_data ,
+ const matrix_type * cvErr ,
+ const int maxP ) {
+
+
+ double * cvMean = (double*)util_calloc( maxP , sizeof * cvMean );
+ double * cvStd = (double*)util_calloc( maxP , sizeof * cvStd );
+ int optP;
+
+ {
+ for (int p = 0; p < maxP; p++ ){
+ double err_sum = 0;
+ for (int folds = 0; folds < cv_data->nfolds; folds++ )
+ err_sum += matrix_iget( cvErr , p, folds );
+
+ cvMean[p] = err_sum / cv_data->nfolds;
+ }
+
+ for ( int p = 0; p < maxP; p++){
+ double err_sum2 = 0;
+ for ( int folds = 0; folds < cv_data->nfolds; folds++)
+ err_sum2 += pow( matrix_iget( cvErr , p , folds ) - cvMean[p] , 2);
+
+ cvStd[p] = sqrt( err_sum2 / (cv_data->nfolds - 1) );
+ }
+ }
+
+ {
+ double minErr = cvMean[0];
+ int i;
+ optP = 1;
+
+ printf("PRESS:\n");
+ printf("%f\n",cvMean[0]);
+ for (i = 1; i < maxP; i++) {
+ printf("%f\n",cvMean[i]);
+ if ((cvMean[i] < minErr) && (cvMean[i] > 0.0)) {
+ minErr = cvMean[i];
+ optP = i+1;
+ }
+ }
+
+
+
+
+ if (cv_data->penalised_press) {
+ for ( i = 0; i < optP; i++){
+ if( cvMean[i] - cvStd[i] <= minErr ){
+ optP = i+1;
+ break;
+ }
+ }
+ }
+ }
+
+ free( cvStd );
+ free( cvMean );
+ return optP;
+}
+
+
+
+
+
+
+/* Function that performs cross-validation to find the optimal subspace dimension, */
+
+
+static int get_optimal_principal_components( cv_enkf_data_type * cv_data ,
+ const matrix_type * A,
+ rng_type * rng) {
+
+
+ const int nrens = matrix_get_columns( cv_data->Z );
+ const int nrmin = matrix_get_rows( cv_data->Z );
+
+
+
+ matrix_type * cvError;
+ int * randperms = (int*)util_calloc( nrens , sizeof * randperms);
+
+ int maxP = nrmin;
+ int optP;
+
+
+ /* We only want to search the non-zero eigenvalues */
+ for (int i = 0; i < nrmin; i++) {
+ if (matrix_iget(cv_data->Z,i,1) == 0.0) {
+ maxP = i;
+ break;
+ }
+ }
+
+ if (maxP > nrmin)
+ maxP = nrmin - 1; // <- Change by Joakim; using nrmin here will load to out
+ // bounds access oc cv_data->Z at line 460.
+
+
+
+
+ if ( nrens < cv_data->nfolds )
+ util_abort("%s: number of ensemble members %d need to be larger than the number of cv-folds - aborting \n",
+ __func__,
+ nrens,
+ cv_data->nfolds);
+
+ for (int i=0; i < nrens; i++)
+ randperms[i] = i;
+
+ rng_shuffle_int( rng , randperms , nrens );
+
+ cvError = matrix_alloc( maxP , cv_data->nfolds );
+ {
+ int ntest, ntrain, k,j,i;
+ int * indexTest = (int*)util_calloc( nrens , sizeof * indexTest );
+ int * indexTrain = (int*)util_calloc( nrens , sizeof * indexTrain );
+ for (i = 0; i < cv_data->nfolds; i++) {
+ ntest = 0;
+ ntrain = 0;
+ k = i;
+ /*extract members for the training and test ensembles */
+ for (j = 0; j < nrens; j++) {
+ if (j == k) {
+ indexTest[ntest] = randperms[j];
+ k += cv_data->nfolds;
+ ntest++;
+ } else {
+ indexTrain[ntrain] = randperms[j];
+ ntrain++;
+ }
+ }
+
+ /*Perform CV for each subspace dimension p */
+ cv_enkf_get_cv_error_prin_comp( cv_data , cvError , A , indexTest , indexTrain, ntest, ntrain , i , maxP);
+ }
+ free( indexTest );
+ free( indexTrain );
+ }
+
+
+ /* find optimal truncation value for the cv-scheme */
+ optP = cv_enkf_get_optimal_numb_comp( cv_data , cvError , maxP);
+
+ matrix_free( cvError );
+ free( randperms );
+ return optP;
+}
+
+
+
+/*NB! HERE WE COUNT optP from 0,1,2,... */
+static void getW_prin_comp(cv_enkf_data_type * cv_data , matrix_type *W , const int optP) {
+
+ int i, j;
+ double tmp2;
+ int nrens = matrix_get_columns( cv_data->Z );
+
+ /* Finally, compute W = Z(1:p,:)' * inv(Z(1:p,:) * Z(1:p,:)' + (n -1) * Rp) */
+ matrix_type *Zp = matrix_alloc( optP, nrens );
+ matrix_type *SigZp = matrix_alloc( optP ,optP);
+
+ // This loop will fail with i to large whcn accessing cv_data->Z
+ // if we do not limit maxP to nrmin - 1?
+ for (i = 0; i < optP ; i++)
+ for (j = 0; j < nrens; j++)
+ matrix_iset_safe(Zp , i , j , matrix_iget_safe(cv_data->Z , i ,j));
+
+ // Matrix copy_block should be used in stead of the double (i,j) loop;
+ // however that failed because the cv_data->Z matrix had one row too few?
+ // matrix_copy_block( Zp , 0 , 0 , optP , nrens , cv_data->Z , 0 , 0 );
+
+ /*Compute SigZp = Zp * Zp' */
+ matrix_dgemm( SigZp , Zp , Zp, false , true , 1.0, 0.0);
+
+ /*Add (ntrain-1) * Rp*/
+ for(i = 0; i < optP; i++) {
+ for( j = 0; j < optP; j++) {
+ tmp2 = matrix_iget(SigZp , i , j) + (nrens - 1) * matrix_iget(cv_data->Rp, i, j);
+ matrix_iset( SigZp , i , j , tmp2 );
+ }
+ }
+
+
+ /* Invert the covariance matrix for the principal components */
+ int inv_ok = matrix_inv( SigZp );
+
+ /*Check if the inversion went ok */
+ if ( inv_ok != 0 )
+ util_abort("%s: inversion of covariance matrix for the principal components failed for subspace dimension p = %d\n - aborting \n",__func__,optP);
+
+
+
+
+ /*Compute W = Zp' * inv(SigZp) */
+ matrix_dgemm( W , Zp , SigZp , true , false , 1.0 , 0.0);
+
+ matrix_free( Zp );
+ matrix_free( SigZp );
+}
+
+
+
+
+void cv_enkf_initX(void * module_data ,
+ matrix_type * X ,
+ const matrix_type * A ,
+ const matrix_type * S ,
+ const matrix_type * R ,
+ const matrix_type * dObs ,
+ const matrix_type * E ,
+ const matrix_type * D,
+ rng_type * rng) {
+
+
+ cv_enkf_data_type * cv_data = cv_enkf_data_safe_cast( module_data );
+ printf("Running CV\n");
+ {
+ int optP;
+ int ens_size = matrix_get_columns( S );
+
+ /* Get the optimal number of principal components
+ where p is found minimizing the PRESS statistic */
+
+ {
+ matrix_type * workA = matrix_alloc_copy( A );
+ matrix_subtract_row_mean( workA );
+ optP = get_optimal_principal_components(cv_data , workA, rng);
+ printf("Optimal subspace dimension found %d\n",optP);
+ matrix_free( workA );
+ }
+
+ {
+ matrix_type * W = matrix_alloc(ens_size , optP);
+
+ /* Compute W = Z(1:p,:)' * inv(Z(1:p,:) * Z(1:p,:)' + (ens_size-1) * Rp(1:p,1:p))*/
+ getW_prin_comp( cv_data , W , optP);
+
+ /*Compute the actual X5 matrix: */
+ /*Compute X5 = W * Dp (The hard way) */
+ for( int i = 0; i < ens_size; i++) {
+ for( int j = 0; j < ens_size; j++) {
+ double tmp = 0.0;
+
+ for( int k = 0; k < optP; k++)
+ tmp += matrix_iget( W , i , k) * matrix_iget( cv_data->Dp , k , j);
+
+ matrix_iset(X , i , j ,tmp);
+ }
+ matrix_iadd( X , i , i , 1.0); /* X5 = I + X5 */
+ }
+
+ matrix_free( W );
+ }
+ }
+}
+
+
+
+void cv_enkf_complete_update( void * arg ) {
+ cv_enkf_data_type * cv_data = cv_enkf_data_safe_cast( arg );
+ {
+ matrix_free( cv_data->Z );
+ matrix_free( cv_data->Rp );
+ matrix_free( cv_data->Dp );
+
+ cv_data->Z = NULL;
+ cv_data->Rp = NULL;
+ cv_data->Dp = NULL;
+ }
+}
+
+
+
+bool cv_enkf_set_double( void * arg , const char * var_name , double value) {
+ cv_enkf_data_type * module_data = cv_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , ENKF_TRUNCATION_KEY_) == 0)
+ cv_enkf_set_truncation( module_data , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+
+bool cv_enkf_set_int( void * arg , const char * var_name , int value) {
+ cv_enkf_data_type * module_data = cv_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , ENKF_NCOMP_KEY_) == 0)
+ cv_enkf_set_subspace_dimension( module_data , value );
+ else if (strcmp( var_name , NFOLDS_KEY) == 0)
+ cv_enkf_set_nfolds( module_data , value);
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+
+bool cv_enkf_set_bool( void * arg , const char * var_name , bool value) {
+ cv_enkf_data_type * module_data = cv_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+ if (strcmp( var_name , CV_PEN_PRESS_KEY) == 0)
+ cv_enkf_set_pen_press( module_data , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+
+long cv_enkf_get_options( void * arg , long flag) {
+ cv_enkf_data_type * cv_data = cv_enkf_data_safe_cast( arg );
+ {
+ return cv_data->option_flags;
+ }
+}
+
+bool cv_enkf_has_var( const void * arg, const char * var_name) {
+ {
+ if (strcmp(var_name , ENKF_NCOMP_KEY_) == 0)
+ return true;
+ else if (strcmp(var_name , ENKF_TRUNCATION_KEY_) == 0)
+ return true;
+ else if (strcmp(var_name , NFOLDS_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , CV_PEN_PRESS_KEY) == 0)
+ return true;
+ else
+ return false;
+ }
+}
+
+double cv_enkf_get_double( const void * arg, const char * var_name) {
+ const cv_enkf_data_type * module_data = cv_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , ENKF_TRUNCATION_KEY_) == 0)
+ return module_data->truncation;
+ else
+ return -1;
+ }
+}
+
+int cv_enkf_get_int( const void * arg, const char * var_name) {
+ const cv_enkf_data_type * module_data = cv_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , ENKF_NCOMP_KEY_) == 0)
+ return module_data->subspace_dimension;
+ else if (strcmp(var_name , NFOLDS_KEY) == 0)
+ return module_data->nfolds;
+ else
+ return -1;
+ }
+}
+
+bool cv_enkf_get_bool( const void * arg, const char * var_name) {
+ const cv_enkf_data_type * module_data = cv_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , CV_PEN_PRESS_KEY) == 0)
+ return module_data->penalised_press;
+ else
+ return false;
+ }
+}
+
+
+#ifdef INTERNAL_LINK
+#define LINK_NAME CV_ENKF
+#else
+#define LINK_NAME EXTERNAL_MODULE_SYMBOL
+#endif
+
+
+analysis_table_type LINK_NAME = {
+ .name = "CV_ENKF",
+ .updateA = NULL,
+ .initX = cv_enkf_initX ,
+ .init_update = cv_enkf_init_update ,
+ .complete_update = cv_enkf_complete_update ,
+
+ .freef = cv_enkf_data_free,
+ .alloc = cv_enkf_data_alloc,
+
+ .set_int = cv_enkf_set_int ,
+ .set_double = cv_enkf_set_double ,
+ .set_bool = cv_enkf_set_bool ,
+ .set_string = NULL ,
+ .get_options = cv_enkf_get_options ,
+
+ .has_var = cv_enkf_has_var,
+ .get_int = cv_enkf_get_int,
+ .get_double = cv_enkf_get_double,
+ .get_bool = cv_enkf_get_bool,
+ .get_ptr = NULL,
+};
diff --git a/libres/lib/analysis/enkf_linalg.cpp b/libres/lib/analysis/enkf_linalg.cpp
new file mode 100644
index 00000000000..94ae5da4ea5
--- /dev/null
+++ b/libres/lib/analysis/enkf_linalg.cpp
@@ -0,0 +1,859 @@
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+void enkf_linalg_genX3(matrix_type * X3 , const matrix_type * W , const matrix_type * D , const double * eig) {
+ const int nrobs = matrix_get_rows( D );
+ const int nrens = matrix_get_columns( D );
+ const int nrmin = util_int_min( nrobs , nrens );
+ int i,j;
+ matrix_type * X1 = matrix_alloc(nrmin , nrobs);
+ matrix_type * X2 = matrix_alloc(nrmin , nrens);
+
+ /* X1 = (I + Lambda1)^(-1) * W'*/
+ for (i=0; i < nrmin; i++)
+ for (j=0; j < nrobs; j++)
+ matrix_iset(X1 , i , j , eig[i] * matrix_iget(W , j , i));
+
+ matrix_matmul(X2 , X1 , D); /* X2 = X1 * D (Eq. 14.31) */
+ matrix_matmul(X3 , W , X2); /* X3 = W * X2 = X1 * X2 (Eq. 14.31) */
+
+ matrix_free( X1 );
+ matrix_free( X2 );
+}
+
+
+void enkf_linalg_genX2(matrix_type * X2 , const matrix_type * S , const matrix_type * W , const double * eig) {
+ const int nrens = matrix_get_columns( S );
+ const int idim = matrix_get_rows( X2 );
+ matrix_dgemm(X2 , W , S , true , false , 1.0 , 0.0);
+ {
+ int i,j;
+ for (j=0; j < nrens; j++)
+ for (i=0; i < idim; i++)
+ matrix_imul(X2 , i,j , sqrt(eig[i]));
+ }
+}
+
+
+
+/**
+ This function calculates the svd of the input matrix S. The number
+ of significant singular values to retain can either be forced to a
+ fixed number, or a cutoff on small singular values can be
+ used. This behaviour is regulated by the @ncomp and @truncation
+ parameters:
+
+ ncomp > 0 , truncation < 0: Use ncomp parameters.
+ ncomp < 0 , truncation > 0: Truncate at level 'truncation'.
+
+ The singular values are returned in the inv_sig0 vector; the values
+ we retain are inverted and the remaining elements in are explicitly
+ set to zero.
+
+ The left-hand singular vectors are returned in the matrix
+ U0. Depending on the value of the flag @store_V0T the right hand
+ singular vectors are stored in the V0T matrix, or just
+ discarded. If you do not intend to use the right hand vectors at
+ all, i.e. store_V0T == DGESVD_NONE, the V0T matrix will not be
+ accessed.
+
+ The input S matrix should have been shifted to zero mean prior to
+ calling this function.
+*/
+
+
+
+/*This function is similar to enkf_linalg_svdS but it returns the eigen values without its inverse and also give the matrices truncated U VT and Sig0*/
+
+// Trunc.SVD(S) = U0 * Sig0 * V0T
+int enkf_linalg_svd_truncation(const matrix_type * S ,
+ double truncation ,
+ int ncomp ,
+ dgesvd_vector_enum store_V0T ,
+ double * sig0,
+ matrix_type * U0 ,
+ matrix_type * V0T) {
+
+ int num_significant = -1;
+ int nrows = matrix_get_rows(S);
+ int ncolumns= matrix_get_columns(S);
+
+ if (((truncation > 0) && (ncomp < 0)) ||
+ ((truncation < 0) && (ncomp > 0))) {
+
+ int num_singular_values = util_int_min( matrix_get_rows( S ) , matrix_get_columns( S ));
+ {
+ matrix_type * workS = matrix_alloc_copy( S );
+ matrix_dgesvd(DGESVD_MIN_RETURN , store_V0T , workS , sig0 , U0 , V0T);
+ matrix_free( workS );
+ }
+ int i;
+
+ if (ncomp > 0)
+ num_significant = ncomp;
+ else {
+ double total_sigma2 = 0;
+ for (i=0; i < num_singular_values; i++)
+ total_sigma2 += sig0[i];
+
+ /*
+ Determine the number of singular values by enforcing that
+ less than a fraction @truncation of the total variance be
+ accounted for.
+ */
+ num_significant = 0;
+ {
+ double running_sigma2 = 0;
+ for (i=0; i < num_singular_values; i++) {
+ if (running_sigma2 / total_sigma2 < truncation) { /* Include one more singular value ? */
+ num_significant++;
+ running_sigma2 += sig0[i];
+ } else
+ break;
+ }
+ }
+ }
+ if (num_significant > 0) {
+ matrix_resize(U0 , nrows , num_significant , true);
+ matrix_resize(V0T , num_significant , ncolumns , true);
+ } else
+ util_abort("%s: zero significant singular values\n",__func__);
+ }
+ else
+ util_abort("%s: truncation:%g ncomp:%d - invalid ambigous input.\n",__func__ , truncation , ncomp );
+
+ return num_significant;
+}
+
+
+static int enkf_linalg_num_significant(int num_singular_values , const double * sig0 , double truncation ) {
+ int num_significant = 0;
+ double total_sigma2 = 0;
+ for (int i=0; i < num_singular_values; i++)
+ total_sigma2 += sig0[i] * sig0[i];
+
+ /*
+ Determine the number of singular values by enforcing that
+ less than a fraction @truncation of the total variance be
+ accounted for.
+ */
+ {
+ double running_sigma2 = 0;
+ for (int i=0; i < num_singular_values; i++) {
+ if (running_sigma2 / total_sigma2 < truncation) { /* Include one more singular value ? */
+ num_significant++;
+ running_sigma2 += sig0[i] * sig0[i];
+ } else
+ break;
+ }
+ }
+
+ return num_significant;
+}
+
+
+int enkf_linalg_svdS(const matrix_type * S ,
+ double truncation ,
+ int ncomp ,
+ dgesvd_vector_enum store_V0T ,
+ double * inv_sig0,
+ matrix_type * U0 ,
+ matrix_type * V0T) {
+
+ double * sig0 = inv_sig0;
+ int num_significant = 0;
+
+
+ if (((truncation > 0) && (ncomp < 0)) ||
+ ((truncation < 0) && (ncomp > 0))) {
+ int num_singular_values = util_int_min( matrix_get_rows( S ) , matrix_get_columns( S ));
+ {
+ matrix_type * workS = matrix_alloc_copy( S );
+ matrix_dgesvd(DGESVD_MIN_RETURN , store_V0T , workS , sig0 , U0 , V0T);
+ matrix_free( workS );
+ }
+
+ if (ncomp > 0)
+ num_significant = ncomp;
+ else
+ num_significant = enkf_linalg_num_significant( num_singular_values , sig0 , truncation );
+
+ {
+ int i;
+/* Inverting the significant singular values */
+ for (i = 0; i < num_significant; i++)
+ inv_sig0[i] = 1.0 / sig0[i];
+
+/* Explicitly setting the insignificant singular values to zero. */
+ for (i=num_significant; i < num_singular_values; i++)
+ inv_sig0[i] = 0;
+ }
+ } else
+
+ util_abort("%s: truncation:%g ncomp:%d - invalid ambigous input.\n",__func__ , truncation , ncomp );
+
+ return num_significant;
+}
+
+
+int enkf_linalg_num_PC(const matrix_type * S , double truncation ) {
+ int num_singular_values = util_int_min( matrix_get_rows( S ) , matrix_get_columns( S ));
+ int num_significant;
+ double * sig0 = (double*)util_calloc( num_singular_values , sizeof * sig0);
+
+ {
+ matrix_type * workS = matrix_alloc_copy( S );
+ matrix_dgesvd(DGESVD_NONE , DGESVD_NONE , workS , sig0 , NULL , NULL);
+ matrix_free( workS );
+ }
+
+ num_significant = enkf_linalg_num_significant( num_singular_values , sig0 , truncation );
+ free( sig0 );
+ return num_significant;
+}
+
+
+
+/*
+****************************************************************************************************
+ Routine computes X1 and eig corresponding to Eqs 14.54-14.55
+ Geir Evensen
+*/
+void enkf_linalg_lowrankE(const matrix_type * S , /* (nrobs x nrens) */
+ const matrix_type * E , /* (nrobs x nrens) */
+ matrix_type * W , /* (nrobs x nrmin) Corresponding to X1 from Eqs. 14.54-14.55 */
+ double * eig , /* (nrmin) Corresponding to 1 / (1 + Lambda1^2) (14.54) */
+ double truncation ,
+ int ncomp) {
+
+
+ const int nrobs = matrix_get_rows( S );
+ const int nrens = matrix_get_columns( S );
+ const int nrmin = util_int_min( nrobs , nrens );
+
+ matrix_type * U0 = matrix_alloc( nrobs , nrmin );
+ double * inv_sig0 = (double*)util_calloc( nrmin , sizeof * inv_sig0);
+ matrix_type * X0 = matrix_alloc( nrmin , nrens );
+
+
+ matrix_type * U1 = matrix_alloc( nrmin , nrmin );
+ double * sig1 = (double*)util_calloc( nrmin , sizeof * sig1);
+
+ int i ,j;
+
+
+/* Compute SVD of S=HA` -> U0, invsig0=sig0^(-1) */
+ enkf_linalg_svdS(S , truncation , ncomp , DGESVD_NONE , inv_sig0, U0 , NULL);
+
+/* X0(nrmin x nrens) = Sigma0^(+) * U0'* E (14.51) */
+ matrix_dgemm(X0 , U0 , E , true , false , 1.0 , 0.0); /* X0 = U0^T * E (14.51) */
+
+
+/* Multiply X0 with sig0^(-1) from left X0 = S^(-1) * X0 */
+ for (j=0; j < matrix_get_columns( X0 ) ; j++)
+ for (i=0; i < matrix_get_rows( X0 ); i++)
+ matrix_imul(X0 , i , j , inv_sig0[i]);
+
+
+/* Compute SVD of X0-> U1*eig*V1 14.52 */
+ matrix_dgesvd(DGESVD_MIN_RETURN , DGESVD_NONE, X0 , sig1, U1 , NULL);
+
+ /* Lambda1 = 1/(I + Lambda^2) in 14.56 */
+ for (i=0; i < nrmin; i++)
+ eig[i] = 1.0 / (1.0 + sig1[i]*sig1[i]);
+
+
+/* Compute sig0^+ U1 (14:55) */
+ for (j=0; j < nrmin; j++)
+ for (i=0; i < nrmin; i++)
+ matrix_imul(U1 , i , j , inv_sig0[i]);
+
+
+/* Compute X1 = W = U0 * (U1=sig0^+ U1) = U0 * Sigma0^(+') * U1 (14:55) */
+ matrix_matmul(W , U0 , U1);
+
+
+ matrix_free( X0 );
+ matrix_free( U0 );
+ free( inv_sig0 );
+
+ matrix_free( U1 );
+ free( sig1 );
+
+}
+
+
+
+
+void enkf_linalg_Cee(matrix_type * B, int nrens , const matrix_type * R , const matrix_type * U0 , const double * inv_sig0) {
+ const int nrmin = matrix_get_rows( B );
+ {
+ matrix_type * X0 = matrix_alloc( nrmin , matrix_get_rows( R ));
+ matrix_dgemm(X0 , U0 , R , true , false , 1.0 , 0.0); /* X0 = U0^T * R */
+ matrix_dgemm(B , X0 , U0 , false , false , 1.0 , 0.0); /* B = X0 * U0 */
+ matrix_free( X0 );
+ }
+
+ {
+ int i ,j;
+
+ /* Funny code ??
+ Multiply B with S^(-1)from left and right
+ BHat = S^(-1) * B * S^(-1)
+ */
+ for (j=0; j < matrix_get_columns( B ) ; j++)
+ for (i=0; i < matrix_get_rows( B ); i++)
+ matrix_imul(B , i , j , inv_sig0[i]);
+
+ for (j=0; j < matrix_get_columns( B ) ; j++)
+ for (i=0; i < matrix_get_rows( B ); i++)
+ matrix_imul(B , i , j , inv_sig0[j]);
+ }
+
+ matrix_scale(B , nrens - 1.0);
+}
+
+
+
+
+void enkf_linalg_lowrankCinv__(const matrix_type * S ,
+ const matrix_type * R ,
+ matrix_type * V0T ,
+ matrix_type * Z,
+ double * eig ,
+ matrix_type * U0,
+ double truncation,
+ int ncomp) {
+
+ const int nrobs = matrix_get_rows( S );
+ const int nrens = matrix_get_columns( S );
+ const int nrmin = util_int_min( nrobs , nrens );
+
+ double * inv_sig0 = (double*)util_calloc( nrmin , sizeof * inv_sig0);
+
+ if (V0T != NULL)
+ enkf_linalg_svdS(S , truncation , ncomp , DGESVD_MIN_RETURN , inv_sig0 , U0 , V0T );
+ else
+ enkf_linalg_svdS(S , truncation , ncomp , DGESVD_NONE , inv_sig0, U0 , NULL);
+
+ {
+ matrix_type * B = matrix_alloc( nrmin , nrmin );
+ enkf_linalg_Cee( B , nrens , R , U0 , inv_sig0); /* B = Xo = (N-1) * Sigma0^(+) * U0'* Cee * U0 * Sigma0^(+') (14.26)*/
+ matrix_dgesvd(DGESVD_MIN_RETURN , DGESVD_NONE, B , eig, Z , NULL);
+ matrix_free( B );
+ }
+
+ {
+ int i,j;
+ /* Lambda1 = (I + Lambda)^(-1) */
+
+ for (i=0; i < nrmin; i++)
+ eig[i] = 1.0 / (1 + eig[i]);
+
+ for (j=0; j < nrmin; j++)
+ for (i=0; i < nrmin; i++)
+ matrix_imul(Z , i , j , inv_sig0[i]); /* Z2 = Sigma0^(+) * Z; */
+ }
+ free( inv_sig0 );
+}
+
+
+void enkf_linalg_lowrankCinv(const matrix_type * S ,
+ const matrix_type * R ,
+ matrix_type * W , /* Corresponding to X1 from Eq. 14.29 */
+ double * eig , /* Corresponding to 1 / (1 + Lambda_1) (14.29) */
+ double truncation ,
+ int ncomp) {
+
+ const int nrobs = matrix_get_rows( S );
+ const int nrens = matrix_get_columns( S );
+ const int nrmin = util_int_min( nrobs , nrens );
+
+ matrix_type * U0 = matrix_alloc( nrobs , nrmin );
+ matrix_type * Z = matrix_alloc( nrmin , nrmin );
+
+ enkf_linalg_lowrankCinv__( S , R , NULL , Z , eig , U0 , truncation , ncomp);
+ matrix_matmul(W , U0 , Z); /* X1 = W = U0 * Z2 = U0 * Sigma0^(+') * Z */
+
+ matrix_free( U0 );
+ matrix_free( Z );
+}
+
+
+void enkf_linalg_meanX5(const matrix_type * S ,
+ const matrix_type * W ,
+ const double * eig ,
+ const matrix_type * dObs,
+ matrix_type * X5) {
+
+
+ const int nrens = matrix_get_columns( S );
+ const int nrobs = matrix_get_rows( S );
+ const int nrmin = util_int_min( nrobs , nrens );
+ double * work = (double*)util_calloc( (2 * nrmin + nrobs + nrens) , sizeof * work );
+ matrix_type * innov = enkf_linalg_alloc_innov( dObs , S );
+ {
+ double * y1 = &work[0];
+ double * y2 = &work[nrmin];
+ double * y3 = &work[2*nrmin];
+ double * y4 = &work[2*nrmin + nrobs];
+
+ if (nrobs == 1) {
+ /* Is this special casing necessary ??? */
+ y1[0] = matrix_iget(W , 0,0) * matrix_iget( innov , 0 , 0);
+ y2[0] = eig[0] * y1[0];
+ y3[0] = matrix_iget(W , 0, 0) *y2[0];
+ for (int iens = 0; iens < nrens; iens++)
+ y4[iens] = y3[0] * matrix_iget(S , 0, iens);
+ } else {
+ matrix_dgemv(W , matrix_get_data( innov ) , y1 , true , 1.0, 0.0); /* y1 = Trans(W) * innov */
+ for (int i= 0; i < nrmin; i++)
+ y2[i] = eig[i] * y1[i]; /* y2 = eig * y1 */
+ matrix_dgemv(W , y2 , y3 , false , 1.0 , 0.0); /* y3 = W * y2; */
+ matrix_dgemv(S , y3 , y4 , true , 1.0 , 0.0); /* y4 = Trans(S) * y3 */
+ }
+
+ for (int iens = 0; iens < nrens; iens++)
+ matrix_set_column(X5 , y4 , iens );
+
+ matrix_shift(X5 , 1.0/nrens);
+ }
+ free( work );
+ matrix_free( innov );
+}
+
+
+
+void enkf_linalg_X5sqrt(matrix_type * X2 , matrix_type * X5 , const matrix_type * randrot, int nrobs) {
+ const int nrens = matrix_get_columns( X5 );
+ const int nrmin = util_int_min( nrobs , nrens );
+ matrix_type * VT = matrix_alloc( nrens , nrens );
+ double * sig = (double*)util_calloc( nrmin , sizeof * sig );
+ double * isig = (double*)util_calloc( nrmin , sizeof * sig );
+
+ matrix_dgesvd(DGESVD_NONE , DGESVD_ALL , X2 , sig , NULL , VT);
+ {
+ matrix_type * X3 = matrix_alloc( nrens , nrens );
+ matrix_type * X33 = matrix_alloc( nrens , nrens );
+ matrix_type * X4 = matrix_alloc( nrens , nrens );
+ matrix_type * IenN = matrix_alloc( nrens , nrens );
+ int i,j;
+ for (i = 0; i < nrmin; i++)
+ isig[i] = sqrt( util_double_max( 1.0 - sig[i]*sig[i] ,0.0));
+
+ for (j = 0; j < nrens; j++)
+ for (i = 0; i < nrens; i++)
+ matrix_iset(X3 , i , j , matrix_iget(VT , j , i));
+
+ for (j=0; j< nrmin; j++)
+ matrix_scale_column(X3 , j , isig[j]);
+
+ matrix_dgemm(X33 , X3 , VT , false , false , 1.0 , 0.0); /* X33 = X3 * VT */
+ if (randrot != NULL)
+ matrix_dgemm(X4 , X33 , randrot , false, false , 1.0 , 0.0); /* X4 = X33 * Randrot */
+ else
+ matrix_assign(X4 , X33);
+
+ matrix_set(IenN , -1.0/ nrens);
+ for (i = 0; i < nrens; i++)
+ matrix_iadd(IenN , i , i , 1.0);
+
+ matrix_dgemm(X5 , IenN , X4 , false , false , 1.0 , 1.0); /* X5 = IenN * X4 + X5 */
+
+ matrix_free( X3 );
+ matrix_free( X33 );
+ matrix_free( X4 );
+ matrix_free( IenN );
+ }
+
+ free(sig);
+ free(isig);
+ matrix_free( VT );
+}
+
+
+matrix_type * enkf_linalg_alloc_innov( const matrix_type * dObs , const matrix_type * S) {
+ matrix_type * innov = matrix_alloc_copy( dObs );
+
+ for (int iobs =0; iobs < matrix_get_row_sum( dObs , iobs); iobs++)
+ matrix_isub( innov , iobs , 0 , matrix_get_row_sum( S , iobs ));
+
+ return innov;
+}
+
+
+void enkf_linalg_init_stdX( matrix_type * X , const matrix_type * S , const matrix_type * D ,
+ const matrix_type * W , const double * eig , bool bootstrap) {
+
+ int nrobs = matrix_get_rows( W );
+ int ens_size = matrix_get_rows( X );
+
+ matrix_type * X3 = matrix_alloc(nrobs , ens_size);
+ enkf_linalg_genX3(X3 , W , D , eig ); /* X2 = diag(eig) * W' * D (Eq. 14.31, Evensen (2007)) */
+ /* X3 = W * X2 = X1 * X2 (Eq. 14.31, Evensen (2007)) */
+
+ matrix_dgemm( X , S , X3 , true , false , 1.0 , 0.0); /* X = S' * X3 */
+ if (!bootstrap) {
+ for (int i = 0; i < ens_size ; i++)
+ matrix_iadd( X , i , i , 1.0); /*X = I + X */
+ }
+
+ matrix_free( X3 );
+}
+
+
+
+void enkf_linalg_init_sqrtX(matrix_type * X5 ,
+ const matrix_type * S ,
+ const matrix_type * randrot ,
+ const matrix_type * innov ,
+ const matrix_type * W ,
+ const double * eig ,
+ bool bootstrap) {
+
+ const int nrobs = matrix_get_rows( S );
+ const int nrens = matrix_get_columns( S );
+ const int nrmin = util_int_min( nrobs , nrens );
+
+ matrix_type * X2 = matrix_alloc(nrmin , nrens);
+
+ if (bootstrap)
+ util_exit("%s: Sorry bootstrap support not fully implemented for SQRT scheme\n",__func__);
+
+ enkf_linalg_meanX5( S , W , eig , innov , X5 );
+ enkf_linalg_genX2(X2 , S , W , eig);
+ enkf_linalg_X5sqrt(X2 , X5 , randrot , nrobs);
+
+ matrix_free( X2 );
+}
+
+/*****************************************************************/
+
+
+
+
+
+/**
+ This routine generates a real orthogonal random matrix.
+ The algorithm is the one by
+ Francesco Mezzadri (2007), How to generate random matrices from the classical
+ compact groups, Notices of the AMS, Vol. 54, pp 592-604.
+ 1. First a matrix with independent random normal numbers are simulated.
+ 2. Then the QR decomposition is computed, and Q will then be a random orthogonal matrix.
+ 3. The diagonal elements of R are extracted and we construct the diagonal matrix X(j,j)=R(j,j)/|R(j,j)|
+ 4. An updated Q'=Q X is computed, and this is now a random orthogonal matrix with a Haar measure.
+
+ The implementation is a plain reimplementation/copy of the old m_randrot.f90 function.
+*/
+
+
+
+/**
+ NB: This should rather use the implementation in m_mean_preserving_rotation.f90.
+*/
+
+void enkf_linalg_set_randrot( matrix_type * Q , rng_type * rng) {
+ int ens_size = matrix_get_rows( Q );
+ double * tau = (double*)util_calloc( ens_size , sizeof * tau );
+ int * sign = (int*)util_calloc( ens_size , sizeof * sign);
+
+ for (int i = 0; i < ens_size; i++)
+ for (int j = 0; j < ens_size; j++)
+ matrix_iset(Q , i , j , rng_std_normal( rng ));
+
+ matrix_dgeqrf( Q , tau ); /* QR factorization */
+ for (int i=0; i < ens_size; i++) {
+ double Qii = matrix_iget( Q , i,i);
+ sign[i] = (Qii>0) ? 1 : -1;
+ }
+
+ matrix_dorgqr( Q , tau , ens_size );
+ for (int i = 0; i < ens_size; i++) {
+ if (sign[i] < 0)
+ matrix_scale_column( Q , i , -1 );
+ }
+
+ free( sign );
+ free( tau );
+}
+
+
+
+
+/**
+ Generates the mean preserving random rotation for the EnKF SQRT algorithm
+ using the algorithm from Sakov 2006-07. I.e, generate rotation Up such that
+ Up*Up^T=I and Up*1=1 (all rows have sum = 1) see eq 17.
+ From eq 18, Up=B * Upb * B^T
+ B is a random orthonormal basis with the elements in the first column equals 1/sqrt(nrens)
+
+ Upb = | 1 0 |
+ | 0 U |
+
+ where U is an arbitrary orthonormal matrix of dim nrens-1 x nrens-1 (eq. 19)
+*/
+
+matrix_type * enkf_linalg_alloc_mp_randrot(int ens_size , rng_type * rng) {
+ matrix_type * Up = matrix_alloc( ens_size , ens_size ); /* The return value. */
+ {
+ matrix_type * B = matrix_alloc( ens_size , ens_size );
+ matrix_type * Upb = matrix_alloc( ens_size , ens_size );
+ matrix_type * U = matrix_alloc_shared(Upb , 1 , 1 , ens_size - 1, ens_size - 1);
+
+
+ {
+ int k,j;
+ matrix_type * R = matrix_alloc( ens_size , ens_size );
+ matrix_random_init( B , rng); /* B is filled up with U(0,1) numbers. */
+ matrix_set_const_column( B , 1.0 / sqrt( ens_size ) , 0 );
+
+ /* modified_gram_schmidt is used to create the orthonormal basis in B.*/
+ for (k=0; k < ens_size; k++) {
+ double Rkk = sqrt( matrix_column_column_dot_product( B , k , B , k));
+ matrix_iset(R , k , k , Rkk);
+ matrix_scale_column(B , k , 1.0/Rkk);
+ for (j=k+1; j < ens_size; j++) {
+ double Rkj = matrix_column_column_dot_product(B , k , B , j);
+ matrix_iset(R , k , j , Rkj);
+ {
+ int i;
+ for (i=0; i < ens_size; i++) {
+ double Bij = matrix_iget(B , i , j);
+ double Bik = matrix_iget(B , i , k);
+ matrix_iset(B , i , j , Bij - Bik * Rkj);
+ }
+ }
+ }
+ }
+ matrix_free( R );
+ }
+
+ enkf_linalg_set_randrot( U , rng );
+ matrix_iset( Upb , 0 , 0 , 1);
+
+
+ {
+ matrix_type * Q = matrix_alloc( ens_size , ens_size );
+ matrix_dgemm( Q , B , Upb , false , false , 1, 0); /* Q = B * Ubp */
+ matrix_dgemm( Up , Q , B , false , true , 1, 0); /* Up = Q * T(B) */
+ matrix_free( Q );
+ }
+
+ matrix_free( B );
+ matrix_free( Upb );
+ matrix_free( U );
+ }
+
+ return Up;
+}
+
+
+/*****************************************************************/
+
+/**
+ Checking that the sum through one row in the X matrix equals
+ @target_sum. @target_sum will be 1 normally, and zero if we are doing
+ bootstrap.
+*/
+
+void enkf_linalg_checkX(const matrix_type * X , bool bootstrap) {
+ matrix_assert_finite( X );
+ {
+ int target_sum;
+ if (bootstrap)
+ target_sum = 0;
+ else
+ target_sum = 1;
+
+ for (int icol = 0; icol < matrix_get_columns( X ); icol++) {
+ double col_sum = matrix_get_column_sum(X , icol);
+ if (std::abs(col_sum - target_sum) > 0.0001)
+ util_abort("%s: something is seriously broken. col:%d col_sum = %g != %g - ABORTING\n",__func__ , icol , col_sum , target_sum);
+ }
+ }
+}
+
+
+/*****************************************************************/
+
+int enkf_linalg_get_PC( const matrix_type * S0,
+ const matrix_type * dObs ,
+ double truncation,
+ int ncomp,
+ matrix_type * PC,
+ matrix_type * PC_obs,
+ double_vector_type * singular_values) {
+
+ const int nrobs = matrix_get_rows( S0 );
+ const int nrens = matrix_get_columns( S0 );
+ const int nrmin = util_int_min( nrobs , nrens );
+
+ matrix_type * U0 = matrix_alloc( nrobs , nrens );
+ matrix_type * S = matrix_alloc_copy( S0 );
+ double * inv_sig0;
+ int num_PC;
+
+ double_vector_iset( singular_values , nrmin - 1 , 0 );
+ matrix_subtract_row_mean( S );
+ ncomp = util_int_min( ncomp , nrmin );
+ inv_sig0 = double_vector_get_ptr( singular_values );
+ {
+ matrix_type * S_mean = matrix_alloc( nrobs , 1 );
+ num_PC = enkf_linalg_svdS(S , truncation , ncomp, DGESVD_NONE , inv_sig0 , U0 , NULL);
+
+ matrix_assign( S , S0); // The svd routine will overwrite S - we therefor must pick it up again from S0.
+ matrix_subtract_and_store_row_mean( S , S_mean);
+
+ /* Multiply with inverted singular values. */
+ matrix_resize( U0 , nrobs , num_PC , true);
+ for (int i=0; i < num_PC; i++)
+ matrix_imul_col( U0 , i , inv_sig0[i] );
+
+ /* The simulated components / projections */
+ {
+ matrix_resize( PC , num_PC , nrens , false );
+ matrix_dgemm( PC , U0 , S , true , false , 1.0 , 0.0 );
+ }
+
+
+ /* The observer projections. */
+ {
+ matrix_scale( S_mean , -1.0);
+ matrix_inplace_add_column( S_mean , dObs , 0 , 0 );
+ matrix_resize( PC_obs , num_PC , 1 , false );
+ matrix_dgemm( PC_obs , U0 , S_mean , true , false , 1.0 , 0.0 );
+ }
+
+ for (int i=0; i < double_vector_size( singular_values ); i++)
+ inv_sig0[i] = 1.0 / inv_sig0[i];
+
+ matrix_free( S_mean );
+ }
+ matrix_free( S );
+ matrix_free( U0 );
+
+ return num_PC;
+}
+
+
+
+void enkf_linalg_rml_enkfX1(matrix_type *X1, matrix_type *Udr, matrix_type *D, matrix_type *R)
+{
+ /*
+ This routine computes X1 for RML_EnKF module as X1 = Ud(T)*Cd(-1/2)*D -- D= (dk-do)
+ here the negative sign cancels with one needed in X3 matrix computation
+ */
+ matrix_type * tmp = matrix_alloc(matrix_get_columns(Udr),matrix_get_rows(R));
+
+ matrix_matmul_with_transpose( tmp, Udr, R, true, false);
+ matrix_matmul( X1 , tmp, D);
+
+ matrix_free(tmp);
+}
+
+
+
+void enkf_linalg_rml_enkfX2(matrix_type *X2 , double *Wdr , matrix_type * X1 , double a , int nsign)
+{
+ /*
+ This routine computes X2 for RML_EnKF module as X2 = ((a*Ipd)+Wd^2)^-1 * X1
+ Since a+Ipd & Wd are diagonal in nature the computation is reduced to array operations
+ */
+
+ for (int i=0; i< nsign ; i++) {
+ double scale_factor = 1 / (a + (Wdr[i]*Wdr[i]));
+ matrix_scale_row(X1 , i , scale_factor);
+ }
+ matrix_assign(X2,X1);
+
+}
+
+
+void enkf_linalg_rml_enkfX3(matrix_type *X3, matrix_type *VdTr, double *Wdr, matrix_type *X2, int nsign)
+{
+ /*
+ This routine computes X3 for RML_EnKF module as X3 = Vd *Wd*X2
+ */
+
+ printf("\nWd: ");
+ matrix_type *tmp = matrix_alloc_copy(VdTr);
+ for (int i=0; i< nsign ; i++) {
+ printf("%5.2f ", Wdr[i]);
+ matrix_scale_row(tmp, i, Wdr[i]);
+ }
+ printf("\n\n");
+
+ matrix_matmul_with_transpose( X3 , tmp , X2 , true, false);
+ matrix_free(tmp);
+}
+
+
+
+
+double enkf_linalg_data_mismatch(matrix_type *D , matrix_type *R , matrix_type *Sk)
+{
+ matrix_type * tmp = matrix_alloc (matrix_get_columns(D), matrix_get_columns(R));
+ double mismatch;
+ matrix_matmul_with_transpose(tmp, D, R,true, false); // tmp = D' * R, i.e. N-by-p
+ matrix_matmul(Sk, tmp, D); // Sk = D' * R * D
+ // Calculate the mismatch
+ mismatch = matrix_trace(Sk)/(matrix_get_columns(D));
+
+ return mismatch;
+}
+
+// Cd = SampCov(E) (including (N-1) normalization)
+void enkf_linalg_Covariance(matrix_type *Cd, const matrix_type *E, double nsc ,int nrobs)
+{
+ matrix_matmul_with_transpose(Cd, E, E,false,true);
+ for (int i=0; i< nrobs; i++) {
+ for (int j=0;j< nrobs; j++) {
+ if (i!=j)
+ matrix_iset(Cd, i, j, 0.0);
+ }
+ }
+ nsc = nsc*nsc;
+ matrix_scale(Cd,nsc);
+}
+
+
+
+// Scale columns (not rows!) of Um by entries in diagonal Wm
+void enkf_linalg_rml_enkfAm(matrix_type * Um, const double * Wm,int nsign1){
+ for (int i=0; i< nsign1 ; i++) {
+ double sc = 1 / Wm[i];
+ matrix_scale_column(Um, i, sc);
+ }
+}
+
+
+
+
+void enkf_linalg_rml_enkfX7(matrix_type * X7, matrix_type * VdT, double * Wdr, double a, matrix_type * X6){
+
+ int nsign = matrix_get_rows(VdT);
+ int ens_size = matrix_get_columns(VdT);
+ matrix_type *tmp1 = matrix_alloc_copy(VdT);
+ matrix_type *tmp2 = matrix_alloc(ens_size,ens_size);
+
+
+ for (int i=0; i < nsign ; i++) {
+ double scale_factor = 1 / ( a + (Wdr[i]*Wdr[i]));
+ matrix_scale_row( tmp1 , i , scale_factor);
+ }
+
+
+ matrix_matmul_with_transpose(tmp2, tmp1, VdT, true, false);
+ matrix_matmul(X7, tmp2, X6);
+ matrix_free(tmp1);
+ matrix_free(tmp2);
+}
+
+
diff --git a/libres/lib/analysis/fwd_step_enkf.cpp b/libres/lib/analysis/fwd_step_enkf.cpp
new file mode 100644
index 00000000000..417c54c16a5
--- /dev/null
+++ b/libres/lib/analysis/fwd_step_enkf.cpp
@@ -0,0 +1,545 @@
+/*
+ copyright (C) 2011 Equinor ASA, Norway.
+
+ The file 'fwd_step_enkf.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+#include
+#include
+#include
+
+#if defined(_OPENMP)
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#define FWD_STEP_ENKF_TYPE_ID 765524
+
+#define DEFAULT_NFOLDS 5
+#define DEFAULT_R2_LIMIT 0.99
+#define DEFAULT_NUM_THREADS -1
+
+#define NFOLDS_KEY "CV_NFOLDS"
+#define R2_LIMIT_KEY "FWD_STEP_R2_LIMIT"
+#define DEFAULT_VERBOSE false
+#define VERBOSE_KEY "VERBOSE"
+#define NUM_THREADS_KEY "NUM_THREADS"
+#define LOG_FILE_KEY "LOG_FILE"
+#define CLEAR_LOG_KEY "CLEAR_LOG"
+
+
+struct fwd_step_enkf_data_struct {
+ UTIL_TYPE_ID_DECLARATION;
+ stepwise_type * stepwise_data;
+ int nfolds;
+ long option_flags;
+ double r2_limit;
+ bool verbose;
+ int num_threads;
+ fwd_step_log_type * fwd_step_log;
+};
+
+
+static UTIL_SAFE_CAST_FUNCTION_CONST( fwd_step_enkf_data , FWD_STEP_ENKF_TYPE_ID )
+static UTIL_SAFE_CAST_FUNCTION( fwd_step_enkf_data , FWD_STEP_ENKF_TYPE_ID )
+
+
+void fwd_step_enkf_set_nfolds( fwd_step_enkf_data_type * data , int nfolds ) {
+ data->nfolds = nfolds;
+}
+
+void fwd_step_enkf_set_r2_limit( fwd_step_enkf_data_type * data , double limit ) {
+ data->r2_limit = limit;
+}
+
+void fwd_step_enkf_set_verbose( fwd_step_enkf_data_type * data , bool verbose ) {
+ data->verbose = verbose;
+}
+
+void fwd_step_enkf_set_num_threads( fwd_step_enkf_data_type * data , int threads ) {
+ data->num_threads = threads;
+}
+
+
+void * fwd_step_enkf_data_alloc( ) {
+ fwd_step_enkf_data_type * data = (fwd_step_enkf_data_type*)util_malloc( sizeof * data );
+ UTIL_TYPE_ID_INIT( data , FWD_STEP_ENKF_TYPE_ID );
+
+ data->stepwise_data = NULL;
+ data->nfolds = DEFAULT_NFOLDS;
+ data->r2_limit = DEFAULT_R2_LIMIT;
+ data->option_flags = ANALYSIS_NEED_ED + ANALYSIS_UPDATE_A + ANALYSIS_SCALE_DATA;
+ data->verbose = DEFAULT_VERBOSE;
+ data->num_threads = DEFAULT_NUM_THREADS;
+ data->fwd_step_log = fwd_step_log_alloc();
+ return data;
+}
+
+void fwd_step_enkf_data_free( void * arg ) {
+ fwd_step_enkf_data_type * fwd_step_data = fwd_step_enkf_data_safe_cast( arg );
+ {
+
+ if (fwd_step_data != NULL) {
+ if (fwd_step_data->stepwise_data != NULL) {
+ stepwise_free( fwd_step_data->stepwise_data );
+ }
+ }
+ }
+ fwd_step_log_free( fwd_step_data->fwd_step_log );
+ free( fwd_step_data );
+}
+
+
+//**********************************************
+// Log-file related stuff
+//**********************************************
+
+
+static void fwd_step_enkf_write_log_header( fwd_step_enkf_data_type * fwd_step_data, const char * ministep_name, const int nx, const int nd, const int ens_size) {
+ const char * format = "%-25s%-25s%-25s%-25s\n";
+ const char * column1 = "Parameter(ActiveIndex)";
+ const char * column2 = "GlobalIndex";
+ const char * column3 = "NumAttached";
+ const char * column4 = "AttachedObs(ActiveIndex)[Percentage sensitivity]";
+ int nfolds = fwd_step_data->nfolds;
+ int num_threads = fwd_step_data->num_threads;
+ double r2_limit = fwd_step_data->r2_limit;
+
+ if (fwd_step_log_is_open( fwd_step_data->fwd_step_log )) {
+ fwd_step_log_line(fwd_step_data->fwd_step_log, "===============================================================================================================================\n");
+ fwd_step_log_line(fwd_step_data->fwd_step_log, "Ministep : %s\n",ministep_name);
+ fwd_step_log_line(fwd_step_data->fwd_step_log, "Total number of parameters : %d\n",nx);
+ fwd_step_log_line(fwd_step_data->fwd_step_log, "Total number of observations: %d\n",nd);
+ fwd_step_log_line(fwd_step_data->fwd_step_log, "Number of ensembles : %d\n",ens_size);
+ fwd_step_log_line(fwd_step_data->fwd_step_log, "CV folds : %d\n",nfolds);
+ fwd_step_log_line(fwd_step_data->fwd_step_log, "Relative R2 tolerance : %f\n",r2_limit);
+ fwd_step_log_line(fwd_step_data->fwd_step_log, "===============================================================================================================================\n");
+ fwd_step_log_line(fwd_step_data->fwd_step_log, format, column1, column2, column3, column4);
+ fwd_step_log_line(fwd_step_data->fwd_step_log, "===============================================================================================================================\n");
+ }
+
+ printf("===============================================================================================================================\n");
+ printf("Ministep : %s\n",ministep_name);
+ printf("Total number of parameters : %d\n",nx);
+ printf("Total number of observations: %d\n",nd);
+ printf("Number of ensembles : %d\n",ens_size);
+ printf("CV folds : %d\n",nfolds);
+ printf("Number of threads : %d\n",num_threads);
+ printf("Relative R2 tolerance : %f\n",r2_limit);
+ printf("===============================================================================================================================\n");
+ printf(format, column1, column2, column3, column4);
+ printf("===============================================================================================================================\n");
+}
+
+static void fwd_step_enkf_write_iter_info( fwd_step_enkf_data_type * data , stepwise_type * stepwise, const char* key, const int data_active_index, const int global_index, const module_info_type * module_info ) {
+
+ const char * format = "%-25s%-25d%-25d";
+ int n_active = stepwise_get_n_active( stepwise);
+ bool_vector_type * active_set = stepwise_get_active_set(stepwise);
+ bool has_log = fwd_step_log_is_open( data->fwd_step_log );
+ module_obs_block_vector_type * module_obs_block_vector = module_info_get_obs_block_vector(module_info);
+ char * loc_key = (char*)util_alloc_string_copy(key);
+ char * data_active_index_str = (char*)util_alloc_sprintf( "(%d)" , data_active_index );
+ char * cat = util_strcat_realloc(loc_key , data_active_index_str );
+ if (has_log)
+ fwd_step_log_line( data->fwd_step_log , format, cat, global_index, n_active);
+
+ printf(format, cat, global_index,n_active);
+
+ const double sum_beta = stepwise_get_sum_beta(stepwise);
+ int obs_active_index = 0;
+ stringlist_type * obs_list = stringlist_alloc_new( );
+ double_vector_type * r_list = double_vector_alloc(0, 0);
+
+ const char * format1 = "%s(%d)[%.1f] ";
+ for (int ivar = 0; ivar < bool_vector_size( active_set); ivar++) {
+ if (!bool_vector_iget( active_set , ivar))
+ continue;
+
+ const module_obs_block_type * module_obs_block = module_obs_block_vector_search_module_obs_block(module_obs_block_vector, ivar);
+ const int* active_indices = module_obs_block_get_active_indices(module_obs_block);
+ bool all_active = active_indices == NULL; /* Inactive are not present in D */
+ int row_start = module_obs_block_get_row_start(module_obs_block);
+ int row_end = module_obs_block_get_row_end(module_obs_block);
+ const char* obs_key = module_obs_block_get_key(module_obs_block);
+ const double var_beta = stepwise_iget_beta(stepwise, ivar);
+ const double var_beta_percent = 100.0 * std::abs(var_beta) / sum_beta;
+
+ int local_index = 0;
+ for (int i = row_start; i < row_end; i++) {
+ if (i == ivar){
+ if (all_active)
+ obs_active_index = local_index;
+ else
+ obs_active_index = active_indices[local_index];
+ break;
+ }
+ local_index ++;
+ }
+
+ char * obs_list_entry = (char*)util_alloc_sprintf(format1 , obs_key, obs_active_index,var_beta_percent);
+ stringlist_append_copy(obs_list, obs_list_entry);
+ double_vector_append(r_list, var_beta_percent);
+ free( obs_list_entry );
+ }
+
+ {
+ /* Sorting with respect to sensitivity */
+ perm_vector_type * sort_perm = double_vector_alloc_rsort_perm(r_list);
+ for (int i = 0; i < stringlist_get_size( obs_list); i++) {
+ const char * obs_list_entry = stringlist_iget(obs_list, perm_vector_iget(sort_perm, i));
+ if (has_log)
+ fwd_step_log_line( data->fwd_step_log , "%s", obs_list_entry);
+
+ printf("%s", obs_list_entry);
+ }
+ perm_vector_free(sort_perm);
+ }
+
+
+
+ if (has_log)
+ fwd_step_log_line( data->fwd_step_log , "\n");
+
+ printf("\n");
+
+ stringlist_free(obs_list);
+ free(data_active_index_str);
+ free(cat);
+}
+
+/*Main function: */
+void fwd_step_enkf_updateA(void * module_data ,
+ matrix_type * A ,
+ const matrix_type * S0 ,
+ const matrix_type * R ,
+ const matrix_type * dObs ,
+ const matrix_type * E ,
+ const matrix_type * D ,
+ const module_info_type* module_info,
+ rng_type * rng) {
+
+
+
+ fwd_step_enkf_data_type * fwd_step_data = fwd_step_enkf_data_safe_cast( module_data );
+ fwd_step_log_open(fwd_step_data->fwd_step_log);
+ module_data_block_vector_type * data_block_vector = module_info_get_data_block_vector(module_info);
+ matrix_type * S = matrix_alloc_copy(S0);
+ {
+
+ int ens_size = matrix_get_columns( S );
+ int nx = matrix_get_rows( A );
+ int nd = matrix_get_rows( S );
+ int nfolds = fwd_step_data->nfolds;
+ double r2_limit = fwd_step_data->r2_limit;
+ bool verbose = fwd_step_data->verbose;
+ int num_kw = module_data_block_vector_get_size(data_block_vector);
+
+#if defined(_OPENMP)
+ #pragma omp parallel
+ #pragma omp master
+ if (fwd_step_data->num_threads == DEFAULT_NUM_THREADS)
+ fwd_step_data->num_threads = omp_get_num_threads();
+#else
+ fwd_step_data->num_threads = 1;
+#endif
+
+ if ( ens_size <= nfolds)
+ util_abort("%s: The number of ensembles must be larger than the CV fold - aborting\n", __func__);
+
+
+ {
+
+ matrix_type * St = matrix_alloc( ens_size , nd );
+ matrix_type * Et = matrix_alloc( ens_size , nd );
+
+ /*workS = S' */
+ matrix_subtract_row_mean( S ); /* Shift away the mean */
+ St = matrix_alloc_transpose( S );
+ Et = matrix_alloc_transpose( E );
+
+ matrix_type * di = matrix_alloc( 1 , nd );
+
+ if (verbose){
+ char * ministep_name = module_info_get_ministep_name(module_info);
+ fwd_step_enkf_write_log_header(fwd_step_data, ministep_name, nx, nd, ens_size);
+ }
+
+ int kw,i;
+
+ /* This is to avoid a global-to-block search function since the number of parameters could be very large*/
+ int_vector_type * kw_list = int_vector_alloc(nx, -1);
+ int_vector_type * local_index_list = int_vector_alloc(nx, -1);
+ for (kw = 0; kw < num_kw; kw++) {
+ module_data_block_type * data_block = module_data_block_vector_iget_module_data_block(data_block_vector, kw);
+ int row_start = module_data_block_get_row_start(data_block);
+ int row_end = module_data_block_get_row_end(data_block);
+ for (i = row_start; i < row_end; i++) {
+ int_vector_iset(kw_list, i, kw);
+ int_vector_iset(local_index_list, i, i - row_start);
+ }
+ }
+
+
+ // =============================================
+ #pragma omp parallel for schedule(dynamic, 1) num_threads(fwd_step_data->num_threads)
+ for (i = 0; i < nx; i++) {
+ int kw_ind = int_vector_iget(kw_list, i);
+ module_data_block_type * data_block = module_data_block_vector_iget_module_data_block(data_block_vector, kw_ind);
+ const char * key = module_data_block_get_key(data_block);
+ const int* active_indices = module_data_block_get_active_indices(data_block);
+ int active_index = 0;
+ bool all_active = active_indices == NULL; /* Inactive are not present in A */
+ stepwise_type * stepwise_data = stepwise_alloc1(ens_size, nd , rng, St, Et);
+
+ /*Update values of y */
+ /*Start of the actual update */
+ matrix_type * y = matrix_alloc( ens_size , 1 );
+
+ for (int j = 0; j < ens_size; j++) {
+ matrix_iset(y , j , 0 , matrix_iget( A, i , j ) );
+ }
+
+ stepwise_set_Y0( stepwise_data , y );
+
+ stepwise_estimate(stepwise_data , r2_limit , nfolds );
+
+ /*manipulate A directly*/
+ for (int j = 0; j < ens_size; j++) {
+ for (int k = 0; k < nd; k++) {
+ matrix_iset(di , 0 , k , matrix_iget( D , k , j ) );
+ }
+ double aij = matrix_iget( A , i , j );
+ double xHat = stepwise_eval(stepwise_data , di );
+ matrix_iset(A , i , j , aij + xHat);
+ }
+
+ if (verbose){
+ int loc_ind = int_vector_iget(local_index_list, i );
+ if (all_active)
+ active_index = loc_ind;
+ else
+ active_index = active_indices[loc_ind];
+
+ fwd_step_enkf_write_iter_info(fwd_step_data, stepwise_data, key, active_index, i, module_info);
+
+ }
+
+ stepwise_free( stepwise_data );
+ }
+
+ if (verbose)
+ printf("===============================================================================================================================\n");
+
+ printf("Done with stepwise regression enkf\n");
+
+
+ matrix_free( di );
+ int_vector_free(kw_list);
+ int_vector_free(local_index_list);
+ }
+
+
+
+ }
+ matrix_free(S);
+ fwd_step_log_close( fwd_step_data->fwd_step_log );
+}
+
+
+
+
+
+
+bool fwd_step_enkf_set_double( void * arg , const char * var_name , double value) {
+ fwd_step_enkf_data_type * module_data = fwd_step_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , R2_LIMIT_KEY ) == 0)
+ fwd_step_enkf_set_r2_limit( module_data , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+
+bool fwd_step_enkf_set_int( void * arg , const char * var_name , int value) {
+ fwd_step_enkf_data_type * module_data = fwd_step_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+
+ if (strcmp( var_name , NFOLDS_KEY) == 0)
+ fwd_step_enkf_set_nfolds( module_data , value); /*Set number of CV folds */
+ else if (strcmp( var_name , NUM_THREADS_KEY) == 0)
+ fwd_step_enkf_set_num_threads( module_data , value); /*Set number of OMP threads */
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+bool fwd_step_enkf_set_bool( void * arg , const char * var_name , bool value) {
+ fwd_step_enkf_data_type * module_data = fwd_step_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ /*Set verbose */
+ if (strcmp( var_name , VERBOSE_KEY) == 0)
+ fwd_step_enkf_set_verbose( module_data , value);
+ else if (strcmp( var_name , CLEAR_LOG_KEY) == 0)
+ fwd_step_log_set_clear_log( module_data->fwd_step_log , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+bool fwd_step_enkf_set_string( void * arg , const char * var_name , const char * value) {
+ fwd_step_enkf_data_type * module_data = fwd_step_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , LOG_FILE_KEY) == 0)
+ fwd_step_log_set_log_file( module_data->fwd_step_log , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+long fwd_step_enkf_get_options( void * arg , long flag) {
+ fwd_step_enkf_data_type * fwd_step_data = fwd_step_enkf_data_safe_cast( arg );
+ {
+ return fwd_step_data->option_flags;
+ }
+}
+
+bool fwd_step_enkf_has_var( const void * arg, const char * var_name) {
+ {
+ if (strcmp(var_name , NFOLDS_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , R2_LIMIT_KEY ) == 0)
+ return true;
+ else if (strcmp(var_name , VERBOSE_KEY ) == 0)
+ return true;
+ else if (strcmp(var_name , LOG_FILE_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , CLEAR_LOG_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , NUM_THREADS_KEY) == 0)
+ return true;
+ else
+ return false;
+ }
+}
+
+double fwd_step_enkf_get_double( const void * arg, const char * var_name) {
+ const fwd_step_enkf_data_type * module_data = fwd_step_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , R2_LIMIT_KEY ) == 0)
+ return module_data->r2_limit;
+ else
+ return -1;
+ }
+}
+
+int fwd_step_enkf_get_int( const void * arg, const char * var_name) {
+ const fwd_step_enkf_data_type * module_data = fwd_step_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , NFOLDS_KEY) == 0)
+ return module_data->nfolds;
+ if (strcmp(var_name , NUM_THREADS_KEY) == 0)
+ return module_data->num_threads;
+ else
+ return -1;
+ }
+}
+
+bool fwd_step_enkf_get_bool( const void * arg, const char * var_name) {
+ const fwd_step_enkf_data_type * module_data = fwd_step_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , VERBOSE_KEY) == 0)
+ return module_data->verbose;
+ else if (strcmp(var_name , CLEAR_LOG_KEY) == 0)
+ return fwd_step_log_get_clear_log( module_data->fwd_step_log );
+ else
+ return false;
+ }
+}
+
+void * fwd_step_enkf_get_ptr( const void * arg , const char * var_name ) {
+ const fwd_step_enkf_data_type * module_data = fwd_step_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , LOG_FILE_KEY) == 0)
+ return (void *) fwd_step_log_get_log_file( module_data->fwd_step_log );
+ else
+ return NULL;
+ }
+}
+
+
+
+
+
+#ifdef INTERNAL_LINK
+#define LINK_NAME FWD_STEP_ENKF
+#else
+#define LINK_NAME EXTERNAL_MODULE_SYMBOL
+#endif
+
+
+analysis_table_type LINK_NAME = {
+ .name = "FWD_STEP_ENKF",
+ .updateA = fwd_step_enkf_updateA,
+ .initX = NULL ,
+ .init_update = NULL ,
+ .complete_update = NULL ,
+
+ .freef = fwd_step_enkf_data_free,
+ .alloc = fwd_step_enkf_data_alloc,
+
+ .set_int = fwd_step_enkf_set_int ,
+ .set_double = fwd_step_enkf_set_double ,
+ .set_bool = fwd_step_enkf_set_bool ,
+ .set_string = fwd_step_enkf_set_string ,
+ .get_options = fwd_step_enkf_get_options ,
+
+ .has_var = fwd_step_enkf_has_var,
+ .get_int = fwd_step_enkf_get_int ,
+ .get_double = fwd_step_enkf_get_double ,
+ .get_bool = fwd_step_enkf_get_bool ,
+ .get_ptr = fwd_step_enkf_get_ptr
+};
+
+
diff --git a/libres/lib/analysis/fwd_step_log.cpp b/libres/lib/analysis/fwd_step_log.cpp
new file mode 100644
index 00000000000..df18bf46097
--- /dev/null
+++ b/libres/lib/analysis/fwd_step_log.cpp
@@ -0,0 +1,105 @@
+/*
+ Copyright (C) 2016 Equinor ASA, Norway.
+
+ The file 'fwd_step_log.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+
+#include
+#include
+
+#include
+
+#include
+
+#define DEFAULT_LOG_FILE "fwd_step.out"
+#define DEFAULT_CLEAR_LOG false
+
+struct fwd_step_log_struct {
+ bool clear_log;
+ char * log_file;
+ FILE * log_stream;
+};
+
+
+fwd_step_log_type * fwd_step_log_alloc() {
+ fwd_step_log_type * fwd_step_log = (fwd_step_log_type*)util_malloc( sizeof * fwd_step_log );
+ fwd_step_log->log_file = NULL;
+ fwd_step_log->log_stream = NULL;
+ fwd_step_log_set_log_file( fwd_step_log , DEFAULT_LOG_FILE);
+ fwd_step_log_set_clear_log( fwd_step_log , DEFAULT_CLEAR_LOG );
+ return fwd_step_log;
+}
+
+bool fwd_step_log_get_clear_log( const fwd_step_log_type * data ) {
+ return data->clear_log;
+}
+
+void fwd_step_log_set_clear_log( fwd_step_log_type * data , bool clear_log) {
+ data->clear_log = clear_log;
+}
+
+void fwd_step_log_set_log_file( fwd_step_log_type * data , const char * log_file ) {
+ data->log_file = util_realloc_string_copy( data->log_file , log_file );
+}
+
+const char * fwd_step_log_get_log_file( const fwd_step_log_type * data) {
+ return data->log_file;
+}
+
+
+void fwd_step_log_free(fwd_step_log_type * fwd_step_log) {
+ fwd_step_log_close( fwd_step_log );
+ free( fwd_step_log->log_file );
+ free( fwd_step_log );
+}
+
+
+void fwd_step_log_open( fwd_step_log_type * fwd_step_log ) {
+ if (fwd_step_log->log_file) {
+ if (fwd_step_log->clear_log)
+ fwd_step_log->log_stream = util_mkdir_fopen( fwd_step_log->log_file , "w");
+ else
+ fwd_step_log->log_stream = util_mkdir_fopen( fwd_step_log->log_file , "a");
+ }
+}
+
+
+bool fwd_step_log_is_open( const fwd_step_log_type * fwd_step_log ) {
+ if (fwd_step_log->log_stream)
+ return true;
+ else
+ return false;
+}
+
+
+void fwd_step_log_close( fwd_step_log_type * fwd_step_log ) {
+ if (fwd_step_log->log_stream)
+ fclose( fwd_step_log->log_stream );
+
+ fwd_step_log->log_stream = NULL;
+}
+
+
+void fwd_step_log_line( fwd_step_log_type * fwd_step_log , const char * fmt , ...) {
+ if (fwd_step_log->log_stream) {
+ va_list ap;
+ va_start(ap , fmt);
+ vfprintf( fwd_step_log->log_stream , fmt , ap );
+ va_end( ap );
+ }
+}
+
+
diff --git a/libres/lib/analysis/module_data_block.cpp b/libres/lib/analysis/module_data_block.cpp
new file mode 100644
index 00000000000..58f262dbb7c
--- /dev/null
+++ b/libres/lib/analysis/module_data_block.cpp
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2016 Equinor ASA, Norway.
+
+ The file 'module_data_block.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+#define MODULE_DATA_BLOCK_TYPE_ID 73217801
+static UTIL_SAFE_CAST_FUNCTION( module_data_block , MODULE_DATA_BLOCK_TYPE_ID);
+UTIL_IS_INSTANCE_FUNCTION( module_data_block , MODULE_DATA_BLOCK_TYPE_ID)
+
+module_data_block_type * module_data_block_alloc( const char * key, const int * index_list , const int row_start, const int n_active) {
+ module_data_block_type * module_data_block = (module_data_block_type*)util_malloc( sizeof * module_data_block );
+ UTIL_TYPE_ID_INIT( module_data_block , MODULE_DATA_BLOCK_TYPE_ID );
+ module_data_block->key = util_alloc_string_copy( key );
+ module_data_block->index_list = index_list;
+ module_data_block->A_row_start = row_start;
+ module_data_block->n_active = n_active;
+ return module_data_block;
+}
+
+
+const char * module_data_block_get_key(const module_data_block_type * module_data_block){
+ return module_data_block->key;
+}
+
+const int module_data_block_get_row_start(const module_data_block_type * module_data_block){
+ return module_data_block->A_row_start;
+}
+
+const int module_data_block_get_row_end(const module_data_block_type * module_data_block){
+ return module_data_block->A_row_start + module_data_block->n_active;
+}
+
+const int * module_data_block_get_active_indices(const module_data_block_type * module_data_block ){
+ return module_data_block->index_list;
+}
+
+void module_data_block_free( module_data_block_type * module_data_block ) {
+ free(module_data_block->key);
+ free( module_data_block );
+}
+
+void module_data_block_free__( void * arg ) {
+ module_data_block_type * data_block = module_data_block_safe_cast( arg );
+ module_data_block_free( data_block );
+}
diff --git a/libres/lib/analysis/module_data_block_vector.cpp b/libres/lib/analysis/module_data_block_vector.cpp
new file mode 100644
index 00000000000..260412fc728
--- /dev/null
+++ b/libres/lib/analysis/module_data_block_vector.cpp
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 2016 Equinor ASA, Norway.
+
+ The file 'module_data_block_vector.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+
+#include
+
+#include
+#include
+
+#define MODULE_DATA_BLOCK_VECTOR_TYPE_ID 732178012
+
+struct module_data_block_vector_struct {
+ UTIL_TYPE_ID_DECLARATION;
+ vector_type * data_block_vector;
+};
+
+UTIL_IS_INSTANCE_FUNCTION( module_data_block_vector , MODULE_DATA_BLOCK_VECTOR_TYPE_ID)
+
+module_data_block_vector_type * module_data_block_vector_alloc( ) {
+ module_data_block_vector_type * module_data_block_vector = (module_data_block_vector_type*)util_malloc( sizeof * module_data_block_vector );
+ UTIL_TYPE_ID_INIT( module_data_block_vector , MODULE_DATA_BLOCK_VECTOR_TYPE_ID );
+ module_data_block_vector->data_block_vector = vector_alloc_new();
+ return module_data_block_vector;
+}
+
+
+void module_data_block_vector_free( module_data_block_vector_type * module_data_block_vector ) {
+ vector_free( module_data_block_vector->data_block_vector );
+ free( module_data_block_vector );
+}
+
+void module_data_block_vector_add_data_block( module_data_block_vector_type * module_data_block_vector , const module_data_block_type * data_block) {
+ vector_append_owned_ref(module_data_block_vector->data_block_vector, data_block , module_data_block_free__);
+}
+
+
+module_data_block_type * module_data_block_vector_iget_module_data_block(const module_data_block_vector_type * module_data_block_vector, int index){
+ return (module_data_block_type*)vector_iget(module_data_block_vector->data_block_vector, index);
+}
+
+int module_data_block_vector_get_size(const module_data_block_vector_type * module_data_block_vector){
+ return vector_get_size(module_data_block_vector->data_block_vector);
+}
+
+
diff --git a/libres/lib/analysis/module_info.cpp b/libres/lib/analysis/module_info.cpp
new file mode 100644
index 00000000000..c374f3dc139
--- /dev/null
+++ b/libres/lib/analysis/module_info.cpp
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) 2016 Equinor ASA, Norway.
+
+ The file 'module_info.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+
+#include
+
+#include
+
+#define MODULE_INFO_TYPE_ID 73780123
+
+struct module_info_struct {
+ UTIL_TYPE_ID_DECLARATION;
+ char * ministep_name;
+ module_data_block_vector_type * data_block_vector;
+ module_obs_block_vector_type * obs_block_vector;
+};
+
+UTIL_IS_INSTANCE_FUNCTION( module_info , MODULE_INFO_TYPE_ID)
+
+module_info_type * module_info_alloc( const char* ministep_name ) {
+ module_info_type * module_info = (module_info_type*)util_malloc( sizeof * module_info );
+ UTIL_TYPE_ID_INIT( module_info , MODULE_INFO_TYPE_ID );
+ module_info->ministep_name = util_alloc_string_copy( ministep_name );
+ module_info->data_block_vector = module_data_block_vector_alloc();
+ module_info->obs_block_vector = module_obs_block_vector_alloc();
+ return module_info;
+}
+
+
+void module_info_free( module_info_type * module_info ) {
+ free(module_info->ministep_name);
+ module_data_block_vector_free( module_info->data_block_vector );
+ module_obs_block_vector_free( module_info->obs_block_vector );
+ free( module_info );
+}
+
+module_data_block_vector_type * module_info_get_data_block_vector(const module_info_type * module_info){
+ return module_info->data_block_vector;
+}
+
+module_obs_block_vector_type * module_info_get_obs_block_vector(const module_info_type * module_info){
+ return module_info->obs_block_vector;
+}
+
+char * module_info_get_ministep_name(const module_info_type * module_info){
+ return module_info->ministep_name;
+}
diff --git a/libres/lib/analysis/module_obs_block.cpp b/libres/lib/analysis/module_obs_block.cpp
new file mode 100644
index 00000000000..5262014a3ff
--- /dev/null
+++ b/libres/lib/analysis/module_obs_block.cpp
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2016 Equinor ASA, Norway.
+
+ The file 'module_obs_block.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+#define MODULE_OBS_BLOCK_TYPE_ID 73217901
+static UTIL_SAFE_CAST_FUNCTION( module_obs_block , MODULE_OBS_BLOCK_TYPE_ID);
+UTIL_IS_INSTANCE_FUNCTION( module_obs_block , MODULE_OBS_BLOCK_TYPE_ID)
+
+module_obs_block_type * module_obs_block_alloc( const char * key, const int * index_list, const int row_start, const int n_active) {
+ module_obs_block_type * module_obs_block = (module_obs_block_type*)util_malloc( sizeof * module_obs_block );
+ UTIL_TYPE_ID_INIT( module_obs_block , MODULE_OBS_BLOCK_TYPE_ID );
+ module_obs_block->key = util_alloc_string_copy( key );
+ module_obs_block->index_list = index_list;
+ module_obs_block->D_row_start = row_start;
+ module_obs_block->n_active = n_active;
+ return module_obs_block;
+}
+
+
+const char * module_obs_block_get_key(const module_obs_block_type * module_obs_block){
+ return module_obs_block->key;
+}
+
+const int module_obs_block_get_row_start(const module_obs_block_type * module_obs_block){
+ return module_obs_block->D_row_start;
+}
+
+const int module_obs_block_get_row_end(const module_obs_block_type * module_obs_block){
+ return module_obs_block->D_row_start + module_obs_block->n_active;
+}
+
+const int * module_obs_block_get_active_indices(const module_obs_block_type * module_obs_block ){
+ return module_obs_block->index_list;
+}
+
+void module_obs_block_free( module_obs_block_type * module_obs_block ) {
+ free(module_obs_block->key);
+ free( module_obs_block );
+}
+
+void module_obs_block_free__( void * arg ) {
+ module_obs_block_type * obs_block = module_obs_block_safe_cast( arg );
+ module_obs_block_free( obs_block );
+}
diff --git a/libres/lib/analysis/module_obs_block_vector.cpp b/libres/lib/analysis/module_obs_block_vector.cpp
new file mode 100644
index 00000000000..ffdfbe90f05
--- /dev/null
+++ b/libres/lib/analysis/module_obs_block_vector.cpp
@@ -0,0 +1,79 @@
+/*
+ Copyright (C) 2016 Equinor ASA, Norway.
+
+ The file 'module_obs_block_vector.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+
+#include
+
+#include
+#include
+
+#define MODULE_OBS_BLOCK_VECTOR_TYPE_ID 732188012
+
+struct module_obs_block_vector_struct {
+ UTIL_TYPE_ID_DECLARATION;
+ vector_type * obs_block_vector;
+};
+
+UTIL_IS_INSTANCE_FUNCTION( module_obs_block_vector , MODULE_OBS_BLOCK_VECTOR_TYPE_ID)
+
+module_obs_block_vector_type * module_obs_block_vector_alloc() {
+ module_obs_block_vector_type * module_obs_block_vector = (module_obs_block_vector_type*)util_malloc( sizeof * module_obs_block_vector );
+ UTIL_TYPE_ID_INIT( module_obs_block_vector , MODULE_OBS_BLOCK_VECTOR_TYPE_ID );
+ module_obs_block_vector->obs_block_vector = vector_alloc_new();
+ return module_obs_block_vector;
+}
+
+
+void module_obs_block_vector_free( module_obs_block_vector_type * module_obs_block_vector ) {
+ vector_free( module_obs_block_vector->obs_block_vector );
+ free( module_obs_block_vector );
+}
+
+void module_obs_block_vector_add_obs_block( module_obs_block_vector_type * module_obs_block_vector , module_obs_block_type * obs_block) {
+ vector_append_owned_ref(module_obs_block_vector->obs_block_vector, obs_block , module_obs_block_free__);
+}
+
+
+module_obs_block_type * module_obs_block_vector_iget_module_obs_block(const module_obs_block_vector_type * module_obs_block_vector, int block_index){
+ return (module_obs_block_type*)vector_iget(module_obs_block_vector->obs_block_vector, block_index);
+}
+
+const module_obs_block_type * module_obs_block_vector_search_module_obs_block(const module_obs_block_vector_type * module_obs_block_vector, int global_index){
+ /* This function maps from a global index to an observation information block. Will return NULL if block is not found */
+ int block_nr = 0;
+ while (true) {
+ if (block_nr >= module_obs_block_vector_get_size( module_obs_block_vector ))
+ break;
+
+ module_obs_block_type * module_obs_block = module_obs_block_vector_iget_module_obs_block (module_obs_block_vector, block_nr);
+ int row_start = module_obs_block_get_row_start(module_obs_block);
+ int row_end = module_obs_block_get_row_end(module_obs_block);
+ if (global_index >= row_start && global_index < row_end)
+ return module_obs_block;
+
+ block_nr++;
+ }
+ return NULL;
+}
+
+int module_obs_block_vector_get_size(const module_obs_block_vector_type * module_obs_block_vector){
+ return vector_get_size(module_obs_block_vector->obs_block_vector);
+}
+
+
diff --git a/libres/lib/analysis/modules.txt b/libres/lib/analysis/modules.txt
new file mode 100644
index 00000000000..5054fba4414
--- /dev/null
+++ b/libres/lib/analysis/modules.txt
@@ -0,0 +1,212 @@
+Overview
+--------
+The ERT application has evolved into a quite complex beast. Originally
+the actual EnKF algorithm was located very deep down in the code, and
+changes to the EnKF algorithm required detailed knowledge about the
+enkf_node objects, the datatype for measurements and observations,
+serialisation and so on. Quite complex and fragile stuff.
+
+To facilitate easier development and testing of new update schemes the
+core EnKF update step has been factored out as analysis modules, which
+are called from the enkf_main scope. The update functions in the
+module get ordinary matrices as input, and do not need to know
+anything about the internals of the ERT application.
+
+The modules can either be built in, or you can compile your own module
+as a shared library and load it runtime. Apart from the initial
+loading internal modules and external modules are treated 100%
+identically.
+
+
+How modules work
+----------------
+The modules are instances of the object type analysis_module_type, and
+the enkf_main layer interacts with the module through the
+analyis_module instance. The most important part of the
+analysis_module type is a list of function pointers to functions which
+"actually do the work". In addition to some functions for setting
+internal module variables the most core functions are:
+
+ init_update()
+ initX()
+ updateA()
+ complete_update()
+
+Of these functions a module should have ONLY ONE of initX() and
+updateA(); the initX() function will initialize an X matrix which is
+then subsequently used to update the ensemble matrix A. Alternatively
+the updateA() function should will directly manipulate the A matrix.
+
+The init_update() and complete_update() functions are optional, they
+can be used to avoid repeated initialization calculations. These
+functions interact quite closely with the scheme used for local
+analysis; so you should understand that in detail before deciding
+whether it is worthwile to implement init_update() and
+complete_update() functions.
+
+
+Loading modules
+---------------
+
+The loading of modules is based on the dlopen() function to open a
+shared a library and map it into the adress space of the current
+process, and then the dlsym() function to locate a symbol table which
+is a list of function pointers. The only difference between internal
+and external modules is in the arguments passed to the dlopen() and
+dlsym() function calls:
+
+ Loading internal modules
+ ------------------------
+ Internal modules means modules which have been compiled into the
+ libanalyis library, i.e. they are already part of the ERT
+ executable, however before we have "loaded" these modules we have
+ no way to access them. The current executable is loaded by
+ passing NULL as argument to dlopen().
+
+ When dlopen() has succeded we must use dlsym() to get a handle to
+ the symbol table. The symbol table is essentially a global
+ variable in the ERT adress space, and must have a unique name for
+ each module.
+
+
+ Loading external modules
+ ------------------------
+ External modules are ordinary shared libraries. To load an external
+ module the name of the shared library must be given to the dlopen()
+ function, normal rules for runtime loading of dynamic libraries
+ apply - i.e. the shared library must be in a location where the
+ dynamic linker can find it. The name of module should be a
+ filename, including the .so extension. undo
+
+ When dlopen() has succeded we must use dlsym() to get a handle to
+ the symbol table; when the module loading is hidden behind a
+ dlopen() call we have essentially created a module namespace, and
+ the symbol tables from the different modules can have the same
+ name. For convenience we assume that the symbol table is given by
+ the name defined by the symbol EXTERNAL_MODULE_TABLE in
+ analysis_module.h.
+
+In practice all of is handled by the functions:
+
+ analysis_module_type * analysis_module_alloc_internal( );
+ analysis_module_type * analysis_module_alloc_external( );
+
+symbol_table
+------------
+
+Interacting with modules
+------------------------
+The modules can implement four different functions to set a scalar
+value. The four functions have signature:
+
+ bool set_int (void * module_data, const char * var_name , int value)
+ bool set_double( void * module_data , const char * var_name , double value)
+ bool set_bool( void * module_data , const char * var_name , bool value )
+ bool set_string( void * module_data , const char * var_name , const char * value )
+
+Common for all these functions is:
+
+ 1. It is not necessary to implement these functions; if you know
+ that the module has no internal integer variables which should be
+ user-modifiable you can just set the set_int function pointer to
+ NULL.
+
+ 2. If the module recognizes the variable name and actually sets an
+ internal variable it should return true, otherwise return false.
+
+ 3. The set_xxx() functions are called from the wrapper function
+ analysis_module_set_var(); when calling the wrapper function the
+ value variable is a string which we try to convert to int, double
+ and bool respectively and then cascade through the functions in
+ the order listed above. This involves two things:
+
+ a) A module has an internal variable namespace which is shared
+ among all variable types.
+
+ b) If the string value argument is incorrectly formatted,
+ i.e. an integer is passed as "12x" the correct low level
+ function will not be called.
+
+The method to get information out from the module is much more
+limited. Each module should contain an internal variable:
+
+ long option_flags;
+
+And when instantiating the module data you should initialize the
+option_flags variable by adding together the relevant option flags
+from analysis_module.h. The enkf_main_UPDATE() method which invokes
+the module functions will inspect the option_flags to see which
+variables to pass to the module, and which module functions to invoke,
+so this must be correct.
+
+Example
+-------
+The module/file std_enkf.c is commented quite heavily to serve as an
+example.
+
+---------------------------------------------------------------------
+
+With the analysis modules we have essentially got a three layer
+design:
+
+ 1. The ert core layer which creates/loads a list of analysis_module
+ instances.
+
+ 2. analysis_module instances are a thing layer which:
+
+ a) Hold on to specific analysis implementations like e.g. the
+ std_enkf implementation on one side.
+
+ b) Present a uniform module interface to the ert layer on the
+ 'other side'.
+
+ 3. Specific analysis module implementations like the std_enkf.
+
+
+In fine ASCII art:
+ .
+ +----------------------+ . +---------------------------+
+ | Analysis module | . | Implementation std_enkf |
+ +----------------------+ . +---------------------------+
+ | | /------> | std_enkf_alloc() |
+ ----------------------> | | / . | std_enkf_free() |
+ / | X |o--------> | std_enkf_get_options() |
+ / | | \ . | std_enkf_initX() |
+ +------------------------------------------+ / | | \------> | std_enkf_set_int() |
+ | The ert core layer; in particular | / | | . | std_enkf_set_double() |
+ | the following functions: | / +----------------------+ . +---------------------------+
+ | - analysis_config_load_external_module() |o .
+ | - analysis_config_load_internal_module() | \ +----------------------+ . +---------------------------+
+ | - enkf_main_module_update() | \ | Analysis module | . | Implementation sqrt_enkf |
+ +------------------------------------------+ \ +----------------------+ . +---------------------------+
+ \ | | /------> | sqrt_enkf_alloc() |
+ \ | | / . | sqrt_enkf_free() |
+ \---------------------> | |o--------> | sqrt_enkf_get_options() |
+ \ | X | \ . | sqrt_enkf_initX() |
+ \ | | \------> | sqrt_enkf_set_int() |
+ \ | | . | sqrt_enkf_set_double() |
+ \ +----------------------+ . +---------------------------+
+ \ .
+ In the ert core layer the \ +----------------------+ . +---------------------------+
+ different analysis module \ | Analysis module | . | Implementation cv_enkf |
+ instances are collected in \ +----------------------+ . +---------------------------+
+ a hash table, and all function \ | | /------> | cv_enkf_alloc() |
+ invocations go through the \ | | / . | cv_enkf_free() |
+ functions in the analysis_module -----------> | |o--------> | cv_enkf_get_options() |
+ layer. | X | \ . | cv_enkf_initX() |
+ | | \------> | cv_enkf_set_int() |
+ | | . | cv_enkf_set_double() |
+ +----------------------+ . +---------------------------+
+ .
+ .
+ .
+ .
+ .
+ The links between the analysis
+ modules and the std_enkf, sqrt_enkf
+ and cv_enkf implementations are
+ in terms of function pointers. These
+ links are established runtime, and
+ the vertical dotted line represents
+ an opaque wall which ert core layer
+ can not see through.
diff --git a/libres/lib/analysis/modules/deprecated/rml_enkf.c b/libres/lib/analysis/modules/deprecated/rml_enkf.c
new file mode 100644
index 00000000000..232834edf37
--- /dev/null
+++ b/libres/lib/analysis/modules/deprecated/rml_enkf.c
@@ -0,0 +1,427 @@
+/*
+ Copyright (C) 2011 Equinor ASA, Norway.
+
+ The file 'rml_enkf.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+/*
+ A random 'magic' integer id which is used for run-time type checking
+ of the input data.
+*/
+#define RML_ENKF_TYPE_ID 261123
+
+
+
+
+/*
+ Observe that only one of the settings subspace_dimension and
+ truncation can be valid at a time; otherwise the svd routine will
+ fail. This implies that the set_truncation() and
+ set_subspace_dimension() routines will set one variable, AND
+ INVALIDATE THE OTHER. For most situations this will be OK, but if
+ you have repeated calls to both of these functions the end result
+ might be a surprise.
+*/
+#define INVALID_SUBSPACE_DIMENSION -1
+#define INVALID_TRUNCATION -1
+#define DEFAULT_SUBSPACE_DIMENSION INVALID_SUBSPACE_DIMENSION
+
+
+
+
+
+/*
+ The configuration data used by the rml_enkf module is contained in a
+ rml_enkf_data_struct instance. The data type used for the rml_enkf
+ module is quite simple; with only a few scalar variables, but there
+ are essentially no limits to what you can pack into such a datatype.
+
+ All the functions in the module have a void pointer as the first
+ argument, this will immediately be casted to a rml_enkf_data_type
+ instance, to get some type safety the UTIL_TYPE_ID system should be
+ used (see documentation in util.h)
+
+ The data structure holding the data for your analysis module should
+ be created and initialized by a constructor, which should be
+ registered with the '.alloc' element of the analysis table; in the
+ same manner the desctruction of this data should be handled by a
+ destructor or free() function registered with the .freef field of
+ the analysis table.
+*/
+
+
+typedef struct rml_enkf_data_struct rml_enkf_data_type;
+
+struct rml_enkf_data_struct {
+ UTIL_TYPE_ID_DECLARATION;
+ double truncation; // Controlled by config key: ENKF_TRUNCATION_KEY
+ int subspace_dimension; // Controlled by config key: ENKF_NCOMP_KEY (-1: use Truncation instead)
+ long option_flags;
+
+ int iteration_nr; // Keep track of the outer iteration loop
+ double lambda; // parameter to control the search direction in Marquardt levenberg optimization
+ double lambda0; // Initial lambda value
+ double Sk; // Objective function value
+ double Std; // Standard Deviation of the Objective function
+ matrix_type *state;
+ bool_vector_type * ens_mask;
+};
+
+
+/*
+ This is a macro which will expand to generate a function:
+
+ rml_enkf_data_type * rml_enkf_data_safe_cast( void * arg ) {}
+
+ which is used for runtime type checking of all the functions which
+ accept a void pointer as first argument.
+*/
+static UTIL_SAFE_CAST_FUNCTION( rml_enkf_data , RML_ENKF_TYPE_ID )
+static UTIL_SAFE_CAST_FUNCTION_CONST( rml_enkf_data , RML_ENKF_TYPE_ID )
+
+
+double rml_enkf_get_truncation( rml_enkf_data_type * data ) {
+ return data->truncation;
+}
+
+int rml_enkf_get_subspace_dimension( rml_enkf_data_type * data ) {
+ return data->subspace_dimension;
+}
+
+void rml_enkf_set_truncation( rml_enkf_data_type * data , double truncation ) {
+ data->truncation = truncation;
+ if (truncation > 0.0)
+ data->subspace_dimension = INVALID_SUBSPACE_DIMENSION;
+}
+
+void rml_enkf_set_lambda0(rml_enkf_data_type * data , double lambda0 ) {
+ data->lambda0 = lambda0;
+}
+
+void rml_enkf_set_subspace_dimension( rml_enkf_data_type * data , int subspace_dimension) {
+ data->subspace_dimension = subspace_dimension;
+ if (subspace_dimension > 0)
+ data->truncation = INVALID_TRUNCATION;
+}
+
+void rml_enkf_set_iteration_number( rml_enkf_data_type *data , int iteration_number ) {
+ data->iteration_nr = iteration_number;
+}
+
+
+void * rml_enkf_data_alloc( rng_type * rng) {
+ rml_enkf_data_type * data = util_malloc( sizeof * data );
+ UTIL_TYPE_ID_INIT( data , RML_ENKF_TYPE_ID );
+
+ rml_enkf_set_truncation( data , DEFAULT_ENKF_TRUNCATION_ );
+ rml_enkf_set_subspace_dimension( data , DEFAULT_SUBSPACE_DIMENSION );
+ data->option_flags = ANALYSIS_NEED_ED + ANALYSIS_UPDATE_A + ANALYSIS_ITERABLE + ANALYSIS_SCALE_DATA;
+ data->iteration_nr = 0;
+ data->Std = 0;
+ data->state = matrix_alloc(1,1); // This will be resized under use; but we need a valid instance
+ data->lambda0 = -1.0;
+ data->ens_mask = bool_vector_alloc(0,false);
+ return data;
+}
+
+
+void rml_enkf_data_free( void * module_data ) {
+ rml_enkf_data_type * data = rml_enkf_data_safe_cast( module_data );
+ matrix_free( data->state );
+ bool_vector_free(data->ens_mask);
+ free( data );
+}
+
+
+
+
+
+/*
+ About the matrix Cd: The matrix Cd is calculated based on the content
+ of the E input matrix. In the original implementation this matrix was
+ only calculated in the first iteration, and then reused between subsequent
+ iterations.
+
+ Due to deactivating outliers the number of active observations can change
+ from one iteration to the next, if the matrix Cd is then reused between
+ iterations we will get a matrix size mismatch in the linear algebra. In the
+ current implementation the Cd matrix is recalculated based on the E input
+ for each iteration.
+ */
+
+void rml_enkf_updateA(void * module_data ,
+ matrix_type * A ,
+ matrix_type * S ,
+ matrix_type * R ,
+ matrix_type * dObs ,
+ matrix_type * E ,
+ matrix_type * D) {
+
+
+ rml_enkf_data_type * data = rml_enkf_data_safe_cast( module_data );
+ double truncation = data->truncation;
+ double Sk_new;
+ double Std_new;
+
+ int ens_size = matrix_get_columns( S );
+ int nrobs = matrix_get_rows( S );
+ matrix_type * Cd = matrix_alloc( nrobs , nrobs);
+ double nsc = 1/sqrt(ens_size-1);
+ matrix_type * Skm = matrix_alloc(matrix_get_columns(D),matrix_get_columns(D));
+ FILE *fp = util_fopen("rml_enkf_output","a");
+
+ int nrmin = util_int_min( ens_size , nrobs);
+ matrix_type * Ud = matrix_alloc( nrobs , nrmin ); /* Left singular vectors. */
+ matrix_type * VdT = matrix_alloc( nrmin , ens_size ); /* Right singular vectors. */
+ double * Wd = util_calloc( nrmin , sizeof * Wd );
+
+
+ Cd = matrix_alloc( nrobs, nrobs );
+ enkf_linalg_Covariance(Cd ,E ,nsc, nrobs);
+ matrix_inv(Cd);
+
+ if (data->iteration_nr == 0) {
+ Sk_new = enkf_linalg_data_mismatch(D,Cd,Skm); //Calculate the intitial data mismatch term
+ Std_new = matrix_diag_std(Skm,Sk_new);
+ rml_enkf_common_store_state( data->state , A , data->ens_mask );
+
+
+
+ if (data->lambda0 < 0)
+ data->lambda = pow(10,floor(log10(Sk_new/(2*nrobs))));
+ else
+ data->lambda = data->lambda0;
+
+ rml_enkf_common_initA__(A,S,Cd,E,D,truncation,data->lambda,Ud,Wd,VdT);
+ data->Sk = Sk_new;
+ data->Std = Std_new;
+ printf("Prior Objective function value is %5.3f \n", data->Sk);
+
+ fprintf(fp,"Iteration number\t Lamda Value \t Current Mean (OB FN) \t Old Mean\t Current Stddev\n");
+ fprintf(fp, "\n\n");
+ fprintf(fp,"%d \t\t NA \t %5.5f \t \t %5.5f \n",data->iteration_nr, Sk_new, Std_new);
+
+ } else {
+ Sk_new = enkf_linalg_data_mismatch(D , Cd , Skm); //Calculate the intitial data mismatch term
+ Std_new= matrix_diag_std(Skm,Sk_new);
+ printf(" Current Objective function value is %5.3f \n\n",Sk_new);
+ printf("The old Objective function value is %5.3f \n", data->Sk);
+
+
+ if ((Sk_new< (data->Sk)) && (Std_new< (data->Std)))
+ {
+ if ( (1- (Sk_new/data->Sk)) < .0001) // check convergence ** model change norm has to be added in this!!
+ data-> iteration_nr = 16;
+
+
+ fprintf(fp,"%d \t\t %5.5f \t %5.5f \t %5.5f \t %5.5f \n",data->iteration_nr,data->lambda, Sk_new,data->Sk, Std_new);
+ data->lambda = data->lambda / 10 ;
+ data->Std = Std_new;
+
+ rml_enkf_common_store_state( data->state , A , data->ens_mask );
+
+ data->Sk = Sk_new;
+ rml_enkf_common_initA__(A,S,Cd,E,D,truncation,data->lambda,Ud,Wd,VdT);
+ }
+ else if((Sk_new< (data->Sk)) && (Std_new > (data->Std)))
+ {
+ if ( (1- (Sk_new/data->Sk)) < .0001) // check convergence ** model change norm has to be added in this!!
+ data-> iteration_nr = 16;
+
+
+ fprintf(fp,"%d \t\t %5.5f \t %5.5f \t %5.5f \t %5.5f \n",data->iteration_nr,data->lambda, Sk_new,data->Sk, Std_new);
+ data->Std=Std_new;
+
+ rml_enkf_common_store_state( data->state , A , data->ens_mask );
+
+ data->Sk = Sk_new;
+ rml_enkf_common_initA__(A,S,Cd,E,D,truncation,data->lambda,Ud,Wd,VdT);
+ }
+ else {
+ fprintf(fp,"%d \t\t %5.5f \t %5.5f \t %5.5f \t %5.5f \n",data->iteration_nr,data->lambda, Sk_new,data->Sk, Std_new);
+ printf("The previous step is rejected !!\n");
+ data->lambda = data ->lambda * 4;
+
+ rml_enkf_common_recover_state( data->state , A , data->ens_mask );
+
+ rml_enkf_common_initA__(A,S,Cd,E,D,truncation,data->lambda,Ud,Wd,VdT);
+ data->iteration_nr--;
+ }
+ }
+ data->iteration_nr++;
+
+ // setting the lower bound for lambda
+ if (data->lambda <.01)
+ data->lambda= .01;
+
+
+ printf ("The current iteration number is %d \n ", data->iteration_nr);
+
+
+ matrix_free(Cd);
+ matrix_free(Ud);
+ matrix_free(VdT);
+ matrix_free(Skm);
+ free(Wd);
+ fclose(fp);
+}
+
+
+void rml_enkf_init_update(void * arg ,
+ const bool_vector_type * ens_mask ,
+ const matrix_type * S ,
+ const matrix_type * R ,
+ const matrix_type * dObs ,
+ const matrix_type * E ,
+ const matrix_type * D ) {
+ rml_enkf_data_type * module_data = rml_enkf_data_safe_cast( arg );
+ bool_vector_memcpy( module_data->ens_mask , ens_mask );
+}
+
+
+
+bool rml_enkf_set_double( void * arg , const char * var_name , double value) {
+ rml_enkf_data_type * module_data = rml_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , ENKF_TRUNCATION_KEY_) == 0)
+ rml_enkf_set_truncation( module_data , value );
+ else if (strcmp( var_name , ENKF_LAMBDA0_KEY_) == 0)
+ rml_enkf_set_lambda0( module_data , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+
+bool rml_enkf_set_int( void * arg , const char * var_name , int value) {
+ rml_enkf_data_type * module_data = rml_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , ENKF_NCOMP_KEY_) == 0)
+ rml_enkf_set_subspace_dimension( module_data , value );
+ else if(strcmp( var_name , ENKF_ITER_KEY_) == 0)
+ rml_enkf_set_iteration_number( module_data , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+
+long rml_enkf_get_options( void * arg , long flag ) {
+ rml_enkf_data_type * module_data = rml_enkf_data_safe_cast( arg );
+ {
+ return module_data->option_flags;
+ }
+}
+
+
+
+ bool rml_enkf_has_var( const void * arg, const char * var_name) {
+ bool ret = false;
+
+ if ((strcmp(var_name , ENKF_ITER_KEY_) == 0) ||
+ (strcmp(var_name , ENKF_TRUNCATION_KEY_) == 0) ||
+ (strcmp(var_name , ENKF_LAMBDA0_KEY_) == 0)) {
+ ret = true;
+ }
+ return ret;
+ }
+
+
+
+
+ int rml_enkf_get_int( const void * arg, const char * var_name) {
+ const rml_enkf_data_type * module_data = rml_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , ENKF_ITER_KEY_) == 0)
+ return module_data->iteration_nr;
+ else
+ return -1;
+ }
+ }
+
+ double rml_enkf_get_double( const void * arg, const char * var_name) {
+ const rml_enkf_data_type * module_data = rml_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , ENKF_TRUNCATION_KEY_) == 0)
+ return module_data->truncation;
+ else if (strcmp(var_name , ENKF_LAMBDA0_KEY_) == 0)
+ return module_data->lambda0;
+ else
+ return -1.0;
+ }
+ }
+
+
+
+
+/**
+ gcc -fpic -c -I??
+ gcc -shared -o
+*/
+
+
+
+#ifdef INTERNAL_LINK
+#define SYMBOL_TABLE rml_enkf_symbol_table
+#else
+#define SYMBOL_TABLE EXTERNAL_MODULE_SYMBOL
+#endif
+
+
+analysis_table_type SYMBOL_TABLE = {
+ .alloc = rml_enkf_data_alloc,
+ .freef = rml_enkf_data_free,
+ .set_int = rml_enkf_set_int ,
+ .set_double = rml_enkf_set_double ,
+ .set_bool = NULL ,
+ .set_string = NULL ,
+ .get_options = rml_enkf_get_options ,
+ .initX = NULL,
+ .updateA = rml_enkf_updateA ,
+ .init_update = rml_enkf_init_update ,
+ .complete_update = NULL,
+ .has_var = rml_enkf_has_var,
+ .get_int = rml_enkf_get_int,
+ .get_double = rml_enkf_get_double,
+ .get_ptr = NULL,
+};
+
diff --git a/libres/lib/analysis/modules/ert_module b/libres/lib/analysis/modules/ert_module
new file mode 100755
index 00000000000..6ff548b2650
--- /dev/null
+++ b/libres/lib/analysis/modules/ert_module
@@ -0,0 +1,252 @@
+#!/usr/bin/env python
+import sys
+import os
+import os.path
+from optparse import OptionParser
+
+res_root = os.path.realpath(
+ os.path.join(os.path.dirname(os.path.realpath(os.path.abspath(__file__))), "../")
+)
+
+# -----------------------------------------------------------------
+
+default_lib_list = ["ecl", "res"]
+default_define_list = ["HAVE_PTHREAD", "COMPILER_GCC"]
+
+
+CFLAGS = "-std=gnu99 -O2 -Wall -fpic -g"
+LDFLAGS_list = ["-shared"]
+CC = "gcc"
+LD = CC
+
+# -----------------------------------------------------------------
+
+
+c_file = 0
+header_file = 1
+object_file = 2
+other = 3
+
+file_types = {".o": object_file, ".h": header_file, ".c": c_file}
+
+
+def base_name(file):
+ (name, ext) = os.path.split(file)
+ return name
+
+
+def file_type(file):
+ name, ext = os.path.splitext(file)
+ return file_types.get(ext, other)
+
+
+def object_file_name(file):
+ (name, ext) = os.path.splitext(file)
+ return "%s.o" % name
+
+
+def make_LDFLAGS(use_rpath, lib_path_list):
+ if use_rpath:
+ LDFLAGS_list.append("-Wl,--enable-new-dtags")
+ for path in lib_path_list:
+ LDFLAGS_list.append("-Wl,-rpath,%s" % path)
+ LDFLAGS_list.append("-Wl,-soname,")
+
+ return " ".join(LDFLAGS_list)
+
+
+def make_XFLAG(X, def_list):
+ FLAG = ""
+ for d in def_list:
+ FLAG += "-%s%s " % (X, d)
+ return FLAG
+
+
+def compile_file(file, IFLAG, DFLAG, verbose):
+ target = object_file_name(file)
+ if os.path.exists(target):
+ os.unlink(target)
+
+ cmd = "%s %s %s %s -c %s -o %s" % (CC, CFLAGS, IFLAG, DFLAG, file, target)
+ if verbose:
+ print "Compiling: %s" % cmd
+ os.system(cmd)
+ if os.path.exists(target):
+ return target
+ else:
+ sys.exit("Compile cmd:%s failed" % cmd)
+
+
+def link(soname, filename, object_list, LDFLAGS, LFLAG, lFLAG, verbose):
+ object_string = ""
+ for obj in object_list:
+ object_string += "%s " % obj
+
+ cmd = "%s %s%s -o %s %s %s %s" % (
+ LD,
+ LDFLAGS,
+ soname,
+ filename,
+ object_string,
+ LFLAG,
+ lFLAG,
+ )
+ if verbose:
+ print "Linking : %s" % cmd
+ if os.path.exists(filename):
+ os.unlink(filename)
+ os.system(cmd)
+ if os.path.exists(filename):
+ return True
+ else:
+ return False
+
+
+usage = """
+The ert_module script is a small convenience script to
+compile C source code into an analysis module which can
+be loaded by ert/libres. The script is controlled by commandline
+arguments:
+
+ 1. The first argument should be the name of the module
+ you are creating, an extension .so will be appended.
+
+ 2. List the source files you want to include, the
+ files should have extension .c. In addition you can
+ include object files which have been compiled by
+ other means, the object files should have
+ extension .o
+
+ 3. Optionally you can pass -I and -D options which are
+ passed to the compiler; and -l and -L options which
+ are passed to the linker.
+
+Example:
+
+ ert_module my_module my_src1.c my_src2.c f90_object1.o f90_object2.o -I/path -DFAST=Yes -L/path/to/lib -lfm -lz
+
+Will create a module 'my_module' based on the src files my_src1.c
+and my_src2.c; in addition the object files f90_object1.o and
+f90_object2.o will be included in the final module.
+
+-----------------------------------------------------------------
+
+To compile the module code you will typically need the include files
+and libraries from an existing libres installation. By default the
+ert_module script will locate the libres installation based on the
+location of the script, but you can pass the option:
+
+ --res-root=/path/where/libres/is/installed
+
+The --res-root option should point to a directory containing the
+lib64/ and include/ directories of a binary etr distribution. In
+addition to --res-root you can use the normal -L/path/to/lib option to
+send in additional link path arguments.
+
+By default the path to shared libraries will not be embedded in the
+resulting module, but by passing the option --use-rpath you can tell
+the script to embed these paths in the final shared object.
+
+-----------------------------------------------------------------
+
+Options summary:
+
+ -L/path/to/lib: Include the path /path/to/lib in the linker path
+
+ -llib1 : Link with the library lib1
+
+ -I/include : Include the path /include in the compiler include path.
+
+ --res-root=/path/to/libres : Use this is as root for libres headers
+ and libraries. [Default: inferred from location of script]
+
+ --use-rpath : Embed library paths in shared objects. Default off.
+
+ --exclude-res: Do not use any default libraries or headers from libres
+
+
+Default flags:
+
+Compile: %s %s %s
+Link: %s %s %s
+""" % (
+ CC,
+ make_XFLAG("I", ["./", "%s/include" % res_root]),
+ make_XFLAG("D", default_define_list),
+ LD,
+ make_XFLAG("L", ["%s/lib64" % res_root]),
+ make_XFLAG("l", default_lib_list),
+)
+
+parser = OptionParser(usage)
+parser.add_option("--res-root", dest="res_root", action="store")
+parser.add_option("-I", dest="include_path_list", action="append", default=[])
+parser.add_option("-D", dest="define_list", action="append", default=[])
+parser.add_option("-L", dest="lib_path_list", action="append", default=[])
+parser.add_option("-l", dest="lib_list", action="append", default=[])
+parser.add_option(
+ "--exclude-res", dest="exclude_res", action="store_true", default=False
+)
+parser.add_option("--use-rpath", dest="use_rpath", action="store_true", default=False)
+parser.add_option("--silent", dest="silent", action="store_true", default=False)
+
+(options, args) = parser.parse_args()
+if len(args) == 0:
+ sys.exit(usage)
+
+if options.res_root:
+ res_root = options.res_root
+
+if options.exclude_res:
+ default_include_path_list = ["./"]
+ default_lib_path_list = []
+ default_lib_list = []
+else:
+ default_include_path_list = ["./", "%s/include" % res_root]
+ default_lib_path_list = ["%s/lib64" % res_root]
+ default_lib_list = default_lib_list
+
+
+# What is supplied as commandline options should take presedence, this
+# implies that it should be first OR last on the commandline.
+include_path_list = options.include_path_list + default_include_path_list
+define_list = default_define_list + options.define_list
+lib_list = options.lib_list + default_lib_list
+lib_path_list = options.lib_path_list + default_lib_path_list
+
+
+verbose = not options.silent
+LDFLAGS = make_LDFLAGS(options.use_rpath, lib_path_list)
+input_name = args[0]
+(path, tmp) = os.path.split(input_name)
+(module, ext) = os.path.splitext(tmp)
+
+soname = "%s.so" % module
+if path:
+ filename = "%s/%s.so" % (path, module)
+ if not os.path.exists(path):
+ os.makedirs(path)
+else:
+ filename = "%s.so" % module
+
+# -----------------------------------------------------------------
+
+IFLAG = make_XFLAG("I", include_path_list)
+DFLAG = make_XFLAG("D", define_list)
+LFLAG = make_XFLAG("L", lib_path_list)
+lFLAG = make_XFLAG("l", lib_list)
+
+object_list = []
+for arg in args[1:]:
+ if file_type(arg) == c_file:
+ object_list.append(compile_file(arg, IFLAG, DFLAG, verbose))
+ elif file_type(arg) == object_file:
+ object_list.append(arg)
+ else:
+ print "** Warning: ignoring file:%s" % arg
+
+
+if link(soname, filename, object_list, LDFLAGS, LFLAG, lFLAG, verbose):
+ sys.exit()
+else:
+ sys.exit("Creating library failed")
diff --git a/libres/lib/analysis/modules/ies_enkf.c b/libres/lib/analysis/modules/ies_enkf.c
new file mode 100644
index 00000000000..37992049441
--- /dev/null
+++ b/libres/lib/analysis/modules/ies_enkf.c
@@ -0,0 +1,970 @@
+/*
+ Copyright (C) 2019 Equinor ASA, Norway.
+
+ The file 'ies_enkf.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+
+#include
+#include
+#include
+
+#include "ies_enkf_config.h"
+#include "ies_enkf_data.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/***************************************************************************************************************/
+/* ies_enkf function headers */
+/***************************************************************************************************************/
+void ies_enkf_linalg_extract_active(const ies_enkf_data_type * data,
+ matrix_type * E,
+ FILE * log_fp,
+ bool dbg);
+
+void ies_enkf_linalg_compute_AA_projection(const matrix_type * A,
+ matrix_type * Y,
+ FILE * log_fp,
+ bool dbg);
+
+void ies_enkf_linalg_extract_active_W(const ies_enkf_data_type * data,
+ matrix_type * W0,
+ FILE * log_fp,
+ bool dbg);
+
+void ies_linalg_solve_S(const matrix_type * W0,
+ const matrix_type * Y,
+ matrix_type * S,
+ double rcond,
+ FILE * log_fp,
+ bool dbg);
+
+void ies_enkf_linalg_subspace_inversion(matrix_type * W0,
+ const int ies_inversion,
+ matrix_type * E,
+ matrix_type * R,
+ const matrix_type * S,
+ const matrix_type * H,
+ double truncation,
+ double ies_steplength,
+ int subspace_dimension,
+ FILE * log_fp,
+ bool dbg);
+
+void ies_enkf_linalg_exact_inversion(matrix_type * W0,
+ const int ies_inversion,
+ const matrix_type * S,
+ const matrix_type * H,
+ double ies_steplength,
+ FILE * log_fp,
+ bool dbg);
+
+void ies_enkf_linalg_store_active_W(ies_enkf_data_type * data,
+ const matrix_type * W0,
+ FILE * log_fp,
+ bool dbg);
+
+void ies_enkf_linalg_extract_active_A0(const ies_enkf_data_type * data,
+ matrix_type * A0,
+ FILE * log_fp,
+ bool dbg);
+/***************************************************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#define ENKF_SUBSPACE_DIMENSION_KEY "ENKF_SUBSPACE_DIMENSION"
+#define ENKF_TRUNCATION_KEY "ENKF_TRUNCATION"
+#define IES_MAX_STEPLENGTH_KEY "IES_MAX_STEPLENGTH"
+#define IES_MIN_STEPLENGTH_KEY "IES_MIN_STEPLENGTH"
+#define IES_DEC_STEPLENGTH_KEY "IES_DEC_STEPLENGTH"
+#define ITER_KEY "ITER"
+
+#define IES_SUBSPACE_KEY "IES_SUBSPACE"
+#define IES_INVERSION_KEY "IES_INVERSION"
+#define IES_LOGFILE_KEY "IES_LOGFILE"
+#define IES_DEBUG_KEY "IES_DEBUG"
+#define IES_AAPROJECTION_KEY "IES_AAPROJECTION"
+
+
+#include "tecplot.c"
+
+//#define DEFAULT_ANALYSIS_SCALE_DATA true
+
+
+static void printf_mask(FILE * log_fp, const char * name, const bool_vector_type * mask) {
+ fprintf(log_fp,"%10s: ",name);
+ for (int i = 0; i < bool_vector_size(mask); i++){
+ fprintf(log_fp,"%d", bool_vector_iget(mask, i));
+ if ((i+1)%10 == 0) fprintf(log_fp,"%s"," ");
+ if ((i+1)%100 == 0) fprintf(log_fp,"\n %9d " ,i+1);
+ }
+ fputs("\n", log_fp);
+}
+
+
+/***************************************************************************************************************
+* Set / Get iteration number
+****************************************************************************************************************/
+/*
+ * -------------------------------------------------------------------------------------------------------------
+ * I E n K S
+ * IEnKS initialization (getting the obs_mask and ens_mask for active observations and realizations)
+ * -------------------------------------------------------------------------------------------------------------
+*/
+void ies_enkf_init_update(void * arg ,
+ const bool_vector_type * ens_mask ,
+ const bool_vector_type * obs_mask ,
+ const matrix_type * S ,
+ const matrix_type * R ,
+ const matrix_type * dObs ,
+ const matrix_type * E ,
+ const matrix_type * D,
+ rng_type * rng) {
+ ies_enkf_data_type * module_data = ies_enkf_data_safe_cast( arg );
+
+/* Store current ens_mask in module_data->ens_mask for each iteration */
+ ies_enkf_data_update_ens_mask(module_data, ens_mask);
+
+/* Store obs_mask for initial iteration in module_data->obs_mask0,
+* for each subsequent iteration we store the current mask in module_data->obs_mask */
+ ies_enkf_store_initial_obs_mask(module_data, obs_mask);
+ ies_enkf_update_obs_mask(module_data, obs_mask);
+}
+
+
+
+/*
+ * -------------------------------------------------------------------------------------------------------------
+ * I E n K S
+ * IEnKS update (IES searching the solution in ensemble subspace)
+ * -------------------------------------------------------------------------------------------------------------
+*/
+
+void ies_enkf_updateA( void * module_data,
+ matrix_type * A , // Updated ensemble A retured to ERT.
+ const matrix_type * Yin , // Ensemble of predicted measurements
+ const matrix_type * Rin , // Measurement error covariance matrix (not used)
+ const matrix_type * dObs , // Actual observations (not used)
+ const matrix_type * Ein , // Ensemble of observation perturbations
+ const matrix_type * Din , // (d+E-Y) Ensemble of perturbed observations - Y
+ const module_info_type * module_info,
+ rng_type * rng) {
+
+ ies_enkf_data_type * data = ies_enkf_data_safe_cast( module_data );
+ const ies_enkf_config_type * ies_config = ies_enkf_data_get_config( data );
+
+
+ int nrobs_msk =ies_enkf_data_get_obs_mask_size(data); // Total number of observations
+ int nrobs_inp =matrix_get_rows( Yin ); // Number of active observations input in current iteration
+
+
+ int ens_size_msk = ies_enkf_data_get_ens_mask_size(data); // Total number of realizations
+ int ens_size = matrix_get_columns( Yin ); // Number of active realizations in current iteration
+ int state_size = matrix_get_rows( A );
+
+ ies_inversion_type ies_inversion = ies_enkf_config_get_ies_inversion( ies_config );
+ double truncation = ies_enkf_config_get_truncation( ies_config );
+ bool ies_debug = ies_enkf_config_get_ies_debug(ies_config);
+ int subspace_dimension = ies_enkf_config_get_enkf_subspace_dimension( ies_config );
+ double rcond=0;
+ int nrsing=0;
+ int iteration_nr = ies_enkf_data_inc_iteration_nr(data);
+ FILE * log_fp;
+
+ ies_enkf_data_update_state_size( data, state_size );
+
+
+ double ies_max_step=ies_enkf_config_get_ies_max_steplength(ies_config);
+ double ies_min_step=ies_enkf_config_get_ies_min_steplength(ies_config);
+ double ies_decline_step=ies_enkf_config_get_ies_dec_steplength(ies_config);
+ if (ies_decline_step < 1.1)
+ ies_decline_step=1.1;
+ double ies_steplength=ies_min_step + (ies_max_step - ies_min_step)*pow(2,-(iteration_nr-1)/(ies_decline_step-1));
+
+ log_fp = ies_enkf_data_open_log(data);
+
+ fprintf(log_fp,"\n\n\n***********************************************************************\n");
+ fprintf(log_fp,"IES Iteration = %d\n", iteration_nr);
+ fprintf(log_fp,"----ies_steplength = %f --- a=%f b=%f c=%f\n", ies_steplength, ies_max_step, ies_min_step, ies_decline_step);
+ fprintf(log_fp,"----ies_inversion = %d\n", ies_inversion);
+ fprintf(log_fp,"----ies_debug = %d\n", ies_debug);
+ fprintf(log_fp,"----truncation = %f %d\n", truncation, subspace_dimension);
+ bool dbg = ies_enkf_config_get_ies_debug( ies_config ) ;
+
+/***************************************************************************************************************/
+/* Counting number of active observations for current iteration.
+ If the observations have been used in previous iterations they are contained in data->E0.
+ If they are introduced in the current iteration they will be augmented to data->E. */
+ ies_enkf_data_store_initialE(data, Ein);
+ ies_enkf_data_augment_initialE(data, Ein);
+
+ double nsc = 1.0/sqrt(ens_size - 1.0);
+
+/* dimensions for printing */
+ int m_nrobs = util_int_min(nrobs_inp -1,7);
+ int m_ens_size = util_int_min(ens_size -1,16);
+ int m_state_size = util_int_min(state_size-1,3);
+
+/***************************************************************************************************************/
+ ies_enkf_data_allocateW(data, ens_size_msk);
+ ies_enkf_data_store_initialA(data, A);
+
+
+/***************************************************************************************************************/
+ fprintf(log_fp,"----active ens_size = %d, total ens_size_msk = %d\n", ens_size,ens_size_msk);
+ fprintf(log_fp,"----active nrobs_inp = %d, total nrobs_msk= %d\n", nrobs_inp, nrobs_msk );
+ fprintf(log_fp,"----active state_size= %d\n", state_size );
+
+
+
+/***************************************************************************************************************/
+/* Print initial observation mask */
+ printf_mask(log_fp, "obsmask_0:", ies_enkf_data_get_obs_mask0(data));
+
+/***************************************************************************************************************/
+/* Print Current observation mask */
+ printf_mask(log_fp, "obsmask_i:", ies_enkf_data_get_obs_mask(data));
+
+/***************************************************************************************************************/
+/* Print Current ensemble mask */
+ printf_mask(log_fp, "ensmask_i:", ies_enkf_data_get_ens_mask(data));
+
+/***************************************************************************************************************
+* Re-structure input matrices according to new active obs_mask and ens_size.
+* Allocates the local matrices to be used.
+* Copies the initial measurement perturbations for the active observations into the current E matrix.
+* Copies the inputs in D, Y and R into their local representations
+*/
+ matrix_type * Y = matrix_alloc_copy( Yin );
+ matrix_type * E = matrix_alloc( nrobs_inp, ens_size );
+ matrix_type * D = matrix_alloc_copy( Din );
+ matrix_type * R = matrix_alloc_copy( Rin );
+
+/* Subtract new measurement perturbations D=D-E */
+ matrix_inplace_sub(D,Ein);
+
+/* Extract active observations and realizations for D, E, Y, and R */
+ ies_enkf_linalg_extract_active(data, E, log_fp, dbg);
+
+/* Add old measurement perturbations */
+ matrix_inplace_add(D,E);
+
+ matrix_type * A0 = matrix_alloc( state_size, ens_size ); // Temporary ensemble matrix
+ matrix_type * W0 = matrix_alloc( ens_size , ens_size ); // Coefficient matrix
+ matrix_type * W = matrix_alloc( ens_size , ens_size ); // Coefficient matrix
+ matrix_type * H = matrix_alloc( nrobs_inp, ens_size ); // Innovation vector "H= S*W+D-Y"
+ matrix_type * S = matrix_alloc( nrobs_inp, ens_size ); // Predicted ensemble anomalies scaled with inv(Omeaga)
+ matrix_type * X = matrix_alloc( ens_size, ens_size ); // Final transform matrix
+
+ if (dbg) matrix_pretty_fprint_submat(A,"Ain","%11.5f",log_fp,0,m_state_size,0,m_ens_size) ;
+ if (dbg) fprintf(log_fp,"Computed matrices\n");
+
+/***************************************************************************************************************
+* Subtract mean of predictions to generate predicted ensemble anomaly matrix (Line 5)
+*/
+ matrix_subtract_row_mean( Y ); // Y=Y*(I-(1/ens_size)*11)
+ matrix_scale(Y,nsc); // Y=Y / sqrt(ens_size-1)
+ if (dbg) matrix_pretty_fprint_submat(Y,"Y","%11.5f",log_fp,0,m_nrobs,0,m_ens_size) ;
+
+
+/***************************************************************************************************************
+* COMPUTING THE PROJECTION Y= Y * (Ai^+ * Ai) (only used when state_size < ens_size-1) */
+ if (ies_enkf_config_get_ies_aaprojection(ies_config) && (state_size <= (ens_size - 1))) {
+ ies_enkf_linalg_compute_AA_projection(A, Y, log_fp, dbg);
+ }
+
+/***************************************************************************************************************
+* COPY ACTIVE REALIZATIONS FROM data->W to W0 */
+ ies_enkf_linalg_extract_active_W(data, W0, log_fp, dbg);
+
+/***************************************************************************************************************
+* When solving the system S = Y inv(Omega) we write (line 6)
+* Omega^T S^T = Y^T
+*/
+ ies_linalg_solve_S(W0, Y, S, rcond, log_fp, dbg);
+
+/***************************************************************************************************************
+* INNOVATION H = S*W + D - Y from Eq. (47) (Line 8) */
+ matrix_assign(H,D) ; // H=D=dobs + E - Y
+ matrix_dgemm(H,S,W0,false,false,1.0,1.0); // H=S*W + H
+
+ if (dbg) matrix_pretty_fprint_submat(H,"H","%11.5f",log_fp,0,m_nrobs,0,m_ens_size) ;
+
+/* Store previous W for convergence test */
+ matrix_assign(W,W0);
+
+/***************************************************************************************************************
+* COMPUTE NEW UPDATED W (Line 9)
+* W = W + ies_steplength * ( W - S'*(S*S'+R)^{-1} H ) (a)
+* which in the case when R=I can be rewritten as
+* W = W + ies_steplength * ( W - (S'*S + I)^{-1} * S' * H ) (b)
+*
+* With R=I the subspace inversion (ies_inversion=1) solving Eq. (a) with singular value
+* trucation=1.000 gives exactly the same solution as the exact inversion (ies_inversion=0).
+*
+* Using ies_inversion=IES_INVERSION_SUBSPACE_EXACT_R(2), and a step length of 1.0,
+* one update gives identical result to ENKF_STD as long as the same SVD
+* truncation is used.
+*
+* With very large data sets it is likely that the inversion becomes poorly
+* conditioned and a trucation=1.000 is not a good choice. In this case the
+* ies_inversion > 0 and EnKF_truncation set to 0.99 or so, should stabelize
+* the algorithm.
+*
+* Using ies_inversion=IES_INVERSION_SUBSPACE_EE_R(3) and
+* ies_inversion=IES_INVERSION_SUBSPACE_RE(2) gives identical results but
+* ies_inversion=IES_INVERSION_SUBSPACE_RE is much faster (N^2m) than
+* ies_inversion=IES_INVERSION_SUBSPACE_EE_R (Nm^2).
+
+ See the enum: ies_inverson in ies_enkf_config.h:
+
+ ies_inversion=IES_INVERSION_EXACT(0) -> exact inversion from (b) with exact R=I
+ ies_inversion=IES_INVERSION_SUBSPACE_EXACT_R(1) -> subspace inversion from (a) with exact R
+ ies_inversion=IES_INVERSION_SUBSPACE_EE_R(2) -> subspace inversion from (a) with R=EE
+ ies_inversion=IES_INVERSION_SUBSPACE_RE(3) -> subspace inversion from (a) with R represented by E
+*/
+ if (ies_inversion != IES_INVERSION_EXACT){
+ fprintf(log_fp,"Subspace inversion. (ies_inversion=%d)\n",ies_inversion);
+ ies_enkf_linalg_subspace_inversion(W0, ies_inversion, E, R, S, H, truncation, ies_steplength, subspace_dimension, log_fp, dbg);
+ } else if (ies_inversion == IES_INVERSION_EXACT) {
+ fprintf(log_fp,"Exact inversion using diagonal R=I. (ies_inversion=%d)\n",ies_inversion);
+ ies_enkf_linalg_exact_inversion( W0, ies_inversion, S, H, ies_steplength, log_fp, dbg);
+ }
+
+ if (dbg) matrix_pretty_fprint_submat(W0,"Updated W","%11.5f",log_fp,0,m_ens_size,0,m_ens_size) ;
+
+/* Store active realizations from W0 to data->W */
+ ies_enkf_linalg_store_active_W(data, W0, log_fp, dbg);
+
+/***************************************************************************************************************
+* CONSTRUCT TRANFORM MATRIX X FOR CURRENT ITERATION (Line 10)
+* X= I + W/sqrt(N-1) */
+ matrix_assign(X,W0);
+ matrix_scale(X,nsc);
+ for (int i = 0; i < ens_size; i++){
+ matrix_iadd(X,i,i,1.0);
+ }
+ if (dbg) matrix_pretty_fprint_submat(X,"X","%11.5f",log_fp,0,m_ens_size-1,0,m_ens_size);
+
+/***************************************************************************************************************
+* COMPUTE NEW ENSEMBLE SOLUTION FOR CURRENT ITERATION Ei=A0*X (Line 11) */
+ matrix_pretty_fprint_submat(A,"A^f","%11.5f",log_fp,0,m_state_size,0,m_ens_size);
+ ies_enkf_linalg_extract_active_A0(data, A0, log_fp, dbg);
+ matrix_matmul(A,A0,X);
+ matrix_pretty_fprint_submat(A,"A^a","%11.5f",log_fp,0,m_state_size,0,m_ens_size);
+
+/***************************************************************************************************************
+* COMPUTE ||W0 - W|| AND EVALUATE COST FUNCTION FOR PREVIOUS ITERATE (Line 12) */
+ matrix_type * DW = matrix_alloc( ens_size , ens_size );
+ matrix_sub(DW,W0,W);
+ teclog(W,D,DW,"iesteclog.dat",ens_size,iteration_nr, rcond, nrsing, nrobs_inp);
+ teccost(W,D,"costf.dat",ens_size,iteration_nr);
+
+/* DONE *********************************************************************************************************/
+
+ ies_enkf_data_fclose_log(data);
+
+ matrix_free( Y );
+ matrix_free( D );
+ matrix_free( E );
+ matrix_free( R );
+ matrix_free( A0 );
+ matrix_free( W0 );
+ matrix_free( W );
+ matrix_free( DW );
+ matrix_free( H );
+ matrix_free( S );
+ matrix_free( X );
+}
+
+
+/***************************************************************************************************************/
+/* ies_enkf linalg functions */
+/***************************************************************************************************************/
+/* Extract active observations and realizations for D, E, Y, and R.
+* IES works from an initial set of active realizations and measurements.
+* In the first iteration we store the measurement perturbations from the initially active observations.
+* In the subsequent iterations we include the new observations that may be introduced by ERT including their new
+* measurement perturbations, and we reuse the intially stored measurement perturbations.
+* Additionally we may loose realizations during the iteratitions, and we extract measurement perturbations for
+* only the active realizations.
+*/
+void ies_enkf_linalg_extract_active(const ies_enkf_data_type * data,
+ matrix_type * E,
+ FILE * log_fp,
+ bool dbg){
+
+ const bool_vector_type * obs_mask = ies_enkf_data_get_obs_mask(data);
+ const bool_vector_type * ens_mask = ies_enkf_data_get_ens_mask(data);
+
+ int obs_size_msk = ies_enkf_data_get_obs_mask_size(data); // Total number of observations
+ int ens_size_msk = ies_enkf_data_get_ens_mask_size(data); // Total number of realizations
+
+ const matrix_type * dataE = ies_enkf_data_getE(data);
+
+ int m=-1; // counter for currently active measurements
+ for (int iobs = 0; iobs < obs_size_msk; iobs++){
+ if ( bool_vector_iget(obs_mask,iobs) ) {
+ m++;
+ fprintf(log_fp,"ies_enkf_linalg_extract_active iobs=%d m=%d)\n",iobs,m);
+ int i=-1;
+ for (int iens = 0; iens < ens_size_msk; iens++){
+ if ( bool_vector_iget(ens_mask,iens) ){
+ i++;
+ matrix_iset_safe(E,m,i,matrix_iget(dataE,iobs,iens)) ;
+ }
+ }
+ }
+ }
+
+ if (dbg) {
+ int m_nrobs = util_int_min(matrix_get_rows( E ) -1,7);
+ int m_ens_size = util_int_min(matrix_get_columns( E )-1,16);
+ matrix_pretty_fprint_submat(E,"E","%11.5f",log_fp,0,m_nrobs,0,m_ens_size) ;
+ }
+
+}
+
+
+
+/* COMPUTING THE PROJECTION Y= Y * (Ai^+ * Ai) (only used when state_size < ens_size-1) */
+void ies_enkf_linalg_compute_AA_projection(const matrix_type * A,
+ matrix_type * Y,
+ FILE * log_fp,
+ bool dbg){
+ int ens_size = matrix_get_columns( A );
+ int state_size = matrix_get_rows( A );
+ int nrobs = matrix_get_rows( Y );
+
+ int m_nrobs = util_int_min(nrobs -1,7);
+ int m_ens_size = util_int_min(ens_size -1,16);
+ int m_state_size = util_int_min(state_size-1,3);
+
+ fprintf(log_fp,"Activating AAi projection for Y\n");
+ double * eig = (double*)util_calloc( ens_size , sizeof * eig);
+ matrix_type * Ai = matrix_alloc_copy( A );
+ matrix_type * AAi = matrix_alloc( ens_size, ens_size );
+ matrix_subtract_row_mean(Ai);
+ matrix_type * VT = matrix_alloc( state_size, ens_size );
+ matrix_dgesvd(DGESVD_NONE , DGESVD_MIN_RETURN , Ai , eig , NULL , VT);
+ if (dbg) matrix_pretty_fprint_submat(VT,"VT","%11.5f",log_fp,0,m_state_size-1,0,m_ens_size) ;
+ matrix_dgemm(AAi,VT,VT,true,false,1.0,0.0);
+ if (dbg) matrix_pretty_fprint_submat(AAi,"AAi","%11.5f",log_fp,0,m_ens_size-1,0,m_ens_size);
+ matrix_inplace_matmul(Y,AAi);
+ matrix_free(Ai);
+ matrix_free(AAi);
+ matrix_free(VT);
+ free(eig);
+ if (dbg) matrix_pretty_fprint_submat(Y,"Yprojected","%11.5f",log_fp,0,m_nrobs,0,m_ens_size) ;
+}
+
+
+
+/*
+* W is stored for each iteration in data->W. If we loose realizations we copy only the active rows and cols from
+* data->W to W0 which is then used in the algorithm.
+*/
+void ies_enkf_linalg_extract_active_W(const ies_enkf_data_type * data,
+ matrix_type * W0,
+ FILE * log_fp,
+ bool dbg){
+
+ const bool_vector_type * ens_mask = ies_enkf_data_get_ens_mask(data);
+ const matrix_type * dataW = ies_enkf_data_getW(data);
+ int ens_size_msk = ies_enkf_data_get_ens_mask_size(data);
+ int ens_size = matrix_get_columns( W0 );
+ int m_ens_size = util_int_min(ens_size -1,16);
+ int i=-1;
+ int j;
+ for (int iens=0; iens < ens_size_msk; iens++){
+ if ( bool_vector_iget(ens_mask,iens) ){
+ i=i+1;
+ j=-1;
+ for (int jens=0; jens < ens_size_msk; jens++){
+ if ( bool_vector_iget(ens_mask,jens) ){
+ j=j+1;
+ matrix_iset_safe(W0,i,j,matrix_iget(dataW,iens,jens)) ;
+ }
+ }
+ }
+ }
+
+ if (ens_size_msk == ens_size){
+ fprintf(log_fp,"data->W copied exactly to W0: %d\n",matrix_equal(dataW,W0)) ;
+ }
+
+ if (dbg) matrix_pretty_fprint_submat(dataW,"data->W","%11.5f",log_fp,0,m_ens_size-1,0,m_ens_size);
+ if (dbg) matrix_pretty_fprint_submat(W0,"W0","%11.5f",log_fp,0,m_ens_size-1,0,m_ens_size);
+}
+
+/*
+* COMPUTE Omega= I + W (I-11'/sqrt(ens_size)) from Eq. (36). (Line 6)
+* When solving the system S = Y inv(Omega) we write
+* Omega^T S^T = Y^T
+*/
+void ies_linalg_solve_S(const matrix_type * W0,
+ const matrix_type * Y,
+ matrix_type * S,
+ double rcond,
+ FILE * log_fp,
+ bool dbg){
+ int ens_size = matrix_get_columns( W0 );
+ int nrobs = matrix_get_rows( S );
+ int m_ens_size = util_int_min(ens_size -1,16);
+ int m_nrobs = util_int_min(nrobs -1,7);
+ double nsc = 1.0/sqrt(ens_size - 1.0);
+
+ matrix_type * YT = matrix_alloc( ens_size, nrobs ); // Y^T used in linear solver
+ matrix_type * ST = matrix_alloc( ens_size, nrobs ); // current S^T used in linear solver
+ matrix_type * Omega = matrix_alloc( ens_size, ens_size ); // current S^T used in linear solver
+
+/* Here we compute the W (I-11'/N) / sqrt(N-1) and transpose it).*/
+ matrix_assign(Omega,W0) ; // Omega=data->W (from previous iteration used to solve for S)
+ matrix_subtract_row_mean(Omega); // Omega=Omega*(I-(1/N)*11')
+ matrix_scale(Omega,nsc); // Omega/sqrt(N-1)
+ matrix_inplace_transpose(Omega); // Omega=transpose(Omega)
+ for (int i = 0; i < ens_size; i++){ // Omega=Omega+I
+ matrix_iadd(Omega,i,i,1.0);
+ }
+ if (dbg) matrix_pretty_fprint_submat(Omega,"OmegaT","%11.5f",log_fp,0,m_ens_size,0,m_ens_size) ;
+ matrix_transpose(Y,YT); // RHS stored in YT
+
+/* Solve system (Line 7) */
+ fprintf(log_fp,"Solving Omega' S' = Y' using LU factorization:\n");
+ matrix_dgesvx(Omega,YT,&rcond);
+ fprintf(log_fp,"dgesvx condition number= %12.5e\n",rcond);
+
+ matrix_transpose(YT,S); // Copy solution to S
+
+ if (dbg) matrix_pretty_fprint_submat(S,"S","%11.5f",log_fp,0,m_nrobs,0,m_ens_size) ;
+ matrix_free(Omega);
+ matrix_free(ST);
+ matrix_free(YT);
+}
+
+
+/*
+* The standard inversion works on the equation
+* S'*(S*S'+R)^{-1} H (a)
+*/
+void ies_enkf_linalg_subspace_inversion(matrix_type * W0,
+ const int ies_inversion,
+ matrix_type * E,
+ matrix_type * R,
+ const matrix_type * S,
+ const matrix_type * H,
+ double truncation,
+ double ies_steplength,
+ int subspace_dimension,
+ FILE * log_fp,
+ bool dbg){
+
+ int ens_size = matrix_get_columns( S );
+ int m_ens_size = util_int_min(ens_size -1,16);
+ int nrobs = matrix_get_rows( S );
+ int m_nrobs = util_int_min(nrobs -1,7);
+ int nrmin = util_int_min( ens_size , nrobs);
+ double nsc = 1.0/sqrt(ens_size - 1.0);
+ matrix_type * X1 = matrix_alloc( nrobs , nrmin ); // Used in subspace inversion
+ matrix_type * X3 = matrix_alloc( nrobs , ens_size ); // Used in subspace inversion
+ double * eig = (double*)util_calloc( ens_size , sizeof * eig);
+
+ fprintf(log_fp,"Subspace inversion. (ies_inversion=%d)\n",ies_inversion);
+
+ if (ies_inversion == IES_INVERSION_SUBSPACE_RE){
+ fprintf(log_fp,"Subspace inversion using E to represent errors. (ies_inversion=%d)\n",ies_inversion);
+ matrix_scale(E,nsc);
+ enkf_linalg_lowrankE( S , E , X1 , eig , truncation , subspace_dimension);
+ } else if (ies_inversion == IES_INVERSION_SUBSPACE_EE_R){
+ fprintf(log_fp,"Subspace inversion using ensemble generated full R=EE. (ies_inversion=%d)'\n",ies_inversion);
+ matrix_scale(E,nsc);
+ matrix_type * Et = matrix_alloc_transpose( E );
+ matrix_type * Cee = matrix_alloc_matmul( E , Et );
+ matrix_scale(Cee,nsc*nsc); // since enkf_linalg_lowrankCinv solves (SS' + (N-1) R)^{-1}
+ if (dbg) matrix_pretty_fprint_submat(Cee,"Cee","%11.5f",log_fp,0,m_nrobs,0,m_nrobs) ;
+ enkf_linalg_lowrankCinv( S , Cee , X1 , eig , truncation , subspace_dimension);
+ matrix_free( Et );
+ matrix_free( Cee );
+ } else if (ies_inversion == IES_INVERSION_SUBSPACE_EXACT_R){
+ fprintf(log_fp,"Subspace inversion using 'exact' full R. (ies_inversion=%d)\n",ies_inversion);
+ matrix_scale(R,nsc*nsc); // since enkf_linalg_lowrankCinv solves (SS' + (N-1) R)^{-1}
+ if (dbg) matrix_pretty_fprint_submat(R,"R","%11.5f",log_fp,0,m_nrobs,0,m_nrobs) ;
+ enkf_linalg_lowrankCinv( S , R , X1 , eig , truncation , subspace_dimension);
+ }
+
+ int nrsing=0;
+ fprintf(log_fp,"\nEig:\n");
+ for (int i=0;iW = (1-ies_steplength) * data->W + ies_steplength * S' * X3 (Line 9) */
+ matrix_dgemm(W0 , S , X3 , true , false , ies_steplength , 1.0-ies_steplength);
+
+ matrix_free( X1 );
+ matrix_free( X3 );
+ free(eig);
+}
+
+/*
+* The standard inversion works on the equation
+* S'*(S*S'+R)^{-1} H (a)
+* which in the case when R=I can be rewritten as
+* (S'*S + I)^{-1} * S' * H (b)
+*/
+void ies_enkf_linalg_exact_inversion(matrix_type * W0,
+ const int ies_inversion,
+ const matrix_type * S,
+ const matrix_type * H,
+ double ies_steplength,
+ FILE * log_fp,
+ bool dbg){
+ int ens_size = matrix_get_columns( S );
+
+ fprintf(log_fp,"Exact inversion using diagonal R=I. (ies_inversion=%d)\n",ies_inversion);
+ matrix_type * Z = matrix_alloc( ens_size , ens_size ); // Eigen vectors of S'S+I
+ matrix_type * ZtStH = matrix_alloc( ens_size , ens_size );
+ matrix_type * StH = matrix_alloc( ens_size , ens_size );
+ matrix_type * StS = matrix_alloc( ens_size , ens_size );
+ double * eig = (double*)util_calloc( ens_size , sizeof * eig);
+
+ matrix_diag_set_scalar(StS,1.0);
+ matrix_dgemm(StS,S,S,true,false,1.0,1.0);
+ matrix_dgesvd(DGESVD_ALL , DGESVD_NONE , StS , eig , Z , NULL);
+
+ matrix_dgemm(StH,S,H,true,false,1.0,0.0);
+ matrix_dgemm(ZtStH,Z,StH,true,false,1.0,0.0);
+
+ for (int i=0;iW = (1-ies_steplength) * data->W + ies_steplength * Z * (Lamda^{-1}) Z' S' H (Line 9) */
+ matrix_dgemm(W0 , Z , ZtStH , false , false , ies_steplength , 1.0-ies_steplength);
+
+ matrix_free(Z);
+ matrix_free(ZtStH);
+ matrix_free(StH);
+ matrix_free(StS);
+ free(eig);
+}
+
+
+/*
+* the updated W is stored for each iteration in data->W. If we have lost realizations we copy only the active rows and cols from
+* W0 to data->W which is then used in the algorithm. (note the definition of the pointer dataW to data->W)
+*/
+void ies_enkf_linalg_store_active_W(ies_enkf_data_type * data,
+ const matrix_type * W0,
+ FILE * log_fp,
+ bool dbg){
+ int ens_size_msk = ies_enkf_data_get_ens_mask_size(data);
+ int ens_size = matrix_get_columns( W0 );
+ int i=0;
+ int j;
+ matrix_type * dataW = ies_enkf_data_getW(data);
+ const bool_vector_type * ens_mask = ies_enkf_data_get_ens_mask(data);
+ matrix_set(dataW , 0.0) ;
+ for (int iens=0; iens < ens_size_msk; iens++){
+ if ( bool_vector_iget(ens_mask,iens) ){
+ j=0;
+ for (int jens=0; jens < ens_size_msk; jens++){
+ if ( bool_vector_iget(ens_mask,jens) ){
+ matrix_iset_safe(dataW,iens,jens,matrix_iget(W0,i,j));
+ j += 1;
+ }
+ }
+ i += 1;
+ }
+ }
+
+ if (ens_size_msk == ens_size){
+ fprintf(log_fp,"W0 copied exactly to data->W: %d\n",matrix_equal(dataW,W0)) ;
+ }
+}
+
+/*
+* Extract active realizations from the initially stored ensemble.
+*/
+void ies_enkf_linalg_extract_active_A0(const ies_enkf_data_type * data,
+ matrix_type * A0,
+ FILE * log_fp,
+ bool dbg){
+ int ens_size_msk = ies_enkf_data_get_ens_mask_size(data);
+ int ens_size = matrix_get_columns( A0 );
+ int state_size = matrix_get_rows( A0 );
+ int m_ens_size = util_int_min(ens_size -1,16);
+ int m_state_size = util_int_min(state_size-1,3);
+ int i=-1;
+ const bool_vector_type * ens_mask = ies_enkf_data_get_ens_mask(data);
+ const matrix_type * dataA0 = ies_enkf_data_getA0(data);
+ matrix_pretty_fprint_submat(dataA0,"data->A0","%11.5f",log_fp,0,m_state_size,0,m_ens_size);
+ for (int iens=0; iens < ens_size_msk; iens++){
+ if ( bool_vector_iget(ens_mask,iens) ){
+ i=i+1;
+ matrix_copy_column(A0,dataA0,i,iens);
+ }
+ }
+
+ if (ens_size_msk == ens_size){
+ fprintf(log_fp,"data->A0 copied exactly to A0: %d\n",matrix_equal(dataA0,A0)) ;
+ }
+}
+
+
+
+//**********************************************
+// Set / Get basic types
+//**********************************************
+bool ies_enkf_set_int( void * arg , const char * var_name , int value) {
+ ies_enkf_data_type * module_data = ies_enkf_data_safe_cast( arg );
+ ies_enkf_config_type * config = ies_enkf_data_get_config( module_data );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , ENKF_SUBSPACE_DIMENSION_KEY) == 0)
+ ies_enkf_config_set_enkf_subspace_dimension(config , value);
+ else if (strcmp( var_name , ITER_KEY) == 0)
+ ies_enkf_data_set_iteration_nr( module_data , value );
+ else if (strcmp( var_name , IES_INVERSION_KEY) == 0) // This should probably translate string value - now it goes directly on the value of the ies_inversion_type enum.
+ ies_enkf_config_set_ies_inversion( config , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+int ies_enkf_get_int( const void * arg, const char * var_name) {
+ const ies_enkf_data_type * module_data = ies_enkf_data_safe_cast_const( arg );
+ const ies_enkf_config_type * ies_config = ies_enkf_data_get_config( module_data );
+ {
+ if (strcmp(var_name , ITER_KEY) == 0)
+ return ies_enkf_data_get_iteration_nr(module_data);
+ else if (strcmp(var_name , ENKF_SUBSPACE_DIMENSION_KEY) == 0)
+ return ies_enkf_config_get_enkf_subspace_dimension(ies_config);
+ else if (strcmp(var_name , IES_INVERSION_KEY) == 0)
+ return ies_enkf_config_get_ies_inversion(ies_config);
+ else
+ return -1;
+ }
+}
+
+bool ies_enkf_set_string( void * arg , const char * var_name , const char * value) {
+ ies_enkf_data_type * module_data = ies_enkf_data_safe_cast( arg );
+ ies_enkf_config_type * ies_config = ies_enkf_data_get_config( module_data );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , IES_LOGFILE_KEY) == 0)
+ ies_enkf_config_set_ies_logfile( ies_config , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+
+bool ies_enkf_set_bool( void * arg , const char * var_name , bool value) {
+ ies_enkf_data_type * module_data = ies_enkf_data_safe_cast( arg );
+ ies_enkf_config_type * ies_config = ies_enkf_data_get_config( module_data );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , IES_SUBSPACE_KEY) == 0)
+ ies_enkf_config_set_ies_subspace( ies_config , value);
+ else if (strcmp( var_name , IES_DEBUG_KEY) == 0)
+ ies_enkf_config_set_ies_debug( ies_config , value );
+ else if (strcmp( var_name , IES_AAPROJECTION_KEY) == 0)
+ ies_enkf_config_set_ies_aaprojection( ies_config , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+bool ies_enkf_get_bool( const void * arg, const char * var_name) {
+ const ies_enkf_data_type * module_data = ies_enkf_data_safe_cast_const( arg );
+ const ies_enkf_config_type * ies_config = ies_enkf_data_get_config( module_data );
+ {
+ if (strcmp(var_name , IES_SUBSPACE_KEY) == 0)
+ return ies_enkf_config_get_ies_subspace( ies_config );
+ else if (strcmp(var_name , IES_DEBUG_KEY) == 0)
+ return ies_enkf_config_get_ies_debug( ies_config );
+ else if (strcmp(var_name , IES_AAPROJECTION_KEY) == 0)
+ return ies_enkf_config_get_ies_aaprojection( ies_config );
+ else
+ return false;
+ }
+}
+
+
+
+
+bool ies_enkf_set_double( void * arg , const char * var_name , double value) {
+ ies_enkf_data_type * module_data = ies_enkf_data_safe_cast( arg );
+ ies_enkf_config_type * ies_config = ies_enkf_data_get_config( module_data );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , ENKF_TRUNCATION_KEY) == 0)
+ ies_enkf_config_set_truncation( ies_config , value );
+ else if (strcmp( var_name , IES_MAX_STEPLENGTH_KEY) == 0)
+ ies_enkf_config_set_ies_max_steplength( ies_config , value );
+ else if (strcmp( var_name , IES_MIN_STEPLENGTH_KEY) == 0)
+ ies_enkf_config_set_ies_min_steplength( ies_config , value );
+ else if (strcmp( var_name , IES_DEC_STEPLENGTH_KEY) == 0)
+ ies_enkf_config_set_ies_dec_steplength( ies_config , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+double ies_enkf_get_double( const void * arg, const char * var_name) {
+ const ies_enkf_data_type * module_data = ies_enkf_data_safe_cast_const( arg );
+ const ies_enkf_config_type * ies_config = ies_enkf_data_get_config( module_data );
+ {
+ if (strcmp(var_name , ENKF_TRUNCATION_KEY) == 0)
+ return ies_enkf_config_get_truncation( ies_config );
+ if (strcmp(var_name , IES_MAX_STEPLENGTH_KEY) == 0)
+ return ies_enkf_config_get_ies_max_steplength(ies_config);
+ if (strcmp(var_name , IES_MIN_STEPLENGTH_KEY) == 0)
+ return ies_enkf_config_get_ies_min_steplength(ies_config);
+ if (strcmp(var_name , IES_DEC_STEPLENGTH_KEY) == 0)
+ return ies_enkf_config_get_ies_dec_steplength(ies_config);
+ return -1;
+ }
+}
+
+
+long ies_enkf_get_options( void * arg , long flag ) {
+ ies_enkf_data_type * module_data = ies_enkf_data_safe_cast( arg );
+ const ies_enkf_config_type * ies_config = ies_enkf_data_get_config( module_data );
+ {
+ return ies_enkf_config_get_option_flags( ies_config );
+ }
+}
+
+bool ies_enkf_has_var( const void * arg, const char * var_name) {
+ {
+ if (strcmp(var_name , ITER_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , IES_MAX_STEPLENGTH_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , IES_MIN_STEPLENGTH_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , IES_DEC_STEPLENGTH_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , IES_SUBSPACE_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , IES_INVERSION_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , IES_LOGFILE_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , IES_DEBUG_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , IES_AAPROJECTION_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , ENKF_TRUNCATION_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , ENKF_SUBSPACE_DIMENSION_KEY) == 0)
+ return true;
+ else
+ return false;
+ }
+}
+
+void * ies_enkf_get_ptr( const void * arg , const char * var_name ) {
+ const ies_enkf_data_type * module_data = ies_enkf_data_safe_cast_const( arg );
+ const ies_enkf_config_type * ies_config = ies_enkf_data_get_config( module_data );
+ {
+ if (strcmp(var_name , IES_LOGFILE_KEY) == 0)
+ return (void *) ies_enkf_config_get_ies_logfile( ies_config );
+ else
+ return NULL;
+ }
+}
+
+
+//**********************************************
+// Symbol table
+//**********************************************
+#ifdef INTERNAL_LINK
+#define LINK_NAME IES_ENKF
+#else
+#define LINK_NAME EXTERNAL_MODULE_SYMBOL
+#endif
+
+
+analysis_table_type LINK_NAME = {
+ .name = "IES_ENKF",
+ .initX = NULL,
+ .updateA = ies_enkf_updateA,
+ .init_update = ies_enkf_init_update,
+ .complete_update = NULL,
+ .alloc = ies_enkf_data_alloc,
+ .freef = ies_enkf_data_free,
+ .has_var = ies_enkf_has_var,
+ .set_int = ies_enkf_set_int ,
+ .set_double = ies_enkf_set_double ,
+ .set_bool = ies_enkf_set_bool ,
+ .set_string = ies_enkf_set_string ,
+ .get_options = ies_enkf_get_options ,
+ .get_int = ies_enkf_get_int,
+ .get_double = ies_enkf_get_double,
+ .get_bool = ies_enkf_get_bool ,
+ .get_ptr = ies_enkf_get_ptr ,
+};
+
diff --git a/libres/lib/analysis/modules/ies_enkf.h b/libres/lib/analysis/modules/ies_enkf.h
new file mode 100644
index 00000000000..fe07777cf51
--- /dev/null
+++ b/libres/lib/analysis/modules/ies_enkf.h
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 2019 Equinor ASA, Norway.
+
+ The file 'ies_enkf.h' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#ifndef IES_ENKF_H
+#define IES_ENKF_H
+
+#include
+#include
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ies_enkf_init_update(void * arg ,
+ const bool_vector_type * ens_mask ,
+ const bool_vector_type * obs_mask ,
+ const matrix_type * S ,
+ const matrix_type * R ,
+ const matrix_type * dObs ,
+ const matrix_type * E ,
+ const matrix_type * D,
+ rng_type * rng);
+
+
+void ies_enkf_updateA( void * module_data,
+ matrix_type * A , // Updated ensemble A retured to ERT.
+ matrix_type * Yin , // Ensemble of predicted measurements
+ matrix_type * Rin , // Measurement error covariance matrix (not used)
+ matrix_type * dObs , // Actual observations (not used)
+ matrix_type * Ein , // Ensemble of observation perturbations
+ matrix_type * Din , // (d+E-Y) Ensemble of perturbed observations - Y
+ const module_info_type * module_info,
+ rng_type * rng);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libres/lib/analysis/modules/ies_enkf_config.c b/libres/lib/analysis/modules/ies_enkf_config.c
new file mode 100644
index 00000000000..510c462a77a
--- /dev/null
+++ b/libres/lib/analysis/modules/ies_enkf_config.c
@@ -0,0 +1,195 @@
+/*
+ Copyright (C) 2019 Equinor ASA, Norway.
+
+ The file 'ies_enkf_config.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+
+
+
+#define INVALID_TRUNCATION -1
+#define INVALID_SUBSPACE_DIMENSION -1
+#define DEFAULT_ENKF_TRUNCATION 0.98
+#define DEFAULT_ENKF_SUBSPACE_DIMENSION INVALID_SUBSPACE_DIMENSION
+
+#define DEFAULT_IES_MAX_STEPLENGTH 0.60
+#define DEFAULT_IES_MIN_STEPLENGTH 0.30
+#define DEFAULT_IES_DEC_STEPLENGTH 2.50
+#define DEFAULT_IES_SUBSPACE false
+#define DEFAULT_IES_INVERSION IES_INVERSION_SUBSPACE_EXACT_R
+#define DEFAULT_IES_LOGFILE "ies.log"
+#define DEFAULT_IES_DEBUG true
+#define DEFAULT_IES_AAPROJECTION false
+
+
+
+#define IES_ENKF_CONFIG_TYPE_ID 196402021
+
+struct ies_enkf_config_struct {
+ UTIL_TYPE_ID_DECLARATION;
+ double truncation; // Controlled by config key: ENKF_TRUNCATION_KEY
+ int subspace_dimension; // Controlled by config key: ENKF_SUBSPACE_DIMENSION_KEY (-1: use Truncation instead)
+ long option_flags;
+ double ies_max_steplength; // Controlled by config key: DEFAULT_IES_MAX_STEPLENGTH_KEY
+ double ies_min_steplength; // Controlled by config key: DEFAULT_IES_MIN_STEPLENGTH_KEY
+ double ies_dec_steplength; // Controlled by config key: DEFAULT_IES_DEC_STEPLENGTH_KEY
+ bool ies_subspace; // Controlled by config key: DEFAULT_IES_SUBSPACE
+ int ies_inversion; // Controlled by config key: DEFAULT_IES_INVERSION
+ char * ies_logfile; // Controlled by config key: DEFAULT_IES_LOGFILE
+ bool ies_debug; // Controlled by config key: DEFAULT_IES_DEBUG
+ bool ies_aaprojection; // Controlled by config key: DEFAULT_IES_AAPROJECTION
+};
+
+
+ies_enkf_config_type * ies_enkf_config_alloc() {
+ ies_enkf_config_type * config = util_malloc( sizeof * config );
+ UTIL_TYPE_ID_INIT( config , IES_ENKF_CONFIG_TYPE_ID );
+ config->ies_logfile = NULL;
+ ies_enkf_config_set_truncation( config , DEFAULT_ENKF_TRUNCATION);
+ ies_enkf_config_set_enkf_subspace_dimension( config , DEFAULT_ENKF_SUBSPACE_DIMENSION);
+ ies_enkf_config_set_option_flags( config , ANALYSIS_NEED_ED + ANALYSIS_UPDATE_A + ANALYSIS_ITERABLE + ANALYSIS_SCALE_DATA);
+ ies_enkf_config_set_ies_max_steplength( config , DEFAULT_IES_MAX_STEPLENGTH );
+ ies_enkf_config_set_ies_min_steplength( config , DEFAULT_IES_MIN_STEPLENGTH );
+ ies_enkf_config_set_ies_dec_steplength( config , DEFAULT_IES_DEC_STEPLENGTH );
+ ies_enkf_config_set_ies_subspace( config , DEFAULT_IES_SUBSPACE );
+ ies_enkf_config_set_ies_inversion( config , DEFAULT_IES_INVERSION );
+ ies_enkf_config_set_ies_logfile( config , DEFAULT_IES_LOGFILE );
+ ies_enkf_config_set_ies_debug( config , DEFAULT_IES_DEBUG );
+ ies_enkf_config_set_ies_aaprojection( config , DEFAULT_IES_AAPROJECTION );
+
+ return config;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* TRUNCATION -> SUBSPACE_DIMENSION */
+double ies_enkf_config_get_truncation( const ies_enkf_config_type * config ) {
+ return config->truncation;
+}
+
+void ies_enkf_config_set_truncation( ies_enkf_config_type * config , double truncation) {
+ config->truncation = truncation;
+ if (truncation > 0.0)
+ config->subspace_dimension = INVALID_SUBSPACE_DIMENSION;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* SUBSPACE_DIMENSION -> TRUNCATION */
+int ies_enkf_config_get_enkf_subspace_dimension( const ies_enkf_config_type * config ) {
+ return config->subspace_dimension;
+}
+
+void ies_enkf_config_set_enkf_subspace_dimension( ies_enkf_config_type * config , int subspace_dimension) {
+ config->subspace_dimension = subspace_dimension;
+ if (subspace_dimension > 0)
+ config->truncation = INVALID_TRUNCATION;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* OPTION_FLAGS */
+
+long ies_enkf_config_get_option_flags( const ies_enkf_config_type * config ) {
+ return config->option_flags;
+}
+
+void ies_enkf_config_set_option_flags( ies_enkf_config_type * config , long flags) {
+ config->option_flags = flags;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* IES_MAX_STEPLENGTH */
+double ies_enkf_config_get_ies_max_steplength( const ies_enkf_config_type * config ) {
+ return config->ies_max_steplength;
+}
+void ies_enkf_config_set_ies_max_steplength( ies_enkf_config_type * config , double ies_max_steplength) {
+ config->ies_max_steplength = ies_max_steplength;
+}
+/*------------------------------------------------------------------------------------------------*/
+/* IES_MIN_STEPLENGTH */
+double ies_enkf_config_get_ies_min_steplength( const ies_enkf_config_type * config ) {
+ return config->ies_min_steplength;
+}
+void ies_enkf_config_set_ies_min_steplength( ies_enkf_config_type * config , double ies_min_steplength) {
+ config->ies_min_steplength = ies_min_steplength;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* IES_DEC_STEPLENGTH */
+double ies_enkf_config_get_ies_dec_steplength( const ies_enkf_config_type * config ) {
+ return config->ies_dec_steplength;
+}
+void ies_enkf_config_set_ies_dec_steplength( ies_enkf_config_type * config , double ies_dec_steplength) {
+ config->ies_dec_steplength = ies_dec_steplength;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* IES_INVERSION */
+ies_inversion_type ies_enkf_config_get_ies_inversion( const ies_enkf_config_type * config ) {
+ return config->ies_inversion;
+}
+void ies_enkf_config_set_ies_inversion( ies_enkf_config_type * config , ies_inversion_type ies_inversion ) {
+ config->ies_inversion = ies_inversion;
+}
+
+
+/*------------------------------------------------------------------------------------------------*/
+/* IES_SUBSPACE */
+bool ies_enkf_config_get_ies_subspace( const ies_enkf_config_type * config ) {
+ return config->ies_subspace;
+}
+void ies_enkf_config_set_ies_subspace( ies_enkf_config_type * config , bool ies_subspace ) {
+ config->ies_subspace = ies_subspace;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* IES_DEBUG */
+bool ies_enkf_config_get_ies_debug( const ies_enkf_config_type * config ) {
+ return config->ies_debug;
+}
+void ies_enkf_config_set_ies_debug( ies_enkf_config_type * config , bool ies_debug ) {
+ config->ies_debug = ies_debug;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* IES_AAPROJECTION */
+bool ies_enkf_config_get_ies_aaprojection( const ies_enkf_config_type * config ) {
+ return config->ies_aaprojection;
+}
+void ies_enkf_config_set_ies_aaprojection( ies_enkf_config_type * config , bool ies_aaprojection ) {
+ config->ies_aaprojection = ies_aaprojection;
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* IES_LOGFILE */
+char * ies_enkf_config_get_ies_logfile( const ies_enkf_config_type * config ) {
+ return config->ies_logfile;
+}
+void ies_enkf_config_set_ies_logfile( ies_enkf_config_type * config , const char * ies_logfile ) {
+ config->ies_logfile = util_realloc_string_copy( config->ies_logfile , ies_logfile );
+}
+
+/*------------------------------------------------------------------------------------------------*/
+/* FREE_CONFIG */
+void ies_enkf_config_free(ies_enkf_config_type * config) {
+ free( config );
+}
diff --git a/libres/lib/analysis/modules/ies_enkf_config.h b/libres/lib/analysis/modules/ies_enkf_config.h
new file mode 100644
index 00000000000..9d07aa4efdc
--- /dev/null
+++ b/libres/lib/analysis/modules/ies_enkf_config.h
@@ -0,0 +1,80 @@
+/*
+ Copyright (C) 2019 Equinor ASA, Norway.
+
+ The file 'ies_enkf_config.h' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#ifndef IES_ENKF_CONFIG_H
+#define IES_ENKF_CONFIG_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef enum {
+ IES_INVERSION_EXACT = 0,
+ IES_INVERSION_SUBSPACE_EXACT_R = 1,
+ IES_INVERSION_SUBSPACE_EE_R = 2,
+ IES_INVERSION_SUBSPACE_RE = 3
+} ies_inversion_type;
+
+
+typedef struct ies_enkf_config_struct ies_enkf_config_type;
+
+
+ ies_enkf_config_type * ies_enkf_config_alloc();
+ void ies_enkf_config_free(ies_enkf_config_type * config);
+
+ int ies_enkf_config_get_enkf_subspace_dimension( const ies_enkf_config_type * config );
+ void ies_enkf_config_set_enkf_subspace_dimension( ies_enkf_config_type * config , int subspace_dimension);
+
+ double ies_enkf_config_get_truncation( const ies_enkf_config_type * config );
+ void ies_enkf_config_set_truncation( ies_enkf_config_type * config , double truncation);
+
+ void ies_enkf_config_set_option_flags( ies_enkf_config_type * config , long flags);
+ long ies_enkf_config_get_option_flags( const ies_enkf_config_type * config );
+
+ double ies_enkf_config_get_ies_max_steplength( const ies_enkf_config_type * config );
+ void ies_enkf_config_set_ies_max_steplength( ies_enkf_config_type * config , double ies_max_steplength);
+
+ double ies_enkf_config_get_ies_min_steplength( const ies_enkf_config_type * config );
+ void ies_enkf_config_set_ies_min_steplength( ies_enkf_config_type * config , double ies_min_steplength);
+
+ double ies_enkf_config_get_ies_dec_steplength( const ies_enkf_config_type * config );
+ void ies_enkf_config_set_ies_dec_steplength( ies_enkf_config_type * config , double ies_dec_steplength);
+
+ ies_inversion_type ies_enkf_config_get_ies_inversion( const ies_enkf_config_type * config ) ;
+ void ies_enkf_config_set_ies_inversion( ies_enkf_config_type * config , ies_inversion_type ies_inversion ) ;
+
+ bool ies_enkf_config_get_ies_subspace( const ies_enkf_config_type * config ) ;
+ void ies_enkf_config_set_ies_subspace( ies_enkf_config_type * config , bool ies_subspace ) ;
+
+ bool ies_enkf_config_get_ies_debug( const ies_enkf_config_type * config ) ;
+ void ies_enkf_config_set_ies_debug( ies_enkf_config_type * config , bool ies_debug ) ;
+
+ bool ies_enkf_config_get_ies_aaprojection( const ies_enkf_config_type * config ) ;
+ void ies_enkf_config_set_ies_aaprojection( ies_enkf_config_type * config , bool ies_aaprojection ) ;
+
+ char * ies_enkf_config_get_ies_logfile( const ies_enkf_config_type * config ) ;
+ void ies_enkf_config_set_ies_logfile( ies_enkf_config_type * config , const char * ies_logfile ) ;
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libres/lib/analysis/modules/ies_enkf_data.c b/libres/lib/analysis/modules/ies_enkf_data.c
new file mode 100644
index 00000000000..94e17c047ca
--- /dev/null
+++ b/libres/lib/analysis/modules/ies_enkf_data.c
@@ -0,0 +1,267 @@
+#include "ies_enkf_config.h"
+#include "ies_enkf_data.h"
+
+//**********************************************
+// IES "object" data definition
+//**********************************************
+/*
+ The configuration data used by the ies_enkf module is contained in a
+ ies_enkf_data_struct instance. The data type used for the ies_enkf
+ module is quite simple; with only a few scalar variables, but there
+ are essentially no limits to what you can pack into such a datatype.
+
+ All the functions in the module have a void pointer as the first
+ argument, this will immediately be casted to an ies_enkf_data_type
+ instance, to get some type safety the UTIL_TYPE_ID system should be
+ used.
+
+ The data structure holding the data for your analysis module should
+ be created and initialized by a constructor, which should be
+ registered with the '.alloc' element of the analysis table; in the
+ same manner the desctruction of this data should be handled by a
+ destructor or free() function registered with the .freef field of
+ the analysis table.
+*/
+
+#define IES_ENKF_DATA_TYPE_ID 6635831
+
+
+struct ies_enkf_data_struct {
+ UTIL_TYPE_ID_DECLARATION;
+ int iteration_nr; // Keep track of the outer iteration loop
+ int state_size; // Initial state_size used for checks in subsequent calls
+ bool_vector_type * ens_mask; // Ensemble mask of active realizations
+ bool_vector_type * obs_mask0; // Initial observation mask for active measurements
+ bool_vector_type * obs_mask; // Current observation mask
+ matrix_type * W; // Coefficient matrix used to compute Omega = I + W (I -11'/N)/sqrt(N-1)
+ matrix_type * A0; // Prior ensemble used in Ei=A0 Omega_i
+ matrix_type * E; // Prior ensemble of measurement perturations (should be the same for all iterations)
+ bool converged; // GN has converged
+ ies_enkf_config_type * config; // This I don't understand but I assume I include data from the ies_enkf_config_type defined in ies_enkf_config.c
+ FILE* log_fp; // logfile id
+};
+
+UTIL_SAFE_CAST_FUNCTION( ies_enkf_data , IES_ENKF_DATA_TYPE_ID )
+UTIL_SAFE_CAST_FUNCTION_CONST( ies_enkf_data , IES_ENKF_DATA_TYPE_ID )
+
+void * ies_enkf_data_alloc( rng_type * rng) {
+ ies_enkf_data_type * data = util_malloc( sizeof * data);
+ UTIL_TYPE_ID_INIT( data , IES_ENKF_DATA_TYPE_ID );
+ data->iteration_nr = 0;
+ data->state_size = 0;
+ data->ens_mask = NULL;
+ data->obs_mask0 = NULL;
+ data->obs_mask = NULL;
+ data->W = NULL;
+ data->A0 = NULL;
+ data->E = NULL;
+ data->converged = false;
+ data->config = ies_enkf_config_alloc();
+ data->log_fp = NULL;
+ return data;
+}
+
+void ies_enkf_data_free( void * arg ) {
+ ies_enkf_data_type * data = ies_enkf_data_safe_cast( arg );
+ ies_enkf_config_free( data->config );
+ free( data );
+}
+
+void ies_enkf_data_set_iteration_nr( ies_enkf_data_type * data , int iteration_nr) {
+ data->iteration_nr = iteration_nr;
+}
+
+int ies_enkf_data_inc_iteration_nr( ies_enkf_data_type * data) {
+ data->iteration_nr++;
+ return data->iteration_nr;
+}
+
+int ies_enkf_data_get_iteration_nr( const ies_enkf_data_type * data ) {
+ return data->iteration_nr;
+}
+
+
+ies_enkf_config_type* ies_enkf_data_get_config(const ies_enkf_data_type * data) {
+ return data->config;
+}
+
+
+
+void ies_enkf_data_update_ens_mask(ies_enkf_data_type * data, const bool_vector_type * ens_mask) {
+ if (data->ens_mask)
+ bool_vector_free(data->ens_mask);
+
+ data->ens_mask = bool_vector_alloc_copy( ens_mask );
+}
+
+
+void ies_enkf_store_initial_obs_mask(ies_enkf_data_type * data, const bool_vector_type * obs_mask) {
+ if (!data->obs_mask0)
+ data->obs_mask0 = bool_vector_alloc_copy(obs_mask);
+}
+
+void ies_enkf_update_obs_mask(ies_enkf_data_type * data, const bool_vector_type * obs_mask) {
+ if (data->obs_mask)
+ bool_vector_free(data->obs_mask);
+
+ data->obs_mask = bool_vector_alloc_copy(obs_mask);
+}
+
+int ies_enkf_data_get_obs_mask_size(const ies_enkf_data_type * data) {
+ return bool_vector_size( data->obs_mask );
+}
+
+int ies_enkf_data_active_obs_count(const ies_enkf_data_type * data) {
+ int nrobs_msk = ies_enkf_data_get_obs_mask_size( data );
+ int nrobs = 0;
+ for (int i = 0; i < nrobs_msk; i++) {
+ if ( bool_vector_iget(data->obs_mask,i) ){
+ nrobs=nrobs+1;
+ }
+ }
+ return nrobs;
+}
+
+
+int ies_enkf_data_get_ens_mask_size(const ies_enkf_data_type * data) {
+ return bool_vector_size(data->ens_mask);
+}
+
+
+void ies_enkf_data_update_state_size(ies_enkf_data_type * data, int state_size) {
+ if (data->state_size == 0)
+ data->state_size = state_size;
+}
+
+FILE * ies_enkf_data_open_log(ies_enkf_data_type * data) {
+ const char * ies_logfile = ies_enkf_config_get_ies_logfile( data->config );
+ FILE * fp;
+ if (data->iteration_nr == 1){
+ fp = fopen(ies_logfile, "w");
+ } else {
+ fp = fopen(ies_logfile, "a");
+ }
+ data->log_fp = fp;
+ return fp;
+}
+
+void ies_enkf_data_fclose_log(ies_enkf_data_type * data) {
+ fflush(data->log_fp);
+ fclose(data->log_fp);
+}
+
+
+/* We store the initial observation perturbations in E, corresponding to active data->obs_mask0
+ in data->E. The unused rows in data->E corresponds to false data->obs_mask0 */
+void ies_enkf_data_store_initialE(ies_enkf_data_type * data, const matrix_type * E0) {
+ if (!data->E){
+ bool dbg = ies_enkf_config_get_ies_debug( data->config ) ;
+ int obs_size_msk = ies_enkf_data_get_obs_mask_size( data );
+ int ens_size_msk = ies_enkf_data_get_ens_mask_size( data );
+ fprintf(data->log_fp,"Allocating and assigning data->E (%d,%d) \n",obs_size_msk,ens_size_msk);
+ data->E = matrix_alloc(obs_size_msk,ens_size_msk);
+ matrix_set(data->E , -999.9) ;
+ int m=0;
+ for (int i = 0; i < obs_size_msk; i++) {
+ if ( bool_vector_iget(data->obs_mask0,i) ){
+ matrix_copy_row(data->E,E0,i,m);
+ m++;
+ }
+ }
+
+ if (dbg) {
+ int nrobs_inp=matrix_get_rows( E0 );
+ int m_nrobs=util_int_min(nrobs_inp-1, 50);
+ int m_ens_size=util_int_min(ens_size_msk-1, 16);
+ matrix_pretty_fprint_submat(E0,"Ein","%11.5f",data->log_fp,0,m_nrobs,0,m_ens_size);
+ m_nrobs=util_int_min(obs_size_msk-1, 50);
+ matrix_pretty_fprint_submat(data->E,"data->E","%11.5f",data->log_fp,0,m_nrobs,0,m_ens_size);
+ }
+
+ }
+}
+
+/* We augment the additional observation perturbations arriving in later iterations, that was not stored before,
+ in data->E. */
+void ies_enkf_data_augment_initialE(ies_enkf_data_type * data, const matrix_type * E0) {
+ if (data->E){
+ fprintf(data->log_fp,"Augmenting new perturbations to data->E \n");
+ bool dbg = ies_enkf_config_get_ies_debug( data->config ) ;
+ int obs_size_msk = ies_enkf_data_get_obs_mask_size( data );
+ int ens_size_msk = ies_enkf_data_get_ens_mask_size( data );
+ int m=0;
+ for (int iobs = 0; iobs < obs_size_msk; iobs++){
+ if ( !bool_vector_iget(data->obs_mask0,iobs) && bool_vector_iget(data->obs_mask,iobs) ){
+ int i=-1;
+ for (int iens = 0; iens < ens_size_msk; iens++){
+ if ( bool_vector_iget(data->ens_mask,iens) ){
+ i++;
+ matrix_iset_safe(data->E,iobs,iens,matrix_iget(E0,m,i)) ;
+ }
+ }
+ bool_vector_iset(data->obs_mask0,iobs,true);
+ }
+ if ( bool_vector_iget(data->obs_mask,iobs) ){
+ m++;
+ }
+ }
+
+ if (dbg) {
+ int m_nrobs=util_int_min(obs_size_msk-1, 50);
+ int m_ens_size=util_int_min(ens_size_msk-1, 16);
+ matrix_pretty_fprint_submat(data->E,"data->E","%11.5f",data->log_fp,0,m_nrobs,0,m_ens_size);
+ }
+ }
+}
+
+void ies_enkf_data_store_initialA(ies_enkf_data_type * data, const matrix_type * A) {
+ if (!data->A0){
+ // We store the initial ensemble to use it in final update equation (Line 11)
+ bool dbg = ies_enkf_config_get_ies_debug( data->config ) ;
+ int m_state_size = util_int_min(matrix_get_rows( A )-1, 50);
+ int m_ens_size = util_int_min(matrix_get_columns( A )-1, 16);
+ fprintf(data->log_fp,"Allocating and assigning data->A0 \n");
+ data->A0 = matrix_alloc_copy(A);
+ if (dbg)
+ matrix_pretty_fprint_submat(data->A0,"Ini data->A0","%11.5f",data->log_fp,0,m_state_size,0,m_ens_size);
+ }
+}
+
+void ies_enkf_data_allocateW(ies_enkf_data_type * data, int ens_size) {
+ if (!data->W){
+ // We initialize data-W which will store W for use in next iteration (Line 9)
+ bool dbg = ies_enkf_config_get_ies_debug( data->config ) ;
+ int m_ens_size = util_int_min(ens_size-1, 16);
+ fprintf(data->log_fp,"Allocating data->W\n");
+ data->W=matrix_alloc(ens_size , ens_size);
+ matrix_set(data->W , 0.0) ;
+ if (dbg)
+ matrix_pretty_fprint_submat(data->W,"Ini data->W","%11.5f",data->log_fp,0,m_ens_size,0,m_ens_size) ;
+ }
+}
+
+const bool_vector_type * ies_enkf_data_get_obs_mask0( const ies_enkf_data_type * data) {
+ return data->obs_mask0;
+}
+
+const bool_vector_type * ies_enkf_data_get_obs_mask( const ies_enkf_data_type * data) {
+ return data->obs_mask;
+}
+
+const bool_vector_type * ies_enkf_data_get_ens_mask( const ies_enkf_data_type * data) {
+ return data->ens_mask;
+}
+
+
+
+const matrix_type * ies_enkf_data_getE(const ies_enkf_data_type * data) {
+ return data->E;
+}
+
+matrix_type * ies_enkf_data_getW(const ies_enkf_data_type * data) {
+ return data->W;
+}
+
+const matrix_type * ies_enkf_data_getA0(const ies_enkf_data_type * data) {
+ return data->A0;
+}
diff --git a/libres/lib/analysis/modules/ies_enkf_data.h b/libres/lib/analysis/modules/ies_enkf_data.h
new file mode 100644
index 00000000000..fac317efe74
--- /dev/null
+++ b/libres/lib/analysis/modules/ies_enkf_data.h
@@ -0,0 +1,57 @@
+#ifndef IES_ENKF_DATA_H
+#define IES_ENKF_DATA_H
+
+#include
+#include
+#include
+#include
+
+#include "ies_enkf_config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ies_enkf_data_struct ies_enkf_data_type;
+
+void * ies_enkf_data_alloc( rng_type * rng);
+void ies_enkf_data_free( void * arg );
+
+void ies_enkf_data_set_iteration_nr( ies_enkf_data_type * data , int iteration_nr);
+int ies_enkf_data_get_iteration_nr( const ies_enkf_data_type * data );
+int ies_enkf_data_inc_iteration_nr( ies_enkf_data_type * data);
+
+ies_enkf_config_type* ies_enkf_data_get_config(const ies_enkf_data_type * data);
+
+void ies_enkf_data_update_ens_mask(ies_enkf_data_type * data, const bool_vector_type * ens_mask);
+void ies_enkf_store_initial_obs_mask(ies_enkf_data_type * data, const bool_vector_type * obs_mask);
+void ies_enkf_update_obs_mask(ies_enkf_data_type * data, const bool_vector_type * obs_mask);
+void ies_enkf_data_update_state_size( ies_enkf_data_type * data, int state_size);
+
+int ies_enkf_data_get_obs_mask_size(const ies_enkf_data_type * data);
+int ies_enkf_data_get_ens_mask_size(const ies_enkf_data_type * data);
+int ies_enkf_data_active_obs_count(const ies_enkf_data_type * data);
+
+const bool_vector_type * ies_enkf_data_get_obs_mask0( const ies_enkf_data_type * data);
+const bool_vector_type * ies_enkf_data_get_obs_mask( const ies_enkf_data_type * data);
+const bool_vector_type * ies_enkf_data_get_ens_mask( const ies_enkf_data_type * data);
+
+FILE * ies_enkf_data_open_log(ies_enkf_data_type * data);
+void ies_enkf_data_fclose_log(ies_enkf_data_type * data);
+
+void ies_enkf_data_allocateW(ies_enkf_data_type * data, int ens_size);
+void ies_enkf_data_store_initialE(ies_enkf_data_type * data, const matrix_type * E0);
+void ies_enkf_data_augment_initialE(ies_enkf_data_type * data, const matrix_type * E0);
+void ies_enkf_data_store_initialA(ies_enkf_data_type * data, const matrix_type * A);
+const matrix_type * ies_enkf_data_getE(const ies_enkf_data_type * data);
+const matrix_type * ies_enkf_data_getA0(const ies_enkf_data_type * data);
+matrix_type * ies_enkf_data_getW(const ies_enkf_data_type * data);
+
+UTIL_SAFE_CAST_HEADER(ies_enkf_data);
+UTIL_SAFE_CAST_HEADER_CONST(ies_enkf_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libres/lib/analysis/modules/rml_enkf.c b/libres/lib/analysis/modules/rml_enkf.c
new file mode 100644
index 00000000000..e1d328c8ed7
--- /dev/null
+++ b/libres/lib/analysis/modules/rml_enkf.c
@@ -0,0 +1,842 @@
+/*
+ Copyright (C) 2011 Equinor ASA, Norway.
+
+ The file 'rml_enkf.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+typedef struct rml_enkf_data_struct rml_enkf_data_type;
+
+
+
+//**********************************************
+// DEFAULT PARAMS
+//**********************************************
+/*
+ Observe that only one of the settings subspace_dimension and
+ truncation can be valid at a time; otherwise the svd routine will
+ fail. This implies that the set_truncation() and
+ set_subspace_dimension() routines will set one variable, AND
+ INVALIDATE THE OTHER. For most situations this will be OK, but if
+ you have repeated calls to both of these functions the end result
+ might be a surprise.
+*/
+
+
+
+#define USE_PRIOR_KEY "USE_PRIOR"
+#define LAMBDA_REDUCE_FACTOR_KEY "LAMBDA_REDUCE"
+#define LAMBDA_INCREASE_FACTOR_KEY "LAMBDA_INCREASE"
+#define LAMBDA0_KEY "LAMBDA0"
+#define LAMBDA_MIN_KEY "LAMBDA_MIN"
+#define LAMBDA_RECALCULATE_KEY "LAMBDA_RECALCULATE"
+#define ITER_KEY "ITER"
+#define LOG_FILE_KEY "LOG_FILE"
+#define CLEAR_LOG_KEY "CLEAR_LOG"
+
+
+
+
+#define RML_ENKF_TYPE_ID 261123
+
+
+//**********************************************
+// RML "object" data definition
+//**********************************************
+/*
+ The configuration data used by the rml_enkf module is contained in a
+ rml_enkf_data_struct instance. The data type used for the rml_enkf
+ module is quite simple; with only a few scalar variables, but there
+ are essentially no limits to what you can pack into such a datatype.
+
+ All the functions in the module have a void pointer as the first
+ argument, this will immediately be casted to a rml_enkf_data_type
+ instance, to get some type safety the UTIL_TYPE_ID system should be
+ used (see documentation in util.h)
+
+ The data structure holding the data for your analysis module should
+ be created and initialized by a constructor, which should be
+ registered with the '.alloc' element of the analysis table; in the
+ same manner the desctruction of this data should be handled by a
+ destructor or free() function registered with the .freef field of
+ the analysis table.
+*/
+
+
+
+
+
+struct rml_enkf_data_struct {
+ UTIL_TYPE_ID_DECLARATION;
+
+ int iteration_nr; // Keep track of the outer iteration loop
+ double Sk; // Objective function value
+ double Std; // Standard Deviation of the Objective function
+ double * Csc;
+ bool_vector_type * ens_mask;
+
+ matrix_type *Am; // Scaled right singular vectors of ensemble anomalies.
+
+ matrix_type *global_prior; // m_pr
+ matrix_type *previous_state; // m_l
+
+
+ double lambda; // parameter to control the setp length in Marquardt levenberg optimization
+
+
+ rml_enkf_log_type * rml_log;
+ rml_enkf_config_type * config;
+};
+
+
+
+static UTIL_SAFE_CAST_FUNCTION( rml_enkf_data , RML_ENKF_TYPE_ID )
+static UTIL_SAFE_CAST_FUNCTION_CONST( rml_enkf_data , RML_ENKF_TYPE_ID )
+
+
+
+
+
+
+//**********************************************
+// Set / Get
+//**********************************************
+
+
+void rml_enkf_set_iteration_nr( rml_enkf_data_type * data , int iteration_nr) {
+ data->iteration_nr = iteration_nr;
+}
+
+int rml_enkf_get_iteration_nr( const rml_enkf_data_type * data ) {
+ return data->iteration_nr;
+}
+
+
+
+
+
+//**********************************************
+// Log-file related stuff
+//**********************************************
+
+
+static void rml_enkf_write_log_header( rml_enkf_data_type * data, const char * format) {
+ if (rml_enkf_log_is_open( data->rml_log )) {
+ const char * column1 = "Iter#";
+ const char * column2 = "Lambda";
+ const char * column3 = "Sk old";
+ const char * column4 = "Sk_new";
+ const char * column5 = "std(Sk)";
+
+ rml_enkf_log_line(data->rml_log, format, column1, column2, column3, column4, column5);
+ }
+}
+
+static void rml_enkf_write_iter_info( rml_enkf_data_type * data , double prev_Sk , double Sk_new, double Std_new ) {
+ if (rml_enkf_log_is_open( data->rml_log )) {
+
+ const char * format = "\n%2d-->%-2d %-7.3f %-7.3f --> %-7.3f %-7.3f";
+ const char * format_headers = "\n%-7s %-7s %-7s --> %-7s %-7s";
+ static bool has_printed_header = false;
+
+ if (!has_printed_header) {
+ rml_enkf_write_log_header( data, format_headers );
+ has_printed_header = true;
+ }
+
+ rml_enkf_log_line( data->rml_log , format, data->iteration_nr, data->iteration_nr+1, data->lambda, prev_Sk, Sk_new, Std_new);
+ }
+}
+
+
+
+
+
+//**********************************************
+// Memory
+//**********************************************
+void * rml_enkf_data_alloc( rng_type * rng) {
+ rml_enkf_data_type * data = util_malloc( sizeof * data);
+ UTIL_TYPE_ID_INIT( data , RML_ENKF_TYPE_ID );
+
+ data->config = rml_enkf_config_alloc();
+ data->rml_log = rml_enkf_log_alloc();
+
+ data->Csc = NULL;
+ data->iteration_nr = 0;
+ data->Std = 0;
+ data->previous_state = matrix_alloc(1,1);
+ data->global_prior = NULL;
+ data->ens_mask = NULL;
+ return data;
+}
+
+void rml_enkf_data_free( void * arg ) {
+ rml_enkf_data_type * data = rml_enkf_data_safe_cast( arg );
+
+ matrix_free( data->previous_state );
+ if (data->global_prior)
+ matrix_free( data->global_prior );
+
+ rml_enkf_log_free( data->rml_log );
+ rml_enkf_config_free( data->config );
+ free( data );
+}
+
+
+
+
+
+//**********************************************
+// Notation
+//**********************************************
+/*
+ * X1-X7, intermediate calculations in iterations. See D.Oliver algorithm
+ *
+ * Variable name in code <-> D.Oliver notation <-> Description
+ * -------------------------------------------------------------------------------------------------------------
+ * A <-> m_l <-> Ensemble matrix. Updated in-place by iterations.
+ * data->previous_state <-> m_(l-1) <-> "A" from the previous iteration. Backs up A in case the update is bad.
+ * data->global_prior <-> <-> Previously: "active_prior". Stores A from before iter0, i.e. the actual prior.
+ * Acopy <-> <-> Eliminated from code. Copy of A (at each iteration, before acceptance/rejection decision)
+
+ *
+ * Am <-> A_m <-> Am = Um*Wm^(-1)
+ * Csc <-> C_sc^(1/2) <-> State scalings. Note the square root.
+ * Dm (in init1__) <-> Delta m <-> Anomalies of prior wrt. its mean (row i scaled by 1/(Csc[i]*sqrt(N-1)))
+ * Dm (in initA__) <-> Csc * Delta m <-> Anomalies of A wrt. its mean (only scaled by 1/sqrt(N-1))
+ * Dk1 (in init2__) <-> Delta m <-> Anomailes of A (row i scaled by 1/(Csc[i]*sqrt(N-1)))
+ * Dk (in init2__) <-> C_sc^(-1) * (m - m_pr ) <-> Anomalies wrt. prior (as opposed to the mean; only scaled by Csc)
+ * dA1 (in initA__) <-> delta m_1 <-> Ensemble updates coming from data mismatch
+ * dA2 (in init2__) <-> delta m_2 <-> Ensemble updates coming from prior mismatch
+*/
+
+
+
+
+//**********************************************
+// Actual Algorithm, called through updateA()
+//**********************************************
+
+// Just (pre)calculates data->Am = Um*Wm^(-1).
+static void rml_enkf_init1__( rml_enkf_data_type * data) {
+ // Differentiate this routine from init2__, which actually calculates the prior mismatch update.
+ // This routine does not change any ensemble matrix.
+ // Um*Wm^(-1) are the scaled, truncated, right singular vectors of data->global_prior
+
+ matrix_type * prior = matrix_alloc_column_compressed_copy( data->global_prior , data->ens_mask);
+ int state_size = matrix_get_rows( prior );
+ int ens_size = matrix_get_columns( prior );
+ int nrmin = util_int_min( ens_size , state_size);
+ matrix_type * Dm = matrix_alloc_copy( prior );
+ matrix_type * Um = matrix_alloc( state_size , nrmin ); /* Left singular vectors. */
+ matrix_type * VmT = matrix_alloc( nrmin , ens_size ); /* Right singular vectors. */
+ double * Wm = util_calloc( nrmin , sizeof * Wm );
+ double nsc = 1/sqrt(ens_size - 1);
+
+ matrix_subtract_row_mean(Dm);
+ {
+ const double * Csc = data->Csc;
+ for (int i=0; i < state_size; i++){
+ double sc = nsc / (Csc[i]);
+ matrix_scale_row( Dm , i , sc);
+ }
+ }
+
+ // Um Wm VmT = Dm; nsign1 = num of non-zero singular values.
+ int nsign1 = enkf_linalg_svd_truncation(Dm , rml_enkf_config_get_truncation( data->config ) , -1 , DGESVD_MIN_RETURN , Wm , Um , VmT);
+
+ // Am = Um*Wm^(-1). I.e. scale *columns* of Um
+ enkf_linalg_rml_enkfAm(Um, Wm, nsign1);
+
+ data->Am = matrix_alloc_copy( Um );
+ matrix_free(Um);
+ matrix_free(VmT);
+ matrix_free(Dm);
+ matrix_free(prior);
+ free(Wm);
+}
+
+
+
+// Creates state scaling matrix
+void rml_enkf_init_Csc(const rml_enkf_data_type * data ){
+ // This seems a strange choice of scaling matrix. Review?
+ matrix_type * prior = matrix_alloc_column_compressed_copy( data->global_prior , data->ens_mask );
+ {
+ int state_size = matrix_get_rows( prior );
+ int ens_size = matrix_get_columns( prior );
+
+ for (int row=0; row < state_size; row++) {
+ double sumrow = matrix_get_row_sum(prior , row);
+ double tmp = sumrow / ens_size;
+
+ if (fabs(tmp)< 1)
+ data->Csc[row] = 0.05;
+ else
+ data->Csc[row] = 1.00;
+
+ }
+ matrix_free( prior );
+ }
+}
+
+// Calculates update from data mismatch (delta m_1). Also provides SVD for later use.
+static void rml_enkf_initA__(rml_enkf_data_type * data, matrix_type * A, matrix_type * S, matrix_type * Cd, matrix_type * E, matrix_type * D, matrix_type * Udr, double * Wdr, matrix_type * VdTr) {
+
+ int ens_size = matrix_get_columns( S );
+ int state_size = matrix_get_rows( A );
+ double nsc = 1/sqrt(ens_size-1);
+ int nsign;
+
+ // Perform SVD of tmp, where: tmp = diag_sqrt(Cd^(-1)) * centered(S) / sqrt(N-1) = Ud * Wd * Vd(T)
+ {
+ int nrobs = matrix_get_rows( S );
+ matrix_type *tmp = matrix_alloc (nrobs, ens_size);
+ matrix_subtract_row_mean( S ); // Center S
+ matrix_inplace_diag_sqrt(Cd); // Assumes that Cd is diag!
+ matrix_matmul(tmp , Cd , S ); //
+ matrix_scale(tmp , nsc); //
+
+ nsign = enkf_linalg_svd_truncation(tmp , rml_enkf_config_get_truncation( data->config ) , -1 , DGESVD_MIN_RETURN , Wdr , Udr , VdTr);
+ matrix_free( tmp );
+ }
+
+ // Calc X3
+ {
+ matrix_type * X3 = matrix_alloc( ens_size, ens_size );
+ {
+ matrix_type * X1 = matrix_alloc( nsign, ens_size);
+ matrix_type * X2 = matrix_alloc( nsign, ens_size );
+
+
+ // See LM-EnRML algorithm in Oliver'2013 (Comp. Geo.) for meaning
+ enkf_linalg_rml_enkfX1(X1, Udr ,D ,Cd ); // X1 = Ud(T)*Cd(-1/2)*D -- D= -(dk-d0)
+ enkf_linalg_rml_enkfX2(X2, Wdr ,X1 ,data->lambda + 1 , nsign); // X2 = ((a*Ipd)+Wd^2)^-1 * X1
+ enkf_linalg_rml_enkfX3(X3, VdTr ,Wdr,X2, nsign); // X3 = Vd *Wd*X2
+
+ matrix_free(X2);
+ matrix_free(X1);
+ }
+
+ // Update A
+ {
+ matrix_type * dA1 = matrix_alloc( state_size , ens_size);
+ matrix_type * Dm = matrix_alloc_copy( A );
+
+ matrix_subtract_row_mean( Dm ); /* Remove the mean from the ensemble of model parameters*/
+ matrix_scale(Dm, nsc);
+
+ matrix_matmul(dA1, Dm , X3);
+ matrix_inplace_add(A,dA1); // dA
+
+ matrix_free(Dm);
+ matrix_free(dA1);
+ }
+ matrix_free(X3);
+
+ }
+}
+
+// Calculate prior mismatch update (delta m_2).
+void rml_enkf_init2__( rml_enkf_data_type * data, matrix_type *A, double * Wdr, matrix_type * VdTr) {
+ // Distinguish from init1__ which only makes preparations, and is only called at iter=0
+
+
+ int state_size = matrix_get_rows( A );
+ int ens_size = matrix_get_columns( A );
+ double nsc = 1/sqrt(ens_size-1);
+
+ matrix_type *Am = matrix_alloc_copy(data->Am);
+ matrix_type *Apr = matrix_alloc_column_compressed_copy(data->global_prior , data->ens_mask );
+
+ // fprintf(stdout,"\n");
+ // fprintf(stdout,"A: %d x %d\n", matrix_get_rows(A), matrix_get_columns(A));
+ // fprintf(stdout,"prior : %d x %d\n", matrix_get_rows(data->global_prior), matrix_get_columns(data->global_prior));
+ // fprintf(stdout,"state : %d x %d\n", matrix_get_rows(data->previous_state), matrix_get_columns(data->previous_state));
+ // fprintf(stdout,"Apr : %d x %d\n", matrix_get_rows(Apr), matrix_get_columns(Apr));
+ // fprintf(stdout,"Am : %d x %d\n", matrix_get_rows(Am), matrix_get_columns(Am));
+ // Example:
+ // A : 27760 x 10
+ // prior : 27760 x 10
+ // state : 27760 x 50
+ // prior0 : 27760 x 50
+ // Apr : 27760 x 10
+ // Am : 27760 x 1
+
+
+ int nsign1 = matrix_get_columns(data->Am);
+
+
+ matrix_type * X4 = matrix_alloc(nsign1,ens_size);
+ matrix_type * X5 = matrix_alloc(state_size,ens_size);
+ matrix_type * X6 = matrix_alloc(ens_size,ens_size);
+ matrix_type * X7 = matrix_alloc(ens_size,ens_size);
+ matrix_type * dA2 = matrix_alloc(state_size , ens_size);
+ matrix_type * Dk1 = matrix_alloc_copy( A );
+
+ // Dk = Csc^(-1) * (A - Aprior)
+ // X4 = Am' * Dk
+ {
+ matrix_type * Dk = matrix_alloc_copy( A );
+
+ matrix_inplace_sub( Dk , Apr );
+ rml_enkf_common_scaleA(Dk , data->Csc , true);
+
+ matrix_dgemm(X4 , Am , Dk , true, false, 1.0, 0.0);
+ matrix_free(Dk);
+ }
+ // X5 = Am * X4
+ matrix_matmul(X5 , Am , X4);
+
+ // Dk1 = Csc^(-1)/sqrt(N-1) * A*(I - 1/N*ones(m,N))
+ matrix_subtract_row_mean(Dk1); // Dk1 = Dk1 * (I - 1/N*ones(m,N))
+ rml_enkf_common_scaleA(Dk1 , data->Csc , true); // Dk1 = Csc^(-1) * Dk1
+ matrix_scale(Dk1,nsc); // Dk1 = Dk1 / sqrt(N-1)
+
+ // X6 = Dk1' * X5
+ matrix_dgemm(X6, Dk1, X5, true, false, 1.0, 0.0);
+
+ // X7
+ enkf_linalg_rml_enkfX7(X7, VdTr , Wdr , data->lambda + 1, X6);
+
+ // delta m_2
+ rml_enkf_common_scaleA(Dk1 , data->Csc , false);
+ matrix_matmul(dA2 , Dk1 , X7);
+ matrix_inplace_sub(A, dA2);
+
+ matrix_free(Am);
+ matrix_free(Apr);
+ matrix_free(X4);
+ matrix_free(X5);
+ matrix_free(X6);
+ matrix_free(X7);
+ matrix_free(dA2);
+ matrix_free(Dk1);
+}
+
+// Initialize state and prior from A. Initialize lambda0, lambda. Call initA__, init1__
+static void rml_enkf_updateA_iter0(rml_enkf_data_type * data, matrix_type * A, matrix_type * S, matrix_type * R, matrix_type * dObs, matrix_type * E, matrix_type * D, matrix_type * Cd) {
+
+ int ens_size = matrix_get_columns( S );
+ int nrobs = matrix_get_rows( S );
+ int nrmin = util_int_min( ens_size , nrobs);
+ int state_size = matrix_get_rows( A );
+ matrix_type * Skm = matrix_alloc(ens_size, ens_size); // Mismatch
+ matrix_type * Ud = matrix_alloc( nrobs , nrmin ); /* Left singular vectors. */
+ matrix_type * VdT = matrix_alloc( nrmin , ens_size ); /* Right singular vectors. */
+ double * Wd = util_calloc( nrmin , sizeof * Wd );
+
+ data->Csc = util_calloc(state_size , sizeof * data->Csc);
+ data->Sk = enkf_linalg_data_mismatch(D,Cd,Skm);
+ data->Std = matrix_diag_std(Skm,data->Sk);
+
+ {
+ double lambda0 = rml_enkf_config_get_lambda0( data->config );
+ if (lambda0 < 0)
+ data->lambda = pow(10 , floor(log10(data->Sk/(2*nrobs))) );
+ else
+ data->lambda = lambda0;
+ }
+
+
+ // state = A
+ rml_enkf_common_store_state( data->previous_state , A , data->ens_mask );
+
+ // prior = A
+ data->global_prior = matrix_alloc_copy( data->previous_state );
+
+ // Update dependant on data mismatch
+ rml_enkf_initA__(data , A, S , Cd , E , D , Ud , Wd , VdT);
+ // Update dependant on prior mismatch. This should be zero (coz iter0).
+ // Therefore the purpose of init1__ is just to prepare some matrices.
+ if (rml_enkf_config_get_use_prior(data->config)) {
+ rml_enkf_init_Csc( data );
+ rml_enkf_init1__( data );
+ }
+
+ rml_enkf_write_iter_info(data, data->Sk , data->Sk, data->Std);
+
+ matrix_free( Skm );
+ matrix_free( Ud );
+ matrix_free( VdT );
+ free( Wd );
+}
+
+
+void rml_enkf_updateA(void * module_data,
+ matrix_type * A,
+ matrix_type * S,
+ matrix_type * R,
+ matrix_type * dObs,
+ matrix_type * E,
+ matrix_type * D,
+ const module_info_type * module_info,
+ rng_type * rng) {
+// A : ensemble matrix
+// R : (Inv?) Obs error cov.
+// S : measured ensemble
+// dObs: observed data
+// E : perturbations for obs
+// D = dObs + E - S : Innovations (wrt pert. obs)
+// module_info: Information on parameters/data for internal logging
+
+
+
+ double Sk_new; // Mismatch
+ double Std_new; // Std dev(Mismatch)
+ rml_enkf_data_type * data = rml_enkf_data_safe_cast( module_data );
+ int nrobs = matrix_get_rows( S ); // Num obs
+ int ens_size = matrix_get_columns( S ); // N
+ double nsc = 1/sqrt(ens_size-1); // Scale factor
+ matrix_type * Cd = matrix_alloc( nrobs, nrobs ); // Cov(E), where E = measurement perturbations?
+
+
+ // Empirical error covar. R is left unused. Investigate?
+ enkf_linalg_Covariance(Cd ,E ,nsc, nrobs); // Cd = SampCov(E) (including (N-1) normalization)
+ matrix_inv(Cd); // In-place inversion
+
+ rml_enkf_log_open(data->rml_log , data->iteration_nr);
+ fprintf(stdout,"\nIter %d --> %d", data->iteration_nr, data->iteration_nr + 1);
+
+
+ if (data->iteration_nr == 0) {
+ // IF ITERATION 0
+ rml_enkf_updateA_iter0(data , A , S , R , dObs , E , D , Cd);
+ data->iteration_nr++;
+ } else {
+ // IF ITERATION 1, 2, ...
+ int nrmin = util_int_min( ens_size , nrobs); // Min(p,N)
+ matrix_type * Ud = matrix_alloc( nrobs , nrmin ); // Left singular vectors. */
+ matrix_type * VdT = matrix_alloc( nrmin , ens_size ); // Right singular vectors. */
+ double * Wd = util_calloc( nrmin , sizeof * Wd ); // Singular values, vector
+ matrix_type * Skm = matrix_alloc(ens_size,ens_size); // Mismatch
+ Sk_new = enkf_linalg_data_mismatch(D,Cd,Skm); // Skm = D'*inv(Cd)*D; Sk_new = trace(Skm)/N
+ Std_new = matrix_diag_std(Skm,Sk_new); // Standard deviation of mismatches.
+
+
+ // Lambda = Normalized data mismatch (rounded)
+ if (rml_enkf_config_get_lambda_recalculate( data->config ))
+ data->lambda = pow(10 , floor(log10(Sk_new / (2*nrobs))) );
+
+ // Accept/Reject update? Lambda calculation.
+ {
+ bool mismatch_reduced = false;
+ bool std_reduced = false;
+
+ if (Sk_new < data->Sk)
+ mismatch_reduced = true;
+
+ if (Std_new <= data->Std)
+ std_reduced = true;
+
+ rml_enkf_write_iter_info(data, data->Sk , Sk_new, Std_new);
+
+ if (mismatch_reduced) {
+ /*
+ Stop check: if ( (1- (Sk_new/data->Sk)) < .0001) // check convergence ** model change norm has to be added in this!!
+ */
+
+ // Reduce Lambda
+ if (std_reduced)
+ data->lambda = data->lambda * rml_enkf_config_get_lambda_decrease_factor( data->config );
+
+ rml_enkf_common_store_state(data->previous_state , A , data->ens_mask );
+
+ data->Sk = Sk_new;
+ data->Std=Std_new;
+ data->iteration_nr++;
+ } else {
+ // Increase lambda
+ data->lambda = data->lambda * rml_enkf_config_get_lambda_increase_factor( data->config );
+ // A = data->previous_state
+ rml_enkf_common_recover_state( data->previous_state , A , data->ens_mask );
+ }
+ }
+
+ // Update dependant on data mismatch (delta m_1)
+ rml_enkf_initA__(data , A , S , Cd , E , D , Ud , Wd , VdT);
+
+ // Update dependant on prior mismatch (delta m_2)
+ if (rml_enkf_config_get_use_prior(data->config)) {
+ rml_enkf_init_Csc( data );
+ rml_enkf_init2__(data , A , Wd , VdT);
+ }
+
+ // Free
+ matrix_free(Skm);
+ matrix_free( Ud );
+ matrix_free( VdT );
+ free( Wd );
+ }
+
+ {
+ double lambda_min = rml_enkf_config_get_lambda_min( data->config );
+ if (data->lambda < lambda_min)
+ data->lambda = lambda_min;
+ }
+
+
+ rml_enkf_log_close( data->rml_log );
+ matrix_free(Cd);
+}
+
+
+
+void rml_enkf_init_update(void * arg,
+ const bool_vector_type * ens_mask,
+ const bool_vector_type * obs_mask,
+ const matrix_type * S,
+ const matrix_type * R,
+ const matrix_type * dObs,
+ const matrix_type * E,
+ const matrix_type * D,
+ rng_type * rng) {
+ rml_enkf_data_type * module_data = rml_enkf_data_safe_cast( arg );
+
+ if (module_data->ens_mask)
+ bool_vector_free( module_data->ens_mask );
+
+ module_data->ens_mask = bool_vector_alloc_copy( ens_mask );
+}
+
+
+
+
+
+
+
+//**********************************************
+// Set / Get basic types
+//**********************************************
+bool rml_enkf_set_int( void * arg , const char * var_name , int value) {
+ rml_enkf_data_type * module_data = rml_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , ENKF_NCOMP_KEY_) == 0)
+ rml_enkf_config_set_subspace_dimension(module_data->config , value);
+ else if (strcmp( var_name , ITER_KEY) == 0)
+ rml_enkf_set_iteration_nr( module_data , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+int rml_enkf_get_int( const void * arg, const char * var_name) {
+ const rml_enkf_data_type * module_data = rml_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , ITER_KEY) == 0)
+ return module_data->iteration_nr;
+ else
+ return -1;
+ }
+}
+
+bool rml_enkf_set_bool( void * arg , const char * var_name , bool value) {
+ rml_enkf_data_type * module_data = rml_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , USE_PRIOR_KEY) == 0)
+ rml_enkf_config_set_use_prior( module_data->config , value);
+ else if (strcmp( var_name , CLEAR_LOG_KEY) == 0)
+ rml_enkf_log_set_clear_log( module_data->rml_log , value );
+ else if (strcmp( var_name , LAMBDA_RECALCULATE_KEY) == 0)
+ rml_enkf_config_set_lambda_recalculate( module_data->config , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+bool rml_enkf_get_bool( const void * arg, const char * var_name) {
+ const rml_enkf_data_type * module_data = rml_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , USE_PRIOR_KEY) == 0)
+ return rml_enkf_config_get_use_prior( module_data->config );
+ else if (strcmp(var_name , CLEAR_LOG_KEY) == 0)
+ return rml_enkf_log_get_clear_log( module_data->rml_log );
+ else if (strcmp(var_name , LAMBDA_RECALCULATE_KEY) == 0)
+ return rml_enkf_config_get_lambda_recalculate( module_data->config );
+ else
+ return false;
+ }
+}
+
+bool rml_enkf_set_double( void * arg , const char * var_name , double value) {
+ rml_enkf_data_type * module_data = rml_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , ENKF_TRUNCATION_KEY_) == 0)
+ rml_enkf_config_set_truncation( module_data->config , value );
+ else if (strcmp( var_name , LAMBDA_INCREASE_FACTOR_KEY) == 0)
+ rml_enkf_config_set_lambda_increase_factor( module_data->config , value );
+ else if (strcmp( var_name , LAMBDA_REDUCE_FACTOR_KEY) == 0)
+ rml_enkf_config_set_lambda_decrease_factor( module_data->config , value );
+ else if (strcmp( var_name , LAMBDA0_KEY) == 0)
+ rml_enkf_config_set_lambda0( module_data->config , value );
+ else if (strcmp( var_name , LAMBDA_MIN_KEY) == 0)
+ rml_enkf_config_set_lambda_min( module_data->config , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+double rml_enkf_get_double( const void * arg, const char * var_name) {
+ const rml_enkf_data_type * module_data = rml_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , LAMBDA_REDUCE_FACTOR_KEY) == 0)
+ return rml_enkf_config_get_lambda_decrease_factor(module_data->config);
+
+ if (strcmp(var_name , LAMBDA_INCREASE_FACTOR_KEY) == 0)
+ return rml_enkf_config_get_lambda_increase_factor(module_data->config);
+
+ if (strcmp(var_name , LAMBDA0_KEY) == 0)
+ return rml_enkf_config_get_lambda0(module_data->config);
+
+ if (strcmp(var_name , LAMBDA_MIN_KEY) == 0)
+ return rml_enkf_config_get_lambda_min(module_data->config);
+
+ if (strcmp(var_name , ENKF_TRUNCATION_KEY_) == 0)
+ return rml_enkf_config_get_truncation( module_data->config );
+
+ return -1;
+ }
+}
+
+
+bool rml_enkf_set_string( void * arg , const char * var_name , const char * value) {
+ rml_enkf_data_type * module_data = rml_enkf_data_safe_cast( arg );
+ {
+ bool name_recognized = true;
+
+ if (strcmp( var_name , LOG_FILE_KEY) == 0)
+ rml_enkf_log_set_log_file( module_data->rml_log , value );
+ else
+ name_recognized = false;
+
+ return name_recognized;
+ }
+}
+
+long rml_enkf_get_options( void * arg , long flag ) {
+ rml_enkf_data_type * module_data = rml_enkf_data_safe_cast( arg );
+ {
+ return rml_enkf_config_get_option_flags( module_data->config );
+ }
+}
+
+bool rml_enkf_has_var( const void * arg, const char * var_name) {
+ {
+ if (strcmp(var_name , ITER_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , USE_PRIOR_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , LAMBDA_INCREASE_FACTOR_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , LAMBDA_REDUCE_FACTOR_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , LAMBDA0_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , LAMBDA_MIN_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , LAMBDA_RECALCULATE_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , ENKF_TRUNCATION_KEY_) == 0)
+ return true;
+ else if (strcmp(var_name , LOG_FILE_KEY) == 0)
+ return true;
+ else if (strcmp(var_name , CLEAR_LOG_KEY) == 0)
+ return true;
+ else
+ return false;
+ }
+}
+
+void * rml_enkf_get_ptr( const void * arg , const char * var_name ) {
+ const rml_enkf_data_type * module_data = rml_enkf_data_safe_cast_const( arg );
+ {
+ if (strcmp(var_name , LOG_FILE_KEY) == 0)
+ return (void *) rml_enkf_log_get_log_file( module_data->rml_log );
+ else
+ return NULL;
+ }
+}
+
+
+
+
+
+
+//**********************************************
+// Symbol table
+//**********************************************
+#ifdef INTERNAL_LINK
+#define LINK_NAME RML_ENKF
+#else
+#define LINK_NAME EXTERNAL_MODULE_SYMBOL
+#endif
+
+
+analysis_table_type LINK_NAME = {
+ .name = "RML_ENKF",
+ .alloc = rml_enkf_data_alloc,
+ .freef = rml_enkf_data_free,
+ .set_int = rml_enkf_set_int ,
+ .set_double = rml_enkf_set_double ,
+ .set_bool = rml_enkf_set_bool,
+ .set_string = rml_enkf_set_string,
+ .get_options = rml_enkf_get_options ,
+ .initX = NULL,
+ .updateA = rml_enkf_updateA ,
+ .init_update = rml_enkf_init_update ,
+ .complete_update = NULL,
+ .has_var = rml_enkf_has_var,
+ .get_int = rml_enkf_get_int,
+ .get_double = rml_enkf_get_double,
+ .get_bool = rml_enkf_get_bool,
+ .get_ptr = rml_enkf_get_ptr,
+};
+
diff --git a/libres/lib/analysis/modules/rml_enkf_common.c b/libres/lib/analysis/modules/rml_enkf_common.c
new file mode 100644
index 00000000000..fb12d42573f
--- /dev/null
+++ b/libres/lib/analysis/modules/rml_enkf_common.c
@@ -0,0 +1,80 @@
+/*
+ Copyright (C) 2011 Equinor ASA, Norway.
+
+ The file 'rml_enkf.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+#include
+
+#include
+
+
+// Explanation
+// zzz_enkf_common_store_state( state , A ,ens_mask) assigns A to state. RESIZES state to rows(A)-by-LEN(ens_mask)
+// zzz_enkf_common_recover_state(state , A ,ens_mask) assigns state to A. RESIZES A to rows(state)-by-SUM(ens_mask)
+
+
+void rml_enkf_common_store_state( matrix_type * state , const matrix_type * A , const bool_vector_type * ens_mask ) {
+ matrix_resize( state , matrix_get_rows( A ) , bool_vector_size( ens_mask ) , false);
+ {
+ const int ens_size = bool_vector_size( ens_mask );
+ int active_index = 0;
+ for (int iens = 0; iens < ens_size; iens++) {
+ if (bool_vector_iget( ens_mask , iens )) {
+ matrix_copy_column( state , A , iens , active_index );
+ active_index++;
+ } else
+ matrix_set_const_column( state , iens , 0);
+ }
+ }
+}
+
+
+
+void rml_enkf_common_recover_state( const matrix_type * state , matrix_type * A , const bool_vector_type * ens_mask ) {
+ const int ens_size = bool_vector_size( ens_mask );
+ const int active_size = bool_vector_count_equal( ens_mask , true );
+ const int rows = matrix_get_rows( state );
+
+ matrix_resize( A , rows , active_size , false );
+ {
+ int active_index = 0;
+ for (int iens = 0; iens < ens_size; iens++) {
+ if (bool_vector_iget( ens_mask , iens )) {
+ matrix_copy_column( A , state , active_index , iens );
+ active_index++;
+ }
+ }
+ }
+}
+
+
+
+// Scale rows by the entries in the vector Csc
+void rml_enkf_common_scaleA(matrix_type *A , const double * Csc, bool invert ){
+ int nrows = matrix_get_rows(A);
+ if (invert) {
+ for (int i=0; i< nrows ; i++) {
+ double sc= 1/Csc[i];
+ matrix_scale_row(A, i, sc);
+ }
+ } else {
+ for (int i=0; i< nrows ; i++) {
+ double sc= Csc[i];
+ matrix_scale_row(A, i, sc);
+ }
+ }
+}
diff --git a/libres/lib/analysis/modules/rml_enkf_common.h b/libres/lib/analysis/modules/rml_enkf_common.h
new file mode 100644
index 00000000000..ead6fd03600
--- /dev/null
+++ b/libres/lib/analysis/modules/rml_enkf_common.h
@@ -0,0 +1,15 @@
+#ifndef ERT_RML_ENKF_COMMON_H
+#define ERT_RML_ENKF_COMMON_H
+
+#include
+
+#include
+#include
+
+#include
+
+void rml_enkf_common_store_state( matrix_type * state , const matrix_type * A , const bool_vector_type * ens_mask );
+void rml_enkf_common_recover_state( const matrix_type * state , matrix_type * A , const bool_vector_type * ens_mask );
+void rml_enkf_common_scaleA(matrix_type *A , const double * Csc, bool invert );
+
+#endif
diff --git a/libres/lib/analysis/modules/rml_enkf_config.c b/libres/lib/analysis/modules/rml_enkf_config.c
new file mode 100644
index 00000000000..253b31d3069
--- /dev/null
+++ b/libres/lib/analysis/modules/rml_enkf_config.c
@@ -0,0 +1,168 @@
+/*
+ Copyright (C) 2015 Equinor ASA, Norway.
+
+ The file 'rml_enkf_config.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+
+#define INVALID_SUBSPACE_DIMENSION -1
+#define INVALID_TRUNCATION -1
+
+#define DEFAULT_SUBSPACE_DIMENSION INVALID_SUBSPACE_DIMENSION
+#define DEFAULT_USE_PRIOR true
+
+#define DEFAULT_LAMBDA_INCREASE_FACTOR 4
+#define DEFAULT_LAMBDA_REDUCE_FACTOR 0.1
+#define DEFAULT_LAMBDA0 -1
+#define DEFAULT_LAMBDA_MIN 0.01
+#define DEFAULT_LAMBDA_RECALCULATE false
+
+
+
+#define RML_ENKF_CONFIG_TYPE_ID 61400061
+
+struct rml_enkf_config_struct {
+ UTIL_TYPE_ID_DECLARATION;
+ double truncation; // Controlled by config key: ENKF_TRUNCATION_KEY
+ int subspace_dimension; // Controlled by config key: ENKF_NCOMP_KEY (-1: use Truncation instead)
+ long option_flags;
+ bool use_prior; // Use exact/approximate scheme? Approximate scheme drops the "prior" term in the LM step.
+
+
+ double lambda0;
+ double lambda_min;
+ double lambda_decrease_factor;
+ double lambda_increase_factor;
+ bool lambda_recalculate;
+};
+
+
+
+
+
+
+
+rml_enkf_config_type * rml_enkf_config_alloc() {
+ rml_enkf_config_type * config = util_malloc( sizeof * config );
+ UTIL_TYPE_ID_INIT( config , RML_ENKF_CONFIG_TYPE_ID );
+
+ rml_enkf_config_set_truncation( config , DEFAULT_ENKF_TRUNCATION_);
+ rml_enkf_config_set_subspace_dimension( config , DEFAULT_SUBSPACE_DIMENSION);
+ rml_enkf_config_set_use_prior( config , DEFAULT_USE_PRIOR );
+ rml_enkf_config_set_option_flags( config , ANALYSIS_NEED_ED + ANALYSIS_UPDATE_A + ANALYSIS_ITERABLE + ANALYSIS_SCALE_DATA);
+
+ rml_enkf_config_set_lambda_min( config , DEFAULT_LAMBDA_MIN );
+ rml_enkf_config_set_lambda0( config , DEFAULT_LAMBDA0 );
+ rml_enkf_config_set_lambda_decrease_factor( config , DEFAULT_LAMBDA_REDUCE_FACTOR );
+ rml_enkf_config_set_lambda_increase_factor( config , DEFAULT_LAMBDA_INCREASE_FACTOR );
+ rml_enkf_config_set_lambda_recalculate( config , DEFAULT_LAMBDA_RECALCULATE );
+
+ return config;
+}
+
+
+bool rml_enkf_config_get_use_prior( const rml_enkf_config_type * config ) {
+ return config->use_prior;
+}
+
+void rml_enkf_config_set_use_prior( rml_enkf_config_type * config , bool use_prior) {
+ config->use_prior = use_prior;
+}
+
+
+double rml_enkf_config_get_truncation( rml_enkf_config_type * config ) {
+ return config->truncation;
+}
+
+void rml_enkf_config_set_truncation( rml_enkf_config_type * config , double truncation) {
+ config->truncation = truncation;
+ if (truncation > 0.0)
+ config->subspace_dimension = INVALID_SUBSPACE_DIMENSION;
+}
+
+int rml_enkf_config_get_subspace_dimension( rml_enkf_config_type * config ) {
+ return config->subspace_dimension;
+}
+
+void rml_enkf_config_set_subspace_dimension( rml_enkf_config_type * config , int subspace_dimension) {
+ config->subspace_dimension = subspace_dimension;
+ if (subspace_dimension > 0)
+ config->truncation = INVALID_TRUNCATION;
+}
+
+void rml_enkf_config_set_option_flags( rml_enkf_config_type * config , long flags) {
+ config->option_flags = flags;
+}
+
+long rml_enkf_config_get_option_flags( const rml_enkf_config_type * config ) {
+ return config->option_flags;
+}
+
+double rml_enkf_config_get_lambda0( rml_enkf_config_type * config ) {
+ return config->lambda0;
+}
+
+void rml_enkf_config_set_lambda0( rml_enkf_config_type * config , double lambda0) {
+ config->lambda0 = lambda0;
+}
+
+
+double rml_enkf_config_get_lambda_min( rml_enkf_config_type * config ) {
+ return config->lambda_min;
+}
+
+void rml_enkf_config_set_lambda_min( rml_enkf_config_type * config , double lambda_min) {
+ config->lambda_min = lambda_min;
+}
+
+
+double rml_enkf_config_get_lambda_increase_factor( rml_enkf_config_type * config ) {
+ return config->lambda_increase_factor;
+}
+
+void rml_enkf_config_set_lambda_increase_factor( rml_enkf_config_type * config , double lambda_increase_factor) {
+ config->lambda_increase_factor = lambda_increase_factor;
+}
+
+double rml_enkf_config_get_lambda_decrease_factor( rml_enkf_config_type * config ) {
+ return config->lambda_decrease_factor;
+}
+
+void rml_enkf_config_set_lambda_decrease_factor( rml_enkf_config_type * config , double lambda_decrease_factor) {
+ config->lambda_decrease_factor = lambda_decrease_factor;
+}
+
+
+bool rml_enkf_config_get_lambda_recalculate( const rml_enkf_config_type * config ) {
+ return config->lambda_recalculate;
+}
+
+void rml_enkf_config_set_lambda_recalculate( rml_enkf_config_type * config , bool lambda_recalculate) {
+ config->lambda_recalculate = lambda_recalculate;
+}
+
+
+void rml_enkf_config_free(rml_enkf_config_type * config) {
+ free( config );
+}
diff --git a/libres/lib/analysis/modules/rml_enkf_config.h b/libres/lib/analysis/modules/rml_enkf_config.h
new file mode 100644
index 00000000000..516a7918289
--- /dev/null
+++ b/libres/lib/analysis/modules/rml_enkf_config.h
@@ -0,0 +1,64 @@
+/*
+ Copyright (C) 2015 Equinor ASA, Norway.
+
+ The file 'rml_enkf_config.h' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#ifndef RML_ENKF_CONFIG_H
+#define RML_ENKF_CONFIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct rml_enkf_config_struct rml_enkf_config_type;
+
+
+ rml_enkf_config_type * rml_enkf_config_alloc();
+ void rml_enkf_config_free(rml_enkf_config_type * config);
+
+ int rml_enkf_config_get_subspace_dimension( rml_enkf_config_type * config );
+ void rml_enkf_config_set_subspace_dimension( rml_enkf_config_type * config , int subspace_dimension);
+
+ double rml_enkf_config_get_truncation( rml_enkf_config_type * config );
+ void rml_enkf_config_set_truncation( rml_enkf_config_type * config , double truncation);
+
+ bool rml_enkf_config_get_use_prior( const rml_enkf_config_type * config );
+ void rml_enkf_config_set_use_prior( rml_enkf_config_type * config , bool use_prior);
+
+ void rml_enkf_config_set_option_flags( rml_enkf_config_type * config , long flags);
+ long rml_enkf_config_get_option_flags( const rml_enkf_config_type * config );
+
+ double rml_enkf_config_get_lambda0( rml_enkf_config_type * config );
+ void rml_enkf_config_set_lambda0( rml_enkf_config_type * config , double lambda0);
+
+ double rml_enkf_config_get_lambda_min( rml_enkf_config_type * config );
+ void rml_enkf_config_set_lambda_min( rml_enkf_config_type * config , double lambda_min);
+
+ double rml_enkf_config_get_lambda_increase_factor( rml_enkf_config_type * config );
+ void rml_enkf_config_set_lambda_increase_factor( rml_enkf_config_type * config , double lambda_increase_factor);
+
+ double rml_enkf_config_get_lambda_decrease_factor( rml_enkf_config_type * config );
+ void rml_enkf_config_set_lambda_decrease_factor( rml_enkf_config_type * config , double lambda_decrease_factor);
+
+ bool rml_enkf_config_get_lambda_recalculate( const rml_enkf_config_type * config );
+ void rml_enkf_config_set_lambda_recalculate( rml_enkf_config_type * config , bool lambda_recalculate);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libres/lib/analysis/modules/rml_enkf_log.c b/libres/lib/analysis/modules/rml_enkf_log.c
new file mode 100644
index 00000000000..3ed5c17c422
--- /dev/null
+++ b/libres/lib/analysis/modules/rml_enkf_log.c
@@ -0,0 +1,110 @@
+/*
+ Copyright (C) 2015 Equinor ASA, Norway.
+
+ The file 'rml_enkf_log.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+
+#include
+#include
+
+#include
+
+#include
+
+#define DEFAULT_LOG_FILE "rml_enkf.out"
+#define DEFAULT_CLEAR_LOG true
+
+struct rml_enkf_log_struct {
+ bool clear_log;
+ char * log_file;
+ FILE * log_stream;
+};
+
+
+rml_enkf_log_type * rml_enkf_log_alloc() {
+ rml_enkf_log_type * rml_log = util_malloc( sizeof * rml_log );
+ rml_log->log_file = NULL;
+ rml_log->log_stream = NULL;
+ rml_enkf_log_set_clear_log( rml_log , DEFAULT_CLEAR_LOG );
+
+ rml_enkf_log_set_log_file( rml_log, DEFAULT_LOG_FILE );
+ return rml_log;
+}
+
+bool rml_enkf_log_get_clear_log( const rml_enkf_log_type * data ) {
+ return data->clear_log;
+}
+
+void rml_enkf_log_set_clear_log( rml_enkf_log_type * data , bool clear_log) {
+ data->clear_log = clear_log;
+}
+
+void rml_enkf_log_set_log_file( rml_enkf_log_type * data , const char * log_file ) {
+ data->log_file = util_realloc_string_copy( data->log_file , log_file );
+}
+
+const char * rml_enkf_log_get_log_file( const rml_enkf_log_type * data) {
+ return data->log_file;
+}
+
+
+void rml_enkf_log_free(rml_enkf_log_type * rml_log) {
+ rml_enkf_log_close( rml_log );
+ free( rml_log->log_file );
+ free( rml_log );
+}
+
+
+void rml_enkf_log_open( rml_enkf_log_type * rml_log , int iteration_nr ) {
+ if (rml_log->log_file) {
+ if ( iteration_nr == 0) {
+
+ if (rml_log->clear_log)
+ rml_log->log_stream = util_mkdir_fopen( rml_log->log_file , "w");
+ else
+ rml_log->log_stream = util_mkdir_fopen( rml_log->log_file , "a");
+
+ } else
+ rml_log->log_stream = util_fopen( rml_log->log_file , "a");
+ }
+}
+
+
+bool rml_enkf_log_is_open( const rml_enkf_log_type * rml_log ) {
+ if (rml_log->log_stream)
+ return true;
+ else
+ return false;
+}
+
+
+void rml_enkf_log_close( rml_enkf_log_type * rml_log ) {
+ if (rml_log->log_stream)
+ fclose( rml_log->log_stream );
+
+ rml_log->log_stream = NULL;
+}
+
+
+void rml_enkf_log_line( rml_enkf_log_type * rml_log , const char * fmt , ...) {
+ if (rml_log->log_stream) {
+ va_list ap;
+ va_start(ap , fmt);
+ vfprintf( rml_log->log_stream , fmt , ap );
+ va_end( ap );
+ }
+}
+
diff --git a/libres/lib/analysis/modules/rml_enkf_log.h b/libres/lib/analysis/modules/rml_enkf_log.h
new file mode 100644
index 00000000000..fc189f4fe0a
--- /dev/null
+++ b/libres/lib/analysis/modules/rml_enkf_log.h
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2015 Equinor ASA, Norway.
+
+ The file 'rml_enkf_log.h' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#ifndef RML_ENKF_LOG_H
+#define RML_ENKF_LOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+ typedef struct rml_enkf_log_struct rml_enkf_log_type;
+
+ rml_enkf_log_type * rml_enkf_log_alloc();
+ void rml_enkf_log_free(rml_enkf_log_type * rml_log);
+ bool rml_enkf_log_get_clear_log( const rml_enkf_log_type * data );
+ void rml_enkf_log_set_clear_log( rml_enkf_log_type * data , bool clear_log);
+ void rml_enkf_log_set_log_file( rml_enkf_log_type * data , const char * log_file );
+ const char * rml_enkf_log_get_log_file( const rml_enkf_log_type * data);
+ void rml_enkf_log_open( rml_enkf_log_type * rml_log , int iteration_nr );
+ void rml_enkf_log_close( rml_enkf_log_type * rml_log );
+ void rml_enkf_log_line( rml_enkf_log_type * rml_log , const char * fmt , ...);
+ bool rml_enkf_log_is_open( const rml_enkf_log_type * rml_log );
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
diff --git a/libres/lib/analysis/modules/std_enkf_debug.c b/libres/lib/analysis/modules/std_enkf_debug.c
new file mode 100644
index 00000000000..5c0df808fde
--- /dev/null
+++ b/libres/lib/analysis/modules/std_enkf_debug.c
@@ -0,0 +1,243 @@
+/*
+ Copyright (C) 2016 Equinor ASA, Norway.
+
+ This file is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#define PREFIX_KEY "PREFIX"
+#define DEFAULT_PREFIX "debug"
+
+/*
+ A random 'magic' integer id which is used for run-time type checking
+ of the input data.
+*/
+#define STD_ENKF_DEBUG_TYPE_ID 269923
+
+typedef struct std_enkf_debug_data_struct std_enkf_debug_data_type;
+
+struct std_enkf_debug_data_struct {
+ UTIL_TYPE_ID_DECLARATION;
+ std_enkf_data_type * std_data;
+ char * prefix;
+ int update_count;
+};
+
+
+static UTIL_SAFE_CAST_FUNCTION_CONST( std_enkf_debug_data , STD_ENKF_DEBUG_TYPE_ID )
+static UTIL_SAFE_CAST_FUNCTION( std_enkf_debug_data , STD_ENKF_DEBUG_TYPE_ID )
+
+
+void std_enkf_debug_data_set_prefix( std_enkf_debug_data_type * data , const char * prefix ) {
+ data->prefix = util_realloc_string_copy( data->prefix , prefix );
+}
+
+
+void * std_enkf_debug_data_alloc( rng_type * rng) {
+ std_enkf_debug_data_type * data = util_malloc( sizeof * data );
+ UTIL_TYPE_ID_INIT( data , STD_ENKF_DEBUG_TYPE_ID );
+ data->std_data = std_enkf_data_alloc();
+
+ data->update_count = 0;
+ data->prefix = NULL;
+ std_enkf_debug_data_set_prefix( data , DEFAULT_PREFIX );
+ return data;
+}
+
+
+void std_enkf_debug_data_free( void * arg ) {
+ std_enkf_debug_data_type * data = std_enkf_debug_data_safe_cast( arg );
+ std_enkf_data_free( data->std_data );
+ free( data );
+}
+
+void std_enkf_debug_save_matrix( const matrix_type * m , const char * path , const char * file, bool transpose) {
+ char * filename = util_alloc_filename( path , file , NULL );
+
+ if (transpose) {
+ matrix_type * mt = matrix_alloc_transpose( m );
+ matrix_dump_csv(mt,filename);
+ matrix_free( mt );
+ } else
+ matrix_dump_csv(m , filename);
+
+
+ free( filename );
+}
+
+
+
+void std_enkf_debug_initX(void * module_data ,
+ matrix_type * X ,
+ matrix_type * A ,
+ matrix_type * S ,
+ matrix_type * R ,
+ matrix_type * dObs ,
+ matrix_type * E ,
+ matrix_type * D,
+ rng_type * rng) {
+
+ std_enkf_debug_data_type * data = std_enkf_debug_data_safe_cast( module_data );
+ char * debug_path = util_alloc_sprintf( "%s/%d" , data->prefix , data->update_count );
+ std_enkf_data_type * std_data = data->std_data;
+ util_make_path( debug_path );
+ std_enkf_debug_save_matrix( A , debug_path , "prior_ert.csv" , true);
+
+ std_enkf_debug_save_matrix( S , debug_path , "S.csv" , true);
+ std_enkf_debug_save_matrix( S , debug_path , "simResponses.csv" , true);
+
+ std_enkf_debug_save_matrix( E , debug_path , "E.csv" , true);
+ std_enkf_debug_save_matrix( E , debug_path , "measurementErrors.csv" , true);
+
+ std_enkf_debug_save_matrix( R , debug_path , "R.csv" , true);
+ std_enkf_debug_save_matrix( D , debug_path , "D.csv" , true);
+ {
+ matrix_type * value = matrix_alloc_sub_copy( dObs , 0 , 0 , matrix_get_rows( dObs ) , 1 );
+ matrix_type * std = matrix_alloc_sub_copy( dObs , 0 , 1 , matrix_get_rows( dObs ) , 1 );
+
+ std_enkf_debug_save_matrix( value , debug_path , "observations.csv", true);
+ std_enkf_debug_save_matrix( std , debug_path , "observations_std.csv", true);
+
+ matrix_free( value );
+ matrix_free( std );
+ }
+ std_enkf_initX( std_data , X , A , S , R , dObs , E , D, rng);
+ {
+ matrix_type * posterior = matrix_alloc_matmul( A , X );
+ std_enkf_debug_save_matrix( posterior , debug_path , "posterior_ert.csv" , true);
+ matrix_free( posterior );
+ }
+
+ data->update_count++;
+}
+
+
+
+
+
+
+
+bool std_enkf_debug_set_double( void * arg , const char * var_name , double value) {
+ std_enkf_debug_data_type * module_data = std_enkf_debug_data_safe_cast( arg );
+ return std_enkf_set_double( module_data->std_data , var_name , value );
+}
+
+
+bool std_enkf_debug_set_int( void * arg , const char * var_name , int value) {
+ std_enkf_debug_data_type * module_data = std_enkf_debug_data_safe_cast( arg );
+ return std_enkf_set_int( module_data->std_data , var_name , value );
+}
+
+
+bool std_enkf_debug_set_bool( void * arg , const char * var_name , bool value) {
+ std_enkf_debug_data_type * module_data = std_enkf_debug_data_safe_cast( arg );
+ return std_enkf_set_bool( module_data->std_data , var_name , value );
+}
+
+
+bool std_enkf_debug_set_string( void * arg , const char * var_name , const char * value) {
+ std_enkf_debug_data_type * module_data = std_enkf_debug_data_safe_cast( arg );
+ if (strcmp(var_name, PREFIX_KEY) == 0) {
+ std_enkf_debug_data_set_prefix( module_data , value );
+ return true;
+ } else
+ return false;
+}
+
+
+
+long std_enkf_debug_get_options( void * arg , long flag ) {
+ const std_enkf_debug_data_type * module_data = std_enkf_debug_data_safe_cast_const( arg );
+ long options = std_enkf_get_options( module_data->std_data , flag );
+ options |= ANALYSIS_USE_A;
+ return options;
+}
+
+
+bool std_enkf_debug_has_var( const void * arg, const char * var_name) {
+ if (strcmp(var_name , PREFIX_KEY) == 0)
+ return true;
+ else {
+ const std_enkf_debug_data_type * module_data = std_enkf_debug_data_safe_cast_const( arg );
+ return std_enkf_has_var( module_data->std_data , var_name );
+ }
+}
+
+
+double std_enkf_debug_get_double( const void * arg, const char * var_name) {
+ const std_enkf_debug_data_type * module_data = std_enkf_debug_data_safe_cast_const( arg );
+ return std_enkf_get_double( module_data->std_data , var_name );
+}
+
+int std_enkf_debug_get_int( const void * arg, const char * var_name) {
+ const std_enkf_debug_data_type * module_data = std_enkf_debug_data_safe_cast_const( arg );
+ return std_enkf_get_int( module_data->std_data , var_name );
+}
+
+
+bool std_enkf_debug_get_bool( const void * arg, const char * var_name) {
+ const std_enkf_debug_data_type * module_data = std_enkf_debug_data_safe_cast_const( arg );
+ return std_enkf_get_bool( module_data->std_data , var_name );
+}
+
+
+void * std_enkf_debug_get_ptr( const void * arg, const char * var_name) {
+ const std_enkf_debug_data_type * module_data = std_enkf_debug_data_safe_cast_const( arg );
+ if (strcmp( var_name , PREFIX_KEY) == 0)
+ return (void *) module_data->prefix;
+ else
+ return NULL;
+}
+
+
+
+#ifdef INTERNAL_LINK
+#define LINK_NAME STD_ENKF_DEBUG
+#else
+#define LINK_NAME EXTERNAL_MODULE_SYMBOL
+#endif
+
+
+analysis_table_type LINK_NAME = {
+ .name = "STD_ENKF_DEBUG_DEBUG",
+ .alloc = std_enkf_debug_data_alloc,
+ .freef = std_enkf_debug_data_free,
+ .set_int = std_enkf_debug_set_int ,
+ .set_double = std_enkf_debug_set_double ,
+ .set_bool = std_enkf_debug_set_bool,
+ .set_string = std_enkf_debug_set_string,
+ .get_options = std_enkf_debug_get_options ,
+ .initX = std_enkf_debug_initX ,
+ .updateA = NULL,
+ .init_update = NULL,
+ .complete_update = NULL,
+ .has_var = std_enkf_debug_has_var,
+ .get_int = std_enkf_debug_get_int,
+ .get_double = std_enkf_debug_get_double,
+ .get_bool = std_enkf_debug_get_bool,
+ .get_ptr = std_enkf_debug_get_ptr
+};
+
diff --git a/libres/lib/analysis/modules/tecplot.c b/libres/lib/analysis/modules/tecplot.c
new file mode 100644
index 00000000000..baaf38a1fac
--- /dev/null
+++ b/libres/lib/analysis/modules/tecplot.c
@@ -0,0 +1,79 @@
+void static teccost(const matrix_type * W,
+ const matrix_type * D,
+ const char * fname,
+ const int ens_size,
+ const int izone)
+{
+ FILE *fp;
+ float costJ[ens_size];
+
+ if (izone == 1){
+ fp = fopen(fname, "w");
+ fprintf(fp, "TITLE = \"%s\"\n",fname);
+ fprintf(fp, "VARIABLES = \"i\" \"Average J\" ");
+ for (int i = 0; i < ens_size; i++)
+ fprintf(fp, "\"%d\" ",i);
+ fprintf(fp,"\n");
+ } else {
+ fp = fopen(fname, "a");
+ }
+
+ float costf=0.0;
+ for (int i = 0; i < ens_size; i++){
+ costJ[i]=matrix_column_column_dot_product(W,i,W,i)+matrix_column_column_dot_product(D,i,D,i);
+ costf += costJ[i];
+ }
+ costf= costf/ens_size;
+
+ fprintf(fp,"%2d %10.3f ", izone-1, costf) ;
+ for (int i = 0; i < ens_size; i++)
+ fprintf(fp,"%10.3f ", costJ[i] ) ;
+ fprintf(fp,"\n") ;
+ fclose(fp);
+}
+
+void static teclog(const matrix_type * W,
+ const matrix_type * D,
+ const matrix_type * DW,
+ const char * fname,
+ const int ens_size,
+ const int itnr,
+ const double rcond,
+ const int nrsing,
+ const int nrobs)
+{
+
+ double diff1W = 0.0;
+ double diff2W = 0.0;
+ double diffW;
+ double costfp,costfd,costf;
+ FILE *fp;
+
+ if (itnr == 1){
+ fp = fopen(fname, "w");
+ fprintf(fp, "TITLE = \"%s\"\n",fname);
+ fprintf(fp, "VARIABLES = \"it\" \"Diff1W\" \"Diff2W\" \"J-prior\" \"J-data\" \"J\" \"rcond\" \"Sing\" \"nrobs\"\n");
+ } else {
+ fp = fopen(fname, "a");
+ }
+
+ for (int i = 0; i < ens_size; i++){
+ diffW=matrix_column_column_dot_product(DW,i,DW,i)/ens_size;
+ diff2W+=diffW;
+ if (diffW > diff1W) diff1W=diffW ;
+ }
+ diff2W=diff2W/ens_size;
+
+ costfp=0.0;
+ costfd=0.0;
+ for (int i = 0; i < ens_size; i++){
+ costfp += matrix_column_column_dot_product(W,i,W,i);
+ costfd += matrix_column_column_dot_product(D,i,D,i);
+ }
+ costfp=costfp/ens_size;
+ costfd=costfd/ens_size;
+ costf= costfp + costfd;
+ fprintf(fp," %d %12.5e %12.5e %12.5e %12.5e %12.5e %12.5e %d %d\n", itnr-1, diff1W, diff2W, costfp, costfd, costf, rcond, nrsing , nrobs ) ;
+ fclose(fp);
+}
+
diff --git a/libres/lib/analysis/modules/tests/analysis_rml_enkf_common.c b/libres/lib/analysis/modules/tests/analysis_rml_enkf_common.c
new file mode 100644
index 00000000000..2133e1a4c14
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/analysis_rml_enkf_common.c
@@ -0,0 +1,133 @@
+/*
+ Copyright (C) 2014 Equinor ASA, Norway.
+
+ The file 'analysis_rml_common.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+
+
+void test_store_recover_state() {
+ rng_type * rng = rng_alloc( MZRAN , INIT_DEFAULT );
+ int ens_size = 10;
+ int active_size = 8;
+ int rows = 100;
+ matrix_type * state = matrix_alloc(1,1);
+ bool_vector_type * ens_mask = bool_vector_alloc(ens_size , false);
+ matrix_type * A = matrix_alloc( rows , active_size);
+ matrix_type * A2 = matrix_alloc( rows, active_size );
+ matrix_type * A3 = matrix_alloc( 1,1 );
+
+ for (int i=0; i < active_size; i++)
+ bool_vector_iset( ens_mask , i + 1 , true );
+
+ matrix_random_init(A , rng);
+ rml_enkf_common_store_state( state , A , ens_mask );
+
+ test_assert_int_equal( matrix_get_rows( state ) , rows );
+ test_assert_int_equal( matrix_get_columns( state ) , ens_size );
+
+ {
+ int g;
+ int a = 0;
+ for (g=0; g < ens_size; g++) {
+ if (bool_vector_iget( ens_mask , g )) {
+ test_assert_true( matrix_columns_equal( state , g , A , a ));
+ a++;
+ }
+ }
+ }
+
+ rml_enkf_common_recover_state( state , A2 , ens_mask);
+ rml_enkf_common_recover_state( state , A3 , ens_mask);
+ test_assert_true( matrix_equal( A , A2 ));
+ test_assert_true( matrix_equal( A , A3 ));
+
+ bool_vector_free( ens_mask );
+ matrix_free( state );
+ matrix_free( A );
+}
+
+
+
+
+
+void test_scaleA() {
+ const int N = 10;
+ matrix_type * m1 = matrix_alloc(N , N);
+ matrix_type * m2 = matrix_alloc(N , N);
+ double * csc = util_calloc( N , sizeof * csc );
+ rng_type * rng = rng_alloc( MZRAN , INIT_DEFAULT );
+
+ matrix_random_init( m1 , rng );
+ matrix_assign(m2 , m1);
+ test_assert_true( matrix_equal(m1 , m2));
+
+ {
+ for (int i=0; i < N; i++)
+ csc[i] = (i + 2);
+ }
+ rml_enkf_common_scaleA( m1 , csc , false );
+ {
+ int row,col;
+ for (row = 0; row < N; row++) {
+ for (col=0; col < N; col++) {
+ double v1 = matrix_iget(m1 , row , col);
+ double v2 = matrix_iget(m2 , row , col);
+
+ test_assert_double_equal( v1 , v2 * csc[row] );
+ }
+ }
+ }
+ rml_enkf_common_scaleA( m2 , csc , false );
+ test_assert_true( matrix_equal(m1 , m2));
+
+ rml_enkf_common_scaleA( m2 , csc , true );
+ {
+ int row,col;
+ for (row = 0; row < N; row++) {
+ for (col=0; col < N; col++) {
+ double v1 = matrix_iget(m1 , row , col);
+ double v2 = matrix_iget(m2 , row , col);
+
+ test_assert_double_equal( v1 , v2 * csc[row] );
+ }
+ }
+ }
+ rml_enkf_common_scaleA( m1 , csc , true );
+ test_assert_true( matrix_equal(m1 , m2));
+
+
+ rng_free( rng );
+ matrix_free(m1);
+ matrix_free(m2);
+ free( csc );
+}
+
+
+
+int main(int argc , char ** argv) {
+ test_store_recover_state();
+ test_scaleA();
+ exit(0);
+}
+
diff --git a/libres/lib/analysis/modules/tests/ies_enkf_config.cpp b/libres/lib/analysis/modules/tests/ies_enkf_config.cpp
new file mode 100644
index 00000000000..63ad103ef18
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/ies_enkf_config.cpp
@@ -0,0 +1,14 @@
+
+
+#include "ies_enkf_data.h"
+
+
+void test_create() {
+ ies_enkf_config_type * config = ies_enkf_config_alloc();
+
+ ies_enkf_config_free(config);
+}
+
+int main(int argc, char ** argv) {
+ test_create();
+}
diff --git a/libres/lib/analysis/modules/tests/ies_enkf_data.cpp b/libres/lib/analysis/modules/tests/ies_enkf_data.cpp
new file mode 100644
index 00000000000..6ac6af92181
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/ies_enkf_data.cpp
@@ -0,0 +1,19 @@
+#include
+
+#include
+
+#include "ies_enkf_data.h"
+
+
+void test_create() {
+ rng_type * rng = rng_alloc( MZRAN, INIT_DEFAULT );
+ ies_enkf_data_type * data = (ies_enkf_data_type *) ies_enkf_data_alloc(rng);
+ test_assert_not_NULL(data);
+ ies_enkf_data_free( data );
+ rng_free( rng );
+}
+
+
+int main(int argc, char ** argv) {
+ test_create();
+}
diff --git a/libres/lib/analysis/modules/tests/ies_enkf_module.cpp b/libres/lib/analysis/modules/tests/ies_enkf_module.cpp
new file mode 100644
index 00000000000..3ef2ef2e6e6
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/ies_enkf_module.cpp
@@ -0,0 +1,64 @@
+#include
+#include
+
+#include
+#include
+
+#include
+
+void test_steplength1(const char * module_lib, const char * path_testdata) {
+ res::es_testdata testdata(path_testdata);
+ rng_type * rng = rng_alloc( MZRAN, INIT_DEFAULT );
+ matrix_type * X = matrix_alloc(testdata.active_ens_size, testdata.active_ens_size);
+ matrix_type * prior = testdata.alloc_matrix("A0", testdata.state_size, testdata.active_ens_size);
+
+ analysis_module_type * std_module = analysis_module_alloc_internal("STD_ENKF");
+ analysis_module_type * ies_module = analysis_module_alloc_external(module_lib);
+
+ test_assert_true( analysis_module_set_var(std_module, ENKF_TRUNCATION_KEY_, "0.95") );
+ test_assert_true( analysis_module_set_var(ies_module, ENKF_TRUNCATION_KEY_, "0.95") );
+
+ analysis_module_init_update(std_module,
+ testdata.ens_mask,
+ testdata.obs_mask,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ rng);
+
+ analysis_module_initX(std_module,
+ X,
+ prior,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ rng );
+
+ rng_free( rng );
+ matrix_free(X);
+ if (prior)
+ matrix_free(prior);
+
+ analysis_module_free(std_module);
+ analysis_module_free(ies_module);
+}
+
+
+void test_load(const char * module_lib) {
+ analysis_module_type * module = analysis_module_alloc_external( module_lib );
+ test_assert_not_NULL( module );
+ analysis_module_free( module );
+}
+
+
+int main(int argc, char ** argv) {
+ const char * module_lib = argv[1];
+ const char * path_testdata = argv[2];
+
+ test_load(module_lib);
+ test_steplength1(module_lib, path_testdata);
+}
diff --git a/libres/lib/analysis/modules/tests/ies_iteration.cpp b/libres/lib/analysis/modules/tests/ies_iteration.cpp
new file mode 100644
index 00000000000..032ace2cbe3
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/ies_iteration.cpp
@@ -0,0 +1,354 @@
+#include
+#include
+
+#include
+
+#include
+#include "ies_enkf_data.h"
+#include "ies_enkf.h"
+
+
+void init_stdA(const res::es_testdata& testdata, matrix_type * A2) {
+ rng_type * rng = rng_alloc( MZRAN, INIT_DEFAULT );
+ std_enkf_data_type * std_data = static_cast(std_enkf_data_alloc());
+ std_enkf_set_truncation(std_data, 1.00);
+ matrix_type * X = matrix_alloc(testdata.active_ens_size, testdata.active_ens_size);
+
+ std_enkf_initX(std_data,
+ X,
+ nullptr,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ rng);
+
+ matrix_inplace_matmul(A2, X);
+
+ std_enkf_data_free(std_data);
+ rng_free(rng);
+}
+
+
+/*
+ This function will run the forward model again and update the matrices in the
+ testdata structure - with A1 as prior.
+*/
+
+void forward_model(res::es_testdata& testdata, const matrix_type *A1) {
+ int nrens = matrix_get_columns( A1 );
+ int ndim = matrix_get_rows( A1 );
+ int nrobs = matrix_get_rows( testdata.S );
+
+ /* Model prediction gives new S given prior S=func(A) */
+ for (int iens=0; iens< nrens; iens++){
+ for (int i=0; i < nrobs; i++){
+ double coeffa = matrix_iget(A1,0,iens) ;
+ double coeffb = matrix_iget(A1,1,iens) ;
+ double coeffc = matrix_iget(A1,2,iens) ;
+ double y = coeffa*i*i + coeffb*i + coeffc ;
+ matrix_iset(testdata.S,i,iens,y) ;
+ }
+ }
+
+ /* Updating D according to new S: D=dObs+E-S*/
+ for (int i=0;i(ies_enkf_data_alloc(rng));
+ ies_enkf_config_type * ies_config = ies_enkf_data_get_config(ies_data);
+
+ forward_model(testdata, A1);
+ ies_enkf_config_set_truncation(ies_config, 1.0);
+ ies_enkf_config_set_ies_max_steplength(ies_config, 0.6);
+ ies_enkf_config_set_ies_min_steplength(ies_config, 0.6);
+ ies_enkf_config_set_ies_inversion(ies_config, IES_INVERSION_EXACT);
+ ies_enkf_config_set_ies_aaprojection(ies_config, false);
+
+/* ES solution */
+
+ init_stdA(testdata, A2);
+
+
+ for (int iter=0; iter < num_iter; iter++) {
+ forward_model(testdata, A1);
+
+ ies_enkf_init_update(ies_data,
+ testdata.ens_mask,
+ testdata.obs_mask,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ rng);
+
+ ies_enkf_updateA(ies_data,
+ A1,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ NULL,
+ rng);
+
+
+ if (verbose) {
+ fprintf(stdout,"IES iteration = %d %d\n", iter, bool_vector_count_equal(testdata.ens_mask, true));
+ matrix_pretty_fprint(A1,"Aies","%11.5f",stdout);
+ matrix_pretty_fprint(A2,"Astdenkf","%11.5f",stdout);
+ }
+ test_assert_int_equal( ies_enkf_data_get_iteration_nr(ies_data), iter + 1);
+
+ if ( matrix_similar(A1, A2, 1e-5)) break;
+ }
+
+ test_assert_true( matrix_similar(A1, A2, 1e-5));
+
+ matrix_free(A1);
+ matrix_free(A2);
+ ies_enkf_data_free(ies_data);
+ rng_free( rng );
+}
+
+
+void cmp_std_ies_delrel(res::es_testdata& testdata) {
+ int num_iter = 100;
+ bool verbose = true;
+ rng_type * rng = rng_alloc( MZRAN, INIT_DEFAULT );
+ matrix_type * A1 = testdata.alloc_state("prior");
+ matrix_type * A2 = testdata.alloc_state("prior");
+ matrix_type * A1c = matrix_alloc_copy(A1);
+ matrix_type * A2c = matrix_alloc_copy(A2);
+ ies_enkf_data_type * ies_data = static_cast(ies_enkf_data_alloc(rng));
+ ies_enkf_config_type * ies_config = ies_enkf_data_get_config(ies_data);
+
+ forward_model(testdata, A1);
+ ies_enkf_config_set_truncation(ies_config, 1.0);
+ ies_enkf_config_set_ies_min_steplength(ies_config, 0.6);
+ ies_enkf_config_set_ies_max_steplength(ies_config, 0.6);
+ ies_enkf_config_set_ies_inversion(ies_config, IES_INVERSION_EXACT);
+ ies_enkf_config_set_ies_aaprojection(ies_config, false);
+ int iens_deact = testdata.active_ens_size / 2;
+
+ if (verbose) {
+ fprintf(stdout,"ES and IES original priors\n");
+ matrix_pretty_fprint(A1,"A1 ","%11.5f",stdout);
+ matrix_pretty_fprint(A2,"A2 ","%11.5f",stdout);
+ }
+
+/* IES solution after with one realization is inactivated */
+ for (int iter=0; iter < num_iter; iter++) {
+ forward_model(testdata, A1);
+
+// Removing the realization
+ if (iter == 6) {
+ testdata.deactivate_realization( iens_deact );
+ A1c = matrix_alloc( matrix_get_rows(A1), bool_vector_count_equal(testdata.ens_mask, true));
+ int iens_active = 0;
+ for (int iens=0; iens < matrix_get_columns(A1); iens++) {
+ if (bool_vector_iget(testdata.ens_mask, iens)) {
+ matrix_copy_column(A1c, A1, iens_active, iens);
+ iens_active += 1;
+ }
+ }
+ matrix_realloc_copy(A1,A1c);
+ }
+
+ ies_enkf_init_update(ies_data,
+ testdata.ens_mask,
+ testdata.obs_mask,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ rng);
+
+ ies_enkf_updateA(ies_data,
+ A1,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ NULL,
+ rng);
+
+
+ if (verbose) {
+ fprintf(stdout,"IES iteration = %d active realizations= %d\n", iter, bool_vector_count_equal(testdata.ens_mask, true));
+ matrix_pretty_fprint(A1,"Aies","%11.5f",stdout);
+ }
+ }
+ fprintf(stdout,"IES solution with %d active realizations\n", bool_vector_count_equal(testdata.ens_mask, true));
+ matrix_pretty_fprint(A1,"A1 ","%11.5f",stdout);
+
+
+/* ES update with one realization removed*/
+ {
+ A2c = matrix_alloc( matrix_get_rows(A2), bool_vector_count_equal(testdata.ens_mask, true));
+ int iens_active = 0;
+ for (int iens=0; iens < matrix_get_columns(A2); iens++) {
+ if (bool_vector_iget(testdata.ens_mask, iens)) {
+ matrix_copy_column(A2c, A2, iens_active, iens);
+ iens_active += 1;
+ }
+ }
+ matrix_realloc_copy(A2,A2c);
+ }
+ forward_model(testdata, A2);
+
+ if (verbose) {
+ fprintf(stdout,"\n\n\nES prior with one realization removed\n");
+ matrix_pretty_fprint(A2,"A2 ","%11.5f",stdout);
+ }
+
+ init_stdA(testdata, A2);
+
+ if (verbose) {
+ fprintf(stdout,"ES solution with one realization removed\n");
+ matrix_pretty_fprint(A2,"A2 ","%11.5f",stdout);
+ }
+
+ test_assert_true( matrix_similar(A1, A2, 1e-5));
+
+ matrix_free(A1c);
+ matrix_free(A2c);
+ matrix_free(A1);
+ matrix_free(A2);
+ ies_enkf_data_free(ies_data);
+ rng_free( rng );
+}
+
+matrix_type * swap_matrix(matrix_type * old_matrix, matrix_type * new_matrix) {
+ matrix_free( old_matrix );
+ return new_matrix;
+}
+
+/*
+ This test verifies that the update iteration do not crash hard when
+ realizations and observations are deactived between iterations.
+ The function is testing reactivation as well. It is a bit tricky since there is no
+ reactivation function. What is done is to start with identical copies, testdata and
+ testdata2. In the first iteration, one observation is removed in testdata2 and before
+ computing the update. In the subsequent iterations, testdata is used which includes
+ the datapoint initially removed from testdata2.
+*/
+
+void test_deactivate_observations_and_realizations(const char * testdata_file) {
+ res::es_testdata testdata(testdata_file);
+ res::es_testdata testdata2(testdata_file);
+ int num_iter = 10;
+ rng_type * rng = rng_alloc( MZRAN, INIT_DEFAULT );
+
+ ies_enkf_data_type * ies_data = static_cast(ies_enkf_data_alloc(rng));
+ ies_enkf_config_type * ies_config = ies_enkf_data_get_config(ies_data);
+
+ matrix_type * A0 = testdata.alloc_state("prior");
+ matrix_type * A = matrix_alloc_copy(A0);
+
+
+ ies_enkf_config_set_truncation(ies_config, 1.00);
+ ies_enkf_config_set_ies_max_steplength(ies_config, 0.50);
+ ies_enkf_config_set_ies_min_steplength(ies_config, 0.50);
+ ies_enkf_config_set_ies_inversion(ies_config, IES_INVERSION_SUBSPACE_EXACT_R);
+ ies_enkf_config_set_ies_aaprojection(ies_config, false);
+
+ for (int iter=0; iter < 1; iter++) {
+ printf("test_deactivate_observations_and_realizations: iter= %d\n",iter);
+
+ // deactivate an observation initially to test reactivation in the following iteration
+ testdata2.deactivate_obs( 2 );
+
+ ies_enkf_init_update(ies_data,
+ testdata2.ens_mask,
+ testdata2.obs_mask,
+ testdata2.S,
+ testdata2.R,
+ testdata2.dObs,
+ testdata2.E,
+ testdata2.D,
+ rng);
+
+ ies_enkf_updateA(ies_data,
+ A,
+ testdata2.S,
+ testdata2.R,
+ testdata2.dObs,
+ testdata2.E,
+ testdata2.D,
+ NULL,
+ rng);
+ }
+
+ for (int iter=1; iter < num_iter; iter++) {
+ printf("test_deactivate_observations_and_realizations: iter= %d\n",iter);
+
+ // Deactivate a realization
+ if (iter == 3) {
+ int iens = testdata.active_ens_size / 2;
+ testdata.deactivate_realization( iens );
+ A = matrix_alloc( matrix_get_rows(A0), bool_vector_count_equal(testdata.ens_mask, true));
+ int iens_active = 0;
+ for (int iens=0; iens < matrix_get_columns(A0); iens++) {
+ if (bool_vector_iget(testdata.ens_mask, iens)) {
+ matrix_copy_column(A, A0, iens_active, iens);
+ iens_active += 1;
+ }
+ }
+ }
+
+ // Now deactivate a previously active observation
+ if (iter == 7)
+ testdata.deactivate_obs( testdata.active_obs_size / 2 );
+
+ ies_enkf_init_update(ies_data,
+ testdata.ens_mask,
+ testdata.obs_mask,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ rng);
+
+ ies_enkf_updateA(ies_data,
+ A,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ NULL,
+ rng);
+ }
+
+
+ matrix_free(A);
+ matrix_free(A0);
+
+ ies_enkf_data_free(ies_data);
+ rng_free( rng );
+}
+
+
+int main(int argc, char ** argv) {
+ res::es_testdata testdata(argv[1]);
+ cmp_std_ies(testdata);
+ cmp_std_ies_delrel(testdata);
+ test_deactivate_observations_and_realizations(argv[1]);
+}
diff --git a/libres/lib/analysis/modules/tests/ies_linalg.cpp b/libres/lib/analysis/modules/tests/ies_linalg.cpp
new file mode 100644
index 00000000000..1d6c7f6ece4
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/ies_linalg.cpp
@@ -0,0 +1,128 @@
+#include
+#include
+
+#include
+
+#include "ies_enkf_data.h"
+#include "ies_enkf.h"
+
+
+
+void update_exact_scheme_subspace_no_truncation_diagR(const res::es_testdata& testdata, ies_enkf_data_type* ies_data, matrix_type * A, rng_type * rng) {
+ ies_enkf_init_update(ies_data,
+ testdata.ens_mask,
+ testdata.obs_mask,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ rng);
+
+ ies_enkf_updateA(ies_data,
+ A,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ NULL,
+ rng);
+}
+
+
+/*
+TEST 1 (Consistency between exact scheme and subspace scheme with no truncation and exact diagonal R):
+ - ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 1.0
+ - ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 0.6
+ - ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 1
+ - ANALYSIS_SELECT IES_ENKF
+should give same result as:
+ - ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 0.6
+ - ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 0
+ - ANALYSIS_SELECT IES_ENKF
+
+*/
+
+void test_consistency_exact_scheme_subspace_no_truncation_diagR(const res::es_testdata& testdata) {
+ rng_type * rng = rng_alloc( MZRAN, INIT_DEFAULT );
+ matrix_type * A1 = testdata.alloc_state("prior");
+ matrix_type * A2 = testdata.alloc_state("prior");
+
+ ies_enkf_data_type * ies_data1 = static_cast(ies_enkf_data_alloc(rng));
+ ies_enkf_config_type * ies_config1 = ies_enkf_data_get_config(ies_data1);
+
+ ies_enkf_data_type * ies_data2 = static_cast(ies_enkf_data_alloc(rng));
+ ies_enkf_config_type * ies_config2 = ies_enkf_data_get_config(ies_data2);
+
+ ies_enkf_config_set_truncation(ies_config1, 1.0);
+ ies_enkf_config_set_ies_max_steplength(ies_config1, 0.6);
+ ies_enkf_config_set_ies_min_steplength(ies_config1, 0.6);
+ ies_enkf_config_set_ies_inversion(ies_config1, IES_INVERSION_SUBSPACE_EXACT_R);
+
+ ies_enkf_config_set_ies_max_steplength(ies_config2, 0.6);
+ ies_enkf_config_set_ies_min_steplength(ies_config2, 0.6);
+ ies_enkf_config_set_ies_inversion(ies_config2, IES_INVERSION_EXACT);
+
+ update_exact_scheme_subspace_no_truncation_diagR(testdata, ies_data1, A1, rng);
+ update_exact_scheme_subspace_no_truncation_diagR(testdata, ies_data2, A2, rng);
+ test_assert_true( matrix_similar(A1, A2, 5e-5));
+
+ matrix_free(A1);
+ matrix_free(A2);
+ ies_enkf_data_free(ies_data1);
+ ies_enkf_data_free(ies_data2);
+ rng_free( rng );
+}
+
+/*
+TEST 2 (consistency between subspace inversion schemes with lowrank R):
+ - ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 0.999
+ - ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 0.6
+ - ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 2
+ - ANALYSIS_SELECT IES_ENKF
+should give same result as
+ - ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 0.999
+ - ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 0.6
+ - ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 3
+ - ANALYSIS_SELECT IES_ENKF
+*/
+
+void test_consistency_scheme_inversions(const res::es_testdata& testdata) {
+ rng_type * rng = rng_alloc( MZRAN, INIT_DEFAULT );
+ matrix_type * A1 = testdata.alloc_state("prior");
+ matrix_type * A2 = testdata.alloc_state("prior");
+
+ ies_enkf_data_type * ies_data1 = static_cast(ies_enkf_data_alloc(rng));
+ ies_enkf_config_type * ies_config1 = ies_enkf_data_get_config(ies_data1);
+
+ ies_enkf_data_type * ies_data2 = static_cast(ies_enkf_data_alloc(rng));
+ ies_enkf_config_type * ies_config2 = ies_enkf_data_get_config(ies_data2);
+
+ ies_enkf_config_set_truncation(ies_config1, 0.95);
+ ies_enkf_config_set_ies_max_steplength(ies_config1, 0.6);
+ ies_enkf_config_set_ies_min_steplength(ies_config1, 0.6);
+ ies_enkf_config_set_ies_inversion(ies_config1, IES_INVERSION_SUBSPACE_EE_R);
+
+ ies_enkf_config_set_truncation(ies_config2, 0.95);
+ ies_enkf_config_set_ies_max_steplength(ies_config2, 0.6);
+ ies_enkf_config_set_ies_min_steplength(ies_config2, 0.6);
+ ies_enkf_config_set_ies_inversion(ies_config2, IES_INVERSION_SUBSPACE_RE);
+
+ update_exact_scheme_subspace_no_truncation_diagR(testdata, ies_data1, A1, rng);
+ update_exact_scheme_subspace_no_truncation_diagR(testdata, ies_data2, A2, rng);
+ test_assert_true( matrix_similar(A1, A2, 5e-6));
+
+ matrix_free(A1);
+ matrix_free(A2);
+ ies_enkf_data_free(ies_data1);
+ ies_enkf_data_free(ies_data2);
+ rng_free( rng );
+}
+
+
+int main(int argc, char ** argv) {
+ res::es_testdata testdata(argv[1]);
+ test_consistency_exact_scheme_subspace_no_truncation_diagR(testdata);
+ test_consistency_scheme_inversions(testdata);
+}
diff --git a/libres/lib/analysis/modules/tests/ies_std_compare.cpp b/libres/lib/analysis/modules/tests/ies_std_compare.cpp
new file mode 100644
index 00000000000..e66cbbac7e7
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/ies_std_compare.cpp
@@ -0,0 +1,84 @@
+#include
+#include
+
+#include
+
+#include
+#include "ies_enkf_data.h"
+#include "ies_enkf.h"
+
+/*
+TEST 3 (consistency between IES and STD_ENKF):
+ - ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 0.999
+ - ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 1.0
+ - ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 1
+ - ANALYSIS_SET_VAR IES_ENKF IES_AAPROJECTION false
+should give same result as:
+ - ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 0.999
+ - ANALYSIS_SELECT STD_ENKF
+*/
+
+
+void cmp_std_ies(const res::es_testdata& testdata) {
+ rng_type * rng = rng_alloc( MZRAN, INIT_DEFAULT );
+ matrix_type * A1 = testdata.alloc_state("prior");
+ matrix_type * A2 = testdata.alloc_state("prior");
+ matrix_type * X = matrix_alloc(testdata.active_ens_size, testdata.active_ens_size);
+
+ ies_enkf_data_type * ies_data1 = static_cast(ies_enkf_data_alloc(rng));
+ ies_enkf_config_type * ies_config1 = ies_enkf_data_get_config(ies_data1);
+ std_enkf_data_type * std_data = static_cast(std_enkf_data_alloc());
+
+ ies_enkf_config_set_truncation(ies_config1, 0.95);
+ ies_enkf_config_set_ies_min_steplength(ies_config1, 1.0);
+ ies_enkf_config_set_ies_max_steplength(ies_config1, 1.0);
+ ies_enkf_config_set_ies_inversion(ies_config1, IES_INVERSION_SUBSPACE_EXACT_R);
+ ies_enkf_config_set_ies_aaprojection(ies_config1, false);
+
+ std_enkf_set_truncation(std_data, 0.95);
+
+ ies_enkf_init_update(ies_data1,
+ testdata.ens_mask,
+ testdata.obs_mask,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ rng);
+
+ ies_enkf_updateA(ies_data1,
+ A1,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ NULL,
+ rng);
+
+ std_enkf_initX(std_data,
+ X,
+ nullptr,
+ testdata.S,
+ testdata.R,
+ testdata.dObs,
+ testdata.E,
+ testdata.D,
+ rng);
+
+ matrix_inplace_matmul(A2, X);
+ test_assert_true( matrix_similar(A1, A2, 5e-6));
+
+ matrix_free(A1);
+ matrix_free(A2);
+ std_enkf_data_free(std_data);
+ ies_enkf_data_free(ies_data1);
+ rng_free( rng );
+}
+
+
+int main(int argc, char ** argv) {
+ res::es_testdata testdata(argv[1]);
+ cmp_std_ies(testdata);
+}
diff --git a/libres/lib/analysis/modules/tests/test-data/poly/D b/libres/lib/analysis/modules/tests/test-data/poly/D
new file mode 100644
index 00000000000..bb02992336b
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly/D
@@ -0,0 +1,5 @@
+14.037 -19.0304 27.6018 3.27521 -5.55972 -3.8635 12.8809 -12.0571 -10.6488 17.0308 14.3497 -9.60167 6.53411 -11.4534 -20.8422 -5.85823 15.8682 -9.92272 -3.07645 18.2103 -8.91641 -12.6759 -18.9869 20.8912 21.3984 -13.8013 -11.7366 -3.76351 16.9072 -7.73579 -3.54676 26.486 -2.03103 13.9796 2.46703 -21.2399 2.10351 29.2362 12.8236 15.0894 -20.7659 19.5082 12.4312 18.6428 14.0054 -16.3454 5.91034 3.28466 -15.4102 -15.979 10.818 -1.62314 14.7865 15.8709 -11.9026 -1.783 13.0904 -17.8698 19.6846 -11.7419 13.4135 6.61099 2.74485 28.827 1.98976 1.6027 -10.5765 10.947 13.7605 5.61157 9.72628 13.377 1.05963 -20.6501 21.4181 -1.71049 1.79426 -2.69719 3.97709 -3.03353 8.78135 25.7855 -12.1803 11.3616 7.08832 -18.8132 1.98778 5.05048 3.4913 7.14471 24.4964 28.2233 -5.45472 21.4797 -17.6684 22.1607 -18.4396 -13.8213 -20.5784 -1.32451
+1.50801 -0.915119 -1.99365 1.00321 1.07739 1.05358 -1.74435 -5.3775 -3.85288 2.36396 0.856811 -0.938823 1.45885 -0.196605 -0.604721 3.35525 0.725861 1.44303 1.78922 3.88788 -0.528919 0.993736 -0.386782 2.33915 2.11098 0.174099 -2.78341 0.180795 0.419222 -3.31673 -1.37143 -0.301649 1.58981 3.30971 -2.69679 -0.961483 -1.1914 4.52652 3.95226 1.85462 -1.70566 1.85824 1.10671 4.57348 3.62923 2.09168 1.69229 1.38094 3.52079 -1.78028 3.22366 0.812722 1.17299 1.84175 0.922275 0.212811 1.53627 1.11394 1.10863 1.26684 1.163 2.06367 3.02087 3.01975 -0.19532 -0.282394 0.389243 -0.323301 0.671168 -0.0903785 -0.113819 1.15513 -0.874725 0.953638 5.15873 -2.9015 -1.95282 0.018342 -1.60256 2.63809 1.06489 3.91469 1.41476 0.437523 -1.33905 -0.805631 1.36115 0.575921 -0.18332 0.769752 -0.175456 4.69637 -1.82316 -2.87756 -1.82605 -0.305235 1.1239 -1.34691 -1.40689 -0.804224
+0.365401 3.54042 1.16639 0.253231 0.766202 1.73317 -1.16726 -1.09199 -2.66273 2.94012 1.86699 -1.09292 0.757884 1.17486 1.07459 5.10578 2.09158 2.87373 1.82039 5.30766 -0.382895 2.40823 1.60487 1.04483 0.209843 0.0220696 -0.872902 2.84283 1.24401 1.2111 -0.573501 -0.206941 1.47953 3.9779 0.654527 3.95002 0.0704477 3.72037 2.80762 -0.113666 -0.0938437 1.50393 3.37603 3.99863 3.44438 4.81979 0.473579 2.05047 2.81515 2.08934 4.80686 3.12007 1.14968 1.91799 2.62676 1.57236 -0.81439 3.98174 -1.2914 3.91287 -0.720208 3.2711 1.69352 4.36437 0.227229 4.00449 2.32583 -1.53177 0.874107 -0.0439946 -0.243214 2.84921 1.06832 2.25302 4.23088 -0.16499 -0.340449 1.94665 0.883753 1.68682 4.53847 2.73984 3.79174 2.21406 3.35727 2.50936 1.23793 1.95969 4.50119 1.6337 -0.866589 5.43409 0.512167 0.101959 -0.761755 2.94079 2.61405 0.966924 0.649661 -1.52897
+-1.14462 0.960122 0.586066 -0.898981 0.695813 0.420293 -2.22637 -2.46021 -1.69173 -0.0852955 -0.844426 0.842969 0.477555 1.15333 3.69708 1.45932 0.864733 0.552596 -1.05578 1.81139 1.07813 0.783451 1.44959 -0.543903 -1.02366 1.63633 -1.24904 1.4877 0.350825 0.436385 0.0889926 -1.86884 1.37399 1.07834 -1.3672 1.76854 0.95662 2.29245 0.649319 -0.503262 0.0304153 1.14904 4.08872 0.841419 -0.793489 2.35789 0.135585 0.459748 4.15671 0.940847 4.23916 1.64172 1.53697 0.636197 1.89512 -1.50218 -0.308845 2.11115 -1.48401 2.87086 -1.33159 0.0099039 -1.70231 2.07549 -0.889054 3.15033 -0.254267 -1.94036 1.4092 0.952302 -2.02758 2.20727 -1.9522 0.958356 2.92409 -0.0657733 -1.72247 0.0262019 0.226067 -1.14314 1.16298 1.73915 2.56274 0.856842 0.352897 -0.370299 1.1016 0.912167 2.87271 -0.782565 -3.02975 2.43537 1.12041 -2.76451 -1.01673 1.69375 1.73375 0.720078 1.42902 1.51405
+0.717493 1.47629 3.21511 1.31698 3.21308 0.34906 -1.61548 -0.814371 -0.811226 2.04331 1.02344 -1.31211 1.08294 -0.192749 2.34744 2.33195 -0.866969 1.27191 2.13977 3.3993 -0.287194 1.1871 3.78521 0.368975 -1.83491 0.903834 -0.450894 2.89537 -2.49934 0.420863 -0.589967 -2.11216 3.08908 2.76563 -2.7662 1.1038 -1.64638 2.08552 0.0304651 -0.117321 0.221653 0.771885 2.97987 0.608119 3.04403 0.969024 -0.775437 0.785879 3.76001 0.105079 2.34475 3.64647 0.170422 -1.20088 0.937736 -1.65651 -0.54401 2.0477 0.0384948 0.993823 -1.24262 0.828661 0.894046 1.73878 -1.58289 1.37644 1.76446 -0.114712 0.437375 0.595192 -1.28691 -0.508559 -0.268689 2.41784 3.7141 -2.87261 -1.8305 0.867807 0.331614 -2.44354 0.482357 1.49783 2.0707 0.0528886 1.54761 2.56982 1.62019 0.856645 3.51763 -0.458752 -2.05424 1.73369 -0.0355098 -0.644493 -0.14917 -1.13779 2.95799 1.82712 2.13844 -1.26763
diff --git a/libres/lib/analysis/modules/tests/test-data/poly/E b/libres/lib/analysis/modules/tests/test-data/poly/E
new file mode 100644
index 00000000000..8b4bb77a0ed
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly/E
@@ -0,0 +1,5 @@
+-1.1491 -1.15091 0.00648833 0.976599 0.371671 -0.19001 -1.18467 1.50345 0.18363 0.422656 -0.677245 -0.981079 -0.359999 0.845076 -0.0747535 -1.08273 0.184747 0.289066 1.61514 -0.809083 -0.817519 0.236962 0.498914 -0.904569 -0.404627 1.52604 -1.90472 1.27118 -0.544589 0.199203 0.403633 0.63391 -0.715039 1.2346 1.45362 -0.667009 1.1529 0.865636 0.0315538 0.52781 -0.575516 1.47121 0.752702 2.22733 -0.546937 -1.74075 -1.26267 0.538149 -1.59659 -0.801463 1.45897 1.01336 0.0258708 0.133624 0.33316 0.599888 -0.139286 -0.924618 1.39388 -0.0668435 -0.841123 0.0544777 -0.0158551 1.18412 -0.851053 -0.339305 -0.502552 -1.40108 1.48468 0.0778586 -1.09012 2.09638 0.858617 -2.27737 0.251614 -0.684596 -0.594152 -1.0555 -1.32522 -1.88504 -1.00846 0.997391 -0.466507 0.838852 -0.555389 1.51417 -1.06673 -1.28373 0.42299 0.6818 0.523401 0.105304 -0.214733 -0.740584 -1.03556 -0.394168 1.24782 2.46625 0.247692 -0.534917
+0.00442592 -0.52484 -3.0493 0.23319 0.994861 0.291145 -0.00119956 -1.73213 0.0713842 -0.0143865 0.238399 0.256622 1.97007 1.38352 0.246679 1.3482 -0.955753 0.724951 1.39149 -0.174785 0.208918 -0.0187088 -1.14466 0.817138 0.801586 1.42242 -0.0220971 0.193221 0.387787 -1.18556 0.643849 -0.463534 -1.00723 -0.264455 -2.00283 -0.635644 -0.921127 0.652018 2.35784 1.37416 -0.361195 0.82115 -1.72528 1.58061 0.0105825 0.168004 0.271501 -0.0653155 1.68799 -0.582993 -0.687149 -0.20677 -1.0499 0.215009 0.221951 1.14825 -0.183938 0.264956 0.547599 0.459405 0.769028 0.272052 1.68939 -0.890986 -0.599159 -1.40699 0.164746 0.69728 -0.936547 -0.32823 0.894935 0.245184 0.237861 -0.185999 0.196195 -1.84579 0.57486 -0.692221 -1.21365 1.64133 -0.914718 1.16212 0.617431 -0.553726 -2.482 -0.635907 0.0513353 0.127925 -0.905216 0.309155 0.89558 0.320424 -0.75868 -2.56583 0.712473 -1.10983 0.455755 -1.47091 0.258837 0.772377
+-1.36775 1.1998 -0.111802 -1.29911 -1.39728 0.164126 -0.49284 -0.0550894 -1.3432 0.711473 1.10948 -1.57995 -0.112951 0.111588 -1.21093 1.64791 0.0235012 0.701682 -0.144643 1.69464 -1.21734 -0.0200412 -1.21137 -0.253034 -0.800454 -1.36976 -0.501213 0.346147 0.937196 1.32286 -0.773216 -0.3181 -1.74485 0.693984 0.177619 1.40327 -0.700745 0.425877 1.00522 -0.974023 -0.601058 -0.242517 -0.185301 1.48314 0.322268 1.05551 -1.15715 0.285088 -0.893757 1.18859 0.673383 0.146858 -1.25156 0.695038 0.686232 0.894422 -2.13238 0.851857 -1.669 0.952575 -1.19914 0.212886 -0.453453 1.19453 -0.617936 1.30865 0.778881 -1.46951 -0.945806 -1.23245 0.100021 0.91254 0.781171 -1.0094 -0.164342 -0.202014 0.51487 0.778756 -0.221204 0.215062 1.68484 0.533535 1.00877 0.203105 0.981857 -0.0720887 -1.13852 0.507438 2.15976 0.950942 -0.180371 1.45432 -0.653788 0.288328 -0.866658 2.43959 -0.304759 -1.26621 -0.971847 -1.81928
+-1.58262 -0.622372 0.464213 -1.21698 -0.607095 0.142603 -0.575258 -0.705376 0.314001 -0.891423 -0.282948 1.37214 0.565589 0.804546 2.05356 -0.907958 0.0331292 -0.48439 -1.98979 -0.281634 1.29793 -0.463429 -0.341766 -0.459488 -0.627739 0.997053 -0.113713 -0.309867 1.28198 1.42805 0.657887 -0.718224 -0.50175 -0.678051 -0.74836 -0.0396137 1.33202 0.458972 0.176969 -0.125705 0.605402 0.492578 1.69995 -0.127466 -2.33491 -0.34576 -0.105936 0.094107 1.48626 0.982204 1.49375 -0.452091 0.46147 0.926425 1.18687 -1.20531 -0.0856244 -0.0990298 -0.465705 0.816936 -0.466477 -2.01983 -2.62552 0.43163 -0.389947 1.41923 -0.626839 -0.835762 0.92368 0.897621 -0.522075 1.32183 -1.21976 -1.30987 0.0765478 1.03886 0.0169884 0.249581 0.0594964 -1.23211 -0.557579 1.00903 0.745246 -0.0911953 -1.00111 -2.17143 -0.142857 0.568323 1.46224 -0.083164 -1.22782 -0.139745 0.74225 -1.34359 -0.370062 2.58646 -0.226573 -0.548136 0.388359 2.08485
+0.203594 -0.384805 2.92031 0.918543 1.65844 0.032242 -0.204465 0.625552 0.857264 1.22396 1.51941 -0.953637 0.939874 -0.85647 0.372289 -0.170093 -1.80458 0.131302 1.03752 1.34037 -0.219155 -0.133773 1.85488 0.407092 -1.47376 -0.0241142 0.38635 0.762996 -1.68074 1.17004 -0.32762 -1.07983 1.20182 1.05947 -2.29767 -0.986853 -1.39476 0.236208 -0.494988 0.154781 0.683419 -0.0704288 0.464049 -0.310048 1.57573 -1.86361 -1.02878 0.423203 0.947566 -0.0474116 -0.412399 1.30533 -0.963201 -0.87817 0.17889 -1.56385 -0.268695 -0.359413 1.02277 -1.27506 -0.429274 -1.39163 -0.114695 0.113291 -1.11105 -0.568764 1.30663 0.787901 -0.0979434 0.4016 0.0800322 -1.58513 0.276096 -0.00630622 0.911862 -1.89169 -0.354296 1.09771 -0.0726683 -2.53069 -1.38283 0.764734 0.0679457 -1.07862 -0.00420536 0.50691 0.243513 0.359917 1.87285 0.224763 -0.442434 -0.883912 -0.70581 0.651251 0.208367 -0.288691 0.825649 0.381684 0.733878 -0.962828
diff --git a/libres/lib/analysis/modules/tests/test-data/poly/R b/libres/lib/analysis/modules/tests/test-data/poly/R
new file mode 100644
index 00000000000..b4db6750c52
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly/R
@@ -0,0 +1,5 @@
+1 0 0 0 0
+0 1 0 0 0
+0 0 1 0 0
+0 0 0 1 0
+0 0 0 0 1
diff --git a/libres/lib/analysis/modules/tests/test-data/poly/S b/libres/lib/analysis/modules/tests/test-data/poly/S
new file mode 100644
index 00000000000..2a736a1ebe3
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly/S
@@ -0,0 +1,5 @@
+13.3464 46.412 0.937209 26.2339 34.4639 32.206 14.4669 42.0931 39.3649 11.9244 13.5056 37.1531 21.6384 40.831 49.3 33.308 12.8491 38.7443 33.2241 9.51313 36.6314 41.4454 48.0183 6.73678 6.72945 43.8598 38.3644 33.5672 11.0807 36.4675 32.4829 2.6804 29.8485 15.7875 27.5191 49.1054 27.5819 0.161983 15.7405 13.9709 48.7229 10.4955 16.854 12.117 13.9802 43.1372 21.3595 25.786 42.3461 43.71 19.1735 31.169 13.7719 12.7952 40.7683 30.9154 15.3028 45.4777 10.2418 40.2076 14.2779 21.976 25.7718 0.889615 25.6917 26.5905 38.6065 16.1844 16.2567 22.9988 17.7161 17.2519 28.3315 46.9052 7.36604 29.5584 26.1441 30.1742 23.2302 29.681 18.7427 3.74438 40.2463 18.0098 20.8888 48.8599 25.478 22.1983 25.4642 22.0696 4.55949 0.414553 33.7725 6.31219 45.1654 5.97766 48.2199 44.8201 49.3586 29.3221
+5.0447 6.93857 5.49264 5.77827 6.46575 5.78585 8.29144 10.1937 10.4726 4.16994 5.92988 7.74373 7.05951 8.12841 7.39969 4.54124 4.86667 5.83021 6.15056 2.48562 7.28613 5.53584 5.79041 5.02627 5.2389 7.79661 9.3096 6.56071 6.51685 8.67946 8.56357 6.3864 3.95124 2.97413 7.24225 6.87413 6.81856 2.67379 4.95388 6.06783 7.89275 5.51119 3.7163 3.55541 2.92964 4.62462 5.1275 5.10203 4.71549 7.74557 2.63748 5.5288 4.32541 4.92155 5.84796 7.48373 4.82808 5.6993 5.98726 5.74085 6.15432 4.75667 5.21681 2.63755 6.14445 5.4237 6.32379 7.56887 4.94057 6.31044 7.55704 5.63834 7.66087 5.40865 1.58575 7.60399 9.07597 5.83772 6.9372 5.55154 4.56868 3.79572 5.75095 5.55704 5.40534 6.71801 5.23847 6.10029 5.82639 6.08769 7.61932 2.17234 7.61277 6.86001 9.08681 5.74369 5.88014 6.42429 8.21401 8.12489
+3.48306 2.8756 3.93802 3.66388 3.05274 3.64718 5.89063 6.25312 6.53574 2.98757 4.4587 4.72919 4.34538 4.15295 2.93069 1.75834 3.14814 3.04417 3.25118 1.60319 4.38177 2.78794 2.39998 3.91835 4.20592 3.82439 5.58791 2.71954 4.9094 5.32798 5.0165 5.10506 1.99184 1.9323 4.73931 2.66947 4.44502 1.92173 3.41381 4.35586 4.709 3.46977 1.65489 2.70072 2.0941 1.45194 3.58549 3.45084 1.50731 4.31547 1.08274 2.24301 2.81497 3.99327 3.27568 4.53828 3.89823 2.08633 4.83861 2.25593 4.73728 2.158 3.06924 2.04637 4.37105 2.52038 3.66927 5.27847 3.3963 4.02777 5.55945 3.27954 4.92906 1.9538 0.820997 5.17919 6.07154 4.04832 4.11126 3.74446 2.36259 3.00991 2.43324 3.20526 2.8408 2.63477 2.83977 3.76396 2.87479 4.53346 5.90243 1.23645 4.05026 5.40259 5.11131 4.71501 2.29741 2.98309 3.59471 4.92591
+3.02514 1.88064 3.34129 3.14514 2.16023 3.18545 5.11425 5.21798 5.46887 2.65701 4.02462 3.99231 3.55117 3.11435 1.81962 1.09586 2.63153 2.42615 2.52913 1.37011 3.68294 2.21626 1.67178 3.54755 3.85905 2.82386 4.59846 1.66557 4.39429 4.4548 4.03203 4.61375 1.5874 1.70675 4.08198 1.65499 3.83854 1.62967 2.99079 3.8407 4.03812 2.80668 1.07437 2.49425 1.92172 0.759489 3.22162 3.0975 0.792678 3.5045 0.717731 1.36932 2.38764 3.75337 2.75489 3.76001 3.68636 1.25296 4.48145 1.40922 4.32826 1.43341 2.53993 1.81928 3.96225 1.73203 3.09057 4.56774 2.97762 3.40846 4.96864 2.5777 4.19558 1.19491 0.615592 4.56777 5.2026 3.68652 3.29657 3.37416 1.74258 2.73302 1.64564 2.5151 2.10913 1.66201 2.21868 3.11929 2.05266 4.16254 5.26507 0.888022 3.08498 4.88406 4.10981 4.35585 1.50282 2.19492 2.42248 4.03393
+2.81329 1.46609 3.03238 2.92875 1.77254 3.01037 4.7382 4.76711 4.99567 2.50784 3.82316 3.68566 3.18412 2.66346 1.35203 0.825138 2.38957 2.18658 2.22493 1.26826 3.39522 2.00631 1.39686 3.3653 3.68833 2.39924 4.16443 1.19481 4.14579 4.07636 3.58953 4.35951 1.43993 1.62102 3.79571 1.23654 3.57881 1.47787 2.80173 3.59929 3.78895 2.48487 0.811368 2.40902 1.85889 0.49455 3.07384 2.96451 0.514738 3.17469 0.570037 0.98604 2.19356 3.6499 2.56834 3.41985 3.6025 0.920075 4.31146 1.0583 4.14053 1.10689 2.31844 1.70169 3.79902 1.38198 2.86935 4.2298 2.79187 3.13359 4.69412 2.25062 3.87197 0.90304 0.524951 4.30811 4.80339 3.55708 2.9229 3.24003 1.462 2.59409 1.32443 2.19567 1.77537 1.26428 1.9505 2.83046 1.68241 4.0107 4.93899 0.709584 2.65688 4.62293 3.68472 4.17628 1.19484 1.88175 1.92262 3.63199
diff --git a/libres/lib/analysis/modules/tests/test-data/poly/dObs b/libres/lib/analysis/modules/tests/test-data/poly/dObs
new file mode 100644
index 00000000000..be4a468cd4c
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly/dObs
@@ -0,0 +1,5 @@
+28.5325 1
+6.54829 1
+5.21622 1
+3.46314 1
+3.32718 1
diff --git a/libres/lib/analysis/modules/tests/test-data/poly/prior b/libres/lib/analysis/modules/tests/test-data/poly/prior
new file mode 100644
index 00000000000..0d06eefca5c
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly/prior
@@ -0,0 +1,3 @@
+0.16266 -1.05466 0.048838 0.296662 -0.781533 0.469504 1.40265 1.45836 1.77051 0.0839536 0.927587 0.701792 0.141148 -0.244405 -1.38312 -1.48585 -0.161885 -0.148102 -0.278987 -0.65261 0.524014 -0.190316 -0.713218 0.553796 0.837893 -0.347314 0.783099 -2.35923 1.09136 0.840131 0.310772 1.24583 -0.503428 -0.332485 0.776475 -1.33136 0.667408 -0.669402 0.212187 0.638302 1.04459 -0.269245 -1.78881 0.145502 -0.145068 -2.27129 0.508629 0.489687 -2.57159 0.326619 -1.48128 -2.08063 -0.188261 1.04967 0.20241 0.396917 1.08666 -1.47276 1.76616 -1.40491 1.36513 -1.45677 -0.0991455 -0.427353 1.13393 -1.11437 0.316106 0.938802 0.215103 0.295896 2.43159 -0.396493 0.750584 -1.28008 -1.42725 1.49635 1.57675 1.02183 -0.0258995 0.717205 -0.882603 0.114459 -0.959698 -0.413471 -0.737028 -1.20752 -0.430884 0.072575 -0.880211 1.4406 2.21665 -1.73392 -0.241963 1.76507 0.48241 1.35098 -0.981669 -0.460991 -0.84013 0.403507
+-0.0275122 0.258564 1.84624 -0.477402 0.522216 -1.25184 3.04456 0.928018 1.50743 -0.481247 -0.0755539 -0.14564 1.01219 0.881886 0.580174 -0.392131 0.205503 -0.864817 -0.073575 -0.882648 -0.284448 -1.54073 -1.01921 0.00919092 -0.0664601 0.477209 0.907064 1.70455 0.392554 0.45537 1.41113 0.826243 -1.83778 -1.61242 0.0561108 0.179462 -0.156516 -0.051101 -0.292941 0.204507 -1.27847 1.07728 0.160637 -1.19027 -2.20247 -0.908623 -0.909353 -1.47144 -0.728853 -0.229449 -0.928003 0.59017 -0.202269 -0.913534 -2.37474 0.334115 -1.38032 -0.358572 -0.178573 -0.0162453 -0.197812 0.48481 -0.44042 -0.335764 -0.93132 0.507776 -1.03712 1.03963 -0.339589 0.110654 0.365954 0.750012 0.318388 -0.790938 -0.933903 -0.203382 1.27992 -2.61584 0.983448 -2.02818 0.251011 -0.24133 -0.235908 0.638136 0.624027 0.0309421 -0.0743802 0.237055 0.759684 -0.851582 2.55223 0.114469 1.01435 0.730552 0.480306 0.0461991 -0.679499 -0.45717 0.932535 1.01346
+-0.622132 1.46281 -2.08041 0.0618976 0.493806 0.369495 -0.555319 1.00214 0.797082 -0.711176 -0.612475 0.652817 -0.169333 0.902555 2.19731 0.429334 -0.652677 0.755032 0.424727 -0.876929 0.620782 0.949862 1.75496 -1.10428 -1.10496 1.16108 0.729943 0.443626 -0.766751 0.610846 0.384398 -1.61083 0.245512 -0.479617 0.126624 2.09938 0.1298 -2.72248 -0.48226 -0.584569 1.95079 -0.806733 -0.420445 -0.698795 -0.584021 1.09273 -0.183531 0.0394138 1.02333 1.14647 -0.296381 0.314369 -0.59645 -0.656024 0.897843 0.301042 -0.507061 1.33801 -0.824469 0.856543 -0.566409 -0.152188 0.0387 -2.10164 0.034685 0.0798212 0.745881 -0.457411 -0.453392 -0.100494 -0.373677 -0.398753 0.167802 1.53905 -1.04799 0.23055 0.0573886 0.262378 -0.0888405 0.236866 -0.319025 -1.44033 0.859352 -0.357936 -0.207587 1.99906 0.0239669 -0.140922 0.0232727 -0.147439 -1.33346 -2.39584 0.455016 -1.14433 1.30064 -1.17722 1.80417 1.26131 2.23137 0.2184
diff --git a/libres/lib/analysis/modules/tests/test-data/poly/size b/libres/lib/analysis/modules/tests/test-data/poly/size
new file mode 100644
index 00000000000..5bf2d0f1e8a
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly/size
@@ -0,0 +1 @@
+100 5
diff --git a/libres/lib/analysis/modules/tests/test-data/poly_normal/D b/libres/lib/analysis/modules/tests/test-data/poly_normal/D
new file mode 100644
index 00000000000..8367ff6e31d
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly_normal/D
@@ -0,0 +1,6 @@
+ 1.52548 2.75179 -1.32931 0.15896 -1.55104 0.56517 0.15219 -0.40329 0.35246 0.26530 0.26008 -0.15279 -1.25075 0.10665 -0.79645 -0.43518 0.11649 1.17645 2.15480 0.77231
+ 1.13243 3.49276 0.32853 0.15843 -1.43686 2.01201 -2.11188 1.63789 0.82473 -1.09149 1.09033 -1.32212 -4.12991 1.62221 -0.53562 0.42622 1.00286 5.10911 3.90837 1.19887
+ 0.31792 4.70628 4.10542 0.27192 -0.99497 3.51820 -7.14951 5.20686 3.29961 -0.97341 2.39100 -3.53781 -9.80201 5.39157 1.34885 1.51219 1.38984 13.60865 8.62863 1.58005
+ -0.91805 6.39236 10.00138 0.49943 -0.22536 5.08375 -14.96070 10.30363 7.77712 0.61953 4.16209 -6.79988 -18.26705 11.41473 4.85695 2.82273 1.27741 26.67507 16.31560 1.91586
+ -2.57548 8.55098 18.01640 0.84095 0.87197 6.70867 -25.54546 16.92820 14.25725 3.68733 6.40359 -11.10830 -29.52503 19.69169 9.98869 4.35783 0.66559 44.30837 26.96927 2.20629
+
diff --git a/libres/lib/analysis/modules/tests/test-data/poly_normal/E b/libres/lib/analysis/modules/tests/test-data/poly_normal/E
new file mode 100644
index 00000000000..8fd763171de
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly_normal/E
@@ -0,0 +1,5 @@
+1.89229 -1.38124 -0.893027 -0.222597 -0.889045 -2.16573 -0.247231 0.00731175 2.37267 -0.276256 0.0933515 -0.668235 -0.160714 0.240051 0.299799 0.391766 0.963025 -0.214031 0.0459991 0.811849
+1.13069 -0.657343 0.144742 -1.81219 -1.06145 0.787098 0.0167473 -1.17625 1.09453 -0.526932 1.40289 -1.77 -0.296392 1.05576 0.818705 1.1447 0.89578 -1.09265 0.167036 -0.265473
+0.727466 -0.0428791 0.229674 2.025 -1.27952 -1.60599 0.403112 -0.494486 -0.583965 -1.02888 0.939607 -0.980959 1.0367 1.88369 0.331132 -0.987473 -0.516382 -0.143899 -0.806977 0.895046
+0.0587886 0.38325 -0.0915199 -0.126322 -1.47977 -0.45529 -0.66219 1.53606 0.73268 -0.103297 -0.95982 -0.380892 -1.31031 -1.35474 2.03372 0.965922 -0.383152 -0.314189 2.03342 -0.122339
+0.122004 1.01694 0.887766 -0.137654 -0.0435175 0.77647 2.31658 1.24436 -0.378901 -0.69661 0.814435 0.949707 -0.121441 -0.79172 -0.726008 -0.845121 -1.25649 -0.244113 -2.05255 -0.834141
diff --git a/libres/lib/analysis/modules/tests/test-data/poly_normal/R b/libres/lib/analysis/modules/tests/test-data/poly_normal/R
new file mode 100644
index 00000000000..b4db6750c52
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly_normal/R
@@ -0,0 +1,5 @@
+1 0 0 0 0
+0 1 0 0 0
+0 0 1 0 0
+0 0 0 1 0
+0 0 0 0 1
diff --git a/libres/lib/analysis/modules/tests/test-data/poly_normal/S b/libres/lib/analysis/modules/tests/test-data/poly_normal/S
new file mode 100644
index 00000000000..3b65818ffeb
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly_normal/S
@@ -0,0 +1,5 @@
+32.5878 63.2456 -38.782 -1.57508 -44.3251 8.58022 -1.74438 -15.6315 3.26232 1.08332 0.952822 -9.36888 -36.8179 -2.88298 -25.4604 -16.4288 -2.63688 23.8621 48.3209 13.7586
+1.44084 6.28978 -1.31845 -0.951198 -4.62901 2.60358 -5.08213 1.48314 0.348176 -3.17545 0.789115 -3.78482 -9.38897 1.68643 -2.64739 -0.734369 0.564822 8.51253 6.77409 1.21929
+-0.535206 1.75502 0.964197 -0.724289 -1.55082 0.908802 -4.34533 1.61443 0.776212 -1.3188 0.321735 -2.62072 -5.81032 1.76672 -0.315473 -0.19173 -0.184142 5.90552 3.59558 -0.0113836
+-0.887804 0.786261 1.3552 -0.651352 -0.904598 0.378514 -4.04956 1.47253 0.958754 -0.619113 0.159181 -2.27272 -4.85331 1.74474 0.25385 -0.173378 -0.482698 5.15744 2.93441 -0.306352
+-0.998081 0.422174 1.47124 -0.616117 -0.665365 0.125411 -3.89414 1.36484 1.05649 -0.259226 0.0780328 -2.11018 -4.43207 1.72397 0.490578 -0.197686 -0.639217 4.81516 2.69164 -0.427464
diff --git a/libres/lib/analysis/modules/tests/test-data/poly_normal/dObs b/libres/lib/analysis/modules/tests/test-data/poly_normal/dObs
new file mode 100644
index 00000000000..d83ae67e1d6
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly_normal/dObs
@@ -0,0 +1,5 @@
+1.6 1
+1.2 1
+0.4 1
+-0.9 1
+-2.5 1
diff --git a/libres/lib/analysis/modules/tests/test-data/poly_normal/prior b/libres/lib/analysis/modules/tests/test-data/poly_normal/prior
new file mode 100644
index 00000000000..848ccbbf0a5
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly_normal/prior
@@ -0,0 +1,3 @@
+-0.210729 0.236275 1.05953 0.0570087 0.163859 0.0296811 -1.38678 0.763898 1.00131 0.737432 0.235209 -0.523182 -1.39647 1.1269 0.811818 0.112282 -0.249699 2.28344 1.48335 -0.0226876
+-0.182324 0.504697 0.598307 -0.0575366 -0.0496834 1.41715 -0.877292 1.27728 -0.529043 -2.09422 0.595043 -0.64615 -1.48269 0.388661 -0.550989 0.749125 1.13607 1.64922 0.270217 0.449244
+1.52548 2.75179 -1.32931 0.158962 -1.55104 0.565174 0.152194 -0.40329 0.352459 0.2653 0.260079 -0.152787 -1.25075 0.106647 -0.796447 -0.435185 0.116493 1.17645 2.1548 0.772313
diff --git a/libres/lib/analysis/modules/tests/test-data/poly_normal/size b/libres/lib/analysis/modules/tests/test-data/poly_normal/size
new file mode 100644
index 00000000000..63d76429dbf
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/test-data/poly_normal/size
@@ -0,0 +1 @@
+20 5
diff --git a/libres/lib/analysis/modules/tests/tests.txt b/libres/lib/analysis/modules/tests/tests.txt
new file mode 100644
index 00000000000..b365039f866
--- /dev/null
+++ b/libres/lib/analysis/modules/tests/tests.txt
@@ -0,0 +1,47 @@
+TEST 1 (Consistency between exact scheme and subspace scheme with no truncation and exact diagonal R):
+ ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 1.0
+ ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 0.6
+ ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 1
+ ANALYSIS_SELECT IES_ENKF
+should give same result as
+ ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 0.6
+ ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 0
+ ANALYSIS_SELECT IES_ENKF
+
+
+TEST 2 (consistency between subspace inversion schemes with lowrank R):
+ ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 0.999
+ ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 0.6
+ ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 2
+ ANALYSIS_SELECT IES_ENKF
+should give same result as
+ ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 0.999
+ ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 0.6
+ ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 3
+ ANALYSIS_SELECT IES_ENKF
+
+
+TEST 3 (consistency between IES and STD_ENKF):
+ ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 0.999
+ ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 1.0
+ ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 1
+ ANALYSIS_SET_VAR IES_ENKF IES_AAPROJECTION false
+ ANALYSIS_SELECT IES_ENKF
+should give same result as
+ ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 0.999
+ ANALYSIS_SELECT STD_ENKF
+
+
+TEST 4 (testing iterations in IES):
+one iteration with
+ ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 0.999
+ ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 1.0
+ ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 1
+ ANALYSIS_SELECT IES_ENKF
+should give approximately the same result as say 10-20 iterations with
+ ANALYSIS_SET_VAR IES_ENKF ENKF_TRUNCATION 0.999
+ ANALYSIS_SET_VAR IES_ENKF IES_STEPLENGTH 0.6
+ ANALYSIS_SET_VAR IES_ENKF IES_INVERSION 1
+ ANALYSIS_SELECT IES_ENKF
+for a linear problem such as poly_example but with prior distributions NORMAL.
+In this linear case the standard ES gives the same result as an itertive scheme,
diff --git a/libres/lib/analysis/null_enkf.cpp b/libres/lib/analysis/null_enkf.cpp
new file mode 100644
index 00000000000..2c9278a45c4
--- /dev/null
+++ b/libres/lib/analysis/null_enkf.cpp
@@ -0,0 +1,84 @@
+/*
+ Copyright (C) 2011 Equinor ASA, Norway.
+
+ The file 'null_enkf.c' is part of ERT - Ensemble based Reservoir Tool.
+
+ ERT 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.
+
+ ERT 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 at
+ for more details.
+*/
+
+#include
+
+#include
+
+#include